From e3b426b443907657948c3dda51349582128fbad3 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Mon, 11 Dec 2023 02:14:18 +0300 Subject: [PATCH 01/22] provider and box implementations --- .../lib/exceptions/question_exceptions.dart | 4 + .../lib/main.dart | 33 +------ .../lib/providers/auth.dart | 3 +- .../lib/providers/question_provider.dart | 92 +++++++++++++++++++ .../widgets/ask_question_form.dart | 74 +++++++++++++++ .../widgets/node_details.dart | 5 +- .../widgets/question_box.dart | 43 +++++++++ .../widgets/questions_list_view.dart | 83 +++++++---------- .../screens/profile_page/profile_page.dart | 76 +++++++++++++-- .../widgets/change_password_form.dart | 25 ++--- .../widgets/profile_activity_tabbar.dart | 2 +- .../widgets/question_activity.dart | 54 ++--------- 12 files changed, 346 insertions(+), 148 deletions(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart create mode 100644 project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart create mode 100644 project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart create mode 100644 project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart b/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart new file mode 100644 index 00000000..972d7dde --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart @@ -0,0 +1,4 @@ +class PostQuestionError implements Exception { + String message; + PostQuestionError({this.message = "Post Question Error"}); +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/main.dart b/project/FrontEnd/collaborative_science_platform/lib/main.dart index 98a9f70e..52796773 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/main.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/main.dart @@ -1,6 +1,7 @@ import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/profile_data_provider.dart'; import 'package:collaborative_science_platform/providers/node_provider.dart'; +import 'package:collaborative_science_platform/providers/question_provider.dart'; import 'package:collaborative_science_platform/providers/user_provider.dart'; import 'package:collaborative_science_platform/providers/workspace_provider.dart'; import 'package:collaborative_science_platform/services/screen_navigation.dart'; @@ -19,7 +20,7 @@ void main() { void configureApp() { if (kIsWeb) { - setUrlStrategy(PathUrlStrategy()); + setUrlStrategy(const PathUrlStrategy()); } } @@ -35,35 +36,9 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (context) => NodeProvider()), ChangeNotifierProvider(create: (context) => UserProvider()), ChangeNotifierProvider(create: (context) => WorkspaceProvider()), + ChangeNotifierProvider( + create: (context) => QuestionAnswerProvider()), ], - // child: MaterialApp( - // debugShowCheckedModeBanner: false, - // title: Constants.appName, - // routes: { - // '/': (context) => const HomePage(), - // LoginPage.routeName: (context) => const LoginPage(), - // SignUpPage.routeName: (context) => const SignUpPage(), - // WorkspacesPage.routeName: (context) => const WorkspacesPage(), -// - // ///ProfilePage.routeName: (context) => const ProfilePage(), - // GraphPage.routeName: (context) => const GraphPage(), - // NotificationPage.routeName: (context) => const NotificationPage(), - // AccountSettingsPage.routeName: (context) => const AccountSettingsPage(), - // PleaseLoginPage2.routeName: (context) => const PleaseLoginPage2(), - // NodeDetailsPage.routeName: (context) { - // final int nodeId = ModalRoute.of(context)!.settings.arguments as int; - // return NodeDetailsPage(nodeID: nodeId); - // }, - // ProfilePage.routeName: (context) { - // final String email = ModalRoute.of(context)!.settings.arguments as String ?? ""; - // return ProfilePage(email: email); - // }, - // }, - // navigatorKey: ScreenNavigation.navigatorKey, - // theme: ThemeData( - // colorScheme: ColorScheme.fromSeed(seedColor: AppColors.primaryColor), - // useMaterial3: true, - // ), child: Portal( child: MaterialApp.router( routerConfig: router, diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart index 75c77ca0..ffa3b48c 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart @@ -10,7 +10,8 @@ import 'package:http/http.dart' as http; class Auth with ChangeNotifier { User? user; BasicUser? basicUser; - //User? user = User(email: "utkangezer@gmail.com", firstName: "utkan", lastName: "gezer"); + //User? user = + // User(email: "utkangezer@gmail.com", firstName: "utkan", lastName: "gezer", token: "token"); bool get isSignedIn { return user != null && user!.token.isNotEmpty; diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart new file mode 100644 index 00000000..8a364f12 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; +import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; +import 'package:collaborative_science_platform/models/question.dart'; +import 'package:collaborative_science_platform/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +class QuestionAnswerProvider with ChangeNotifier { + final List _questions = []; + + List get questions { + return [..._questions]; + } + + Future postQuestion(String questionText, int nodeId) async { + Uri url = Uri.parse("${Constants.apiUrl}/ask_question/"); + final Map headers = { + "Accept": "application/json", + "content-type": "application/json", + }; + final Map postData = { + "question_text": questionText, + "node_id": nodeId, + }; + + try { + final response = await http.post( + url, + headers: headers, + body: json.encode(postData), + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + _questions.add(Question( + questionID: data['question_id'], + askedBy: data['asked_by'], + questionContent: data['question_content'], + answer: data['answer_content'], + publishDate: data['created_at'], + respondedBy: data['responded_by'], + )); + notifyListeners(); + } else if (response.statusCode == 400) { + throw PostQuestionError(); + } else { + throw Exception("Error posting question"); + } + } catch (error) { + rethrow; + } + } + + Future postAnswer(String answerText, int questionId) async { + Uri url = Uri.parse("${Constants.apiUrl}/answer_question/"); + final Map headers = { + "Accept": "application/json", + "content-type": "application/json", + }; + final Map postData = { + "answer_text": answerText, + "question_id": questionId, + }; + + try { + final response = await http.post( + url, + headers: headers, + body: json.encode(postData), + ); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + _questions.add(Question( + questionID: data['question_id'], + askedBy: data['asked_by'], + questionContent: data['question_content'], + answer: data['answer_content'], + publishDate: data['created_at'], + respondedBy: data['responded_by'], + )); + notifyListeners(); + } else if (response.statusCode == 400) { + throw PostQuestionError(); + } else { + throw Exception("Error posting answer"); + } + } catch (error) { + rethrow; + } + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart new file mode 100644 index 00000000..d3a894ec --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart @@ -0,0 +1,74 @@ +import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; +import 'package:collaborative_science_platform/providers/question_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AskQuestionForm extends StatefulWidget { + final int nodeId; + + const AskQuestionForm({Key? key, required this.nodeId}) : super(key: key); + + @override + State createState() => _AskQuestionFormState(); +} + +class _AskQuestionFormState extends State { + bool error = false; + String errorMessage = ""; + bool isLoading = false; + + TextEditingController questionController = TextEditingController(); + + void askQuestion() async { + try { + if (questionController.text.isNotEmpty) { + final questionProvider = Provider.of(context); + setState(() { + isLoading = true; + }); + await questionProvider.postQuestion(questionController.text, widget.nodeId); + questionController.clear(); + } + } on PostQuestionError { + setState(() { + error = true; + errorMessage = PostQuestionError().message; + }); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + } finally { + setState(() { + isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Ask a Question', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8.0), + TextField( + controller: questionController, + decoration: const InputDecoration(labelText: 'Your Question'), + ), + const SizedBox(height: 16.0), + ElevatedButton( + onPressed: () { + askQuestion(); + }, + child: const Text('Submit Question'), + ), + const SizedBox(height: 16.0), + ], + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index e1a9a948..4e45e644 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -187,7 +187,10 @@ class _NodeDetailsState extends State { //Q/A Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: QuestionsView(questions: widget.node.questions), + child: QuestionsView( + questions: widget.node.questions, + nodeId: widget.node.nodeId, + ), ), if (currentIndex == 5) //contributors diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart new file mode 100644 index 00000000..763c10f4 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class QuestionBox extends StatelessWidget { + final String question; + final String askedBy; + final String answer; + + const QuestionBox({super.key, required this.question, required this.askedBy, this.answer = ''}); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.all(16.0), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Q: $question', + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8.0), + Text('Asked by: $askedBy'), + Visibility( + visible: answer.isNotEmpty, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8.0), + Text( + 'A: $answer', + style: const TextStyle(fontSize: 16), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart index c57d1069..b8eecb7b 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart @@ -1,61 +1,42 @@ -import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; -import 'package:collaborative_science_platform/utils/text_styles.dart'; -import 'package:collaborative_science_platform/widgets/card_container.dart'; +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/screens/node_details_page/widgets/question_box.dart'; import 'package:flutter/material.dart'; - -import '../../../models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/screens/node_details_page/widgets/ask_question_form.dart'; +import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; class QuestionsView extends StatelessWidget { final List questions; - const QuestionsView({super.key, required this.questions}); + final int nodeId; + + const QuestionsView({Key? key, required this.questions, required this.nodeId}) : super(key: key); @override Widget build(BuildContext context) { - return Container( - width: Responsive.desktopPageWidth, - decoration: BoxDecoration(color: Colors.grey[200]), - child: ListView.builder( - scrollDirection: Axis.vertical, - shrinkWrap: true, - padding: const EdgeInsets.all(8), - itemCount: questions.length, - itemBuilder: (BuildContext context, int index) { - if (Responsive.isDesktop(context)) { - return Padding( - padding: const EdgeInsets.all(5), - child: CardContainer( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SelectableText( - "Q: ${questions[index].content}", - style: TextStyles.title4black, - textAlign: TextAlign.start, - ), - SelectableText( - "asked by ${questions[index].asker} at ${questions[index].createdAt}", - style: TextStyles.bodyGrey, - textAlign: TextAlign.end, - ), - SelectableText( - "A: ${questions[index].answer}", - style: TextStyles.bodyBlack, - textAlign: TextAlign.start, - ), - SelectableText( - "answered by ${questions[index].answerer} at ${questions[index].answeredAt}", - style: TextStyles.bodyGrey, - textAlign: TextAlign.end, - ), - ], - ), - ), - ); - } else { - return const SizedBox(); - } - }), + return SingleChildScrollView( + child: Container( + width: Responsive.desktopPageWidth, + height: 1000, + decoration: BoxDecoration(color: Colors.grey[200]), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AskQuestionForm(nodeId: nodeId), + Flexible( + child: ListView.builder( + shrinkWrap: true, + itemCount: questions.length, + itemBuilder: (BuildContext context, int index) { + return QuestionBox( + question: "Q: ${questions[index].content}", + askedBy: "asked by ${questions[index].asker} at ${questions[index].createdAt}", + answer: "A: ${questions[index].answer}", + ); + }, + ), + ), + ], + ), + ), ); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index adc8b502..d89cacf4 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -1,4 +1,5 @@ import 'package:collaborative_science_platform/exceptions/profile_page_exceptions.dart'; +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; import 'package:collaborative_science_platform/models/profile_data.dart'; import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; @@ -174,12 +175,31 @@ class _ProfilePageState extends State { ), ), if (currentIndex == 1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: CardContainer( child: SizedBox( height: 400, - child: QuestionActivity(), + child: QuestionActivity(questions: + // temp question list + [ + Question( + content: "How does Flutter work?", + asker: User( + firstName: "John Doe", + email: "as", + lastName: "1", + ), + createdAt: DateTime.now().toString(), + answer: + "Flutter works by using the Dart programming language...", + answerer: User( + firstName: "John Doe", + email: "as", + lastName: "1", + ), + answeredAt: DateTime.now().toString()), + ]), ), ), ), @@ -242,12 +262,31 @@ class _ProfilePageState extends State { ), ), if (currentIndex == 1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: CardContainer( child: SizedBox( height: 400, - child: QuestionActivity(), + child: QuestionActivity(questions: + // temp question list + [ + Question( + content: "How does Flutter work?", + asker: User( + firstName: "John Doe", + email: "as", + lastName: "1", + ), + createdAt: DateTime.now().toString(), + answer: + "Flutter works by using the Dart programming language...", + answerer: User( + firstName: "John Doe", + email: "as", + lastName: "1", + ), + answeredAt: DateTime.now().toString()), + ]), ), ), ), @@ -320,12 +359,31 @@ class _ProfilePageState extends State { ), ), if (currentIndex == 1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: CardContainer( child: SizedBox( height: 400, - child: QuestionActivity(), + child: QuestionActivity(questions: + // temp question list + [ + Question( + content: "How does Flutter work?", + asker: User( + firstName: "John Doe", + email: "as", + lastName: "1", + ), + createdAt: DateTime.now().toString(), + answer: + "Flutter works by using the Dart programming language...", + answerer: User( + firstName: "John Doe", + email: "as", + lastName: "1", + ), + answeredAt: DateTime.now().toString()), + ]), ), ), ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/change_password_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/change_password_form.dart index c7eccec6..5e2d3cdd 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/change_password_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/change_password_form.dart @@ -1,6 +1,5 @@ import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/settings_provider.dart'; -import 'package:collaborative_science_platform/utils/colors.dart'; import 'package:collaborative_science_platform/widgets/app_button.dart'; import 'package:flutter/material.dart'; import 'package:collaborative_science_platform/models/profile_data.dart'; @@ -28,7 +27,7 @@ class _ChangePasswordFormState extends State { final newPassFocusNode = FocusNode(); final double x = 300; -bool buttonState = false; + bool buttonState = false; String errorMessage = ""; bool error = false; @@ -67,8 +66,7 @@ bool buttonState = false; } void controllerCheck() { - if (newPassController.text.isNotEmpty && - oldPassController.text.isNotEmpty) { + if (newPassController.text.isNotEmpty && oldPassController.text.isNotEmpty) { setState(() { buttonState = true; }); @@ -111,16 +109,19 @@ bool buttonState = false; ], ), const SizedBox(height: 20.0), - AppButton( - onTap: () => oldPassController.text.isNotEmpty && newPassController.text.isNotEmpty ? changePass() : {}, - text: "Save", - height: 50, - isActive: buttonState, - // isLoading: isLoading, - ), + onTap: () => oldPassController.text.isNotEmpty && newPassController.text.isNotEmpty + ? changePass() + : {}, + text: "Save", + height: 50, + isActive: buttonState, + ), const SizedBox(height: 10.0), - Text(errorMessage, style: const TextStyle(fontSize: 16.0),), + Text( + errorMessage, + style: const TextStyle(fontSize: 16.0), + ), ], ), ); diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/profile_activity_tabbar.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/profile_activity_tabbar.dart index fee7c83d..7033b6de 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/profile_activity_tabbar.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/profile_activity_tabbar.dart @@ -29,7 +29,7 @@ class _ProfileActivityTabBar extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Center( + const Center( child: Text( "Activities", style: TextStyles.bodyBlack, diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart index 84e537e0..51e22a9f 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart @@ -1,8 +1,11 @@ +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/screens/node_details_page/widgets/question_box.dart'; import 'package:flutter/material.dart'; -// TODO: currently no API to get questions class QuestionActivity extends StatelessWidget { - const QuestionActivity({super.key}); + final List questions; + + const QuestionActivity({Key? key, required this.questions}) : super(key: key); @override Widget build(BuildContext context) { @@ -10,49 +13,12 @@ class QuestionActivity extends StatelessWidget { padding: const EdgeInsets.all(0), scrollDirection: Axis.vertical, shrinkWrap: true, - itemCount: 10, + itemCount: questions.length, itemBuilder: (BuildContext context, int index) { - return Card( - elevation: 4.0, - margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - child: InkWell( - // onTap: Navigate to the screen of the question/answer - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SelectableText( - "question/answer $index", - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18.0, - ), - ), - const SizedBox(height: 8.0), - const SizedBox(height: 8.0), - const Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SelectableText( - 'some date', - style: TextStyle( - color: Colors.grey, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ], - ), - ), - ), + return QuestionBox( + question: "Q: ${questions[index].content}", + askedBy: "asked by ${questions[index].asker} at ${questions[index].createdAt}", + answer: "A: ${questions[index].answer}", ); }, ); From e05bf4e4f971809264884c05645fb1b5e3b84d68 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Mon, 11 Dec 2023 23:01:29 +0300 Subject: [PATCH 02/22] add route for q/a and reply button --- .../lib/exceptions/question_exceptions.dart | 5 ++ .../lib/main.dart | 4 +- .../models/node_details_page/question.dart | 37 ++++++++-- .../lib/models/profile_data.dart | 24 ++++--- .../lib/providers/auth.dart | 6 +- .../node_details_page/widgets/answer_box.dart | 68 +++++++++++++++++++ .../widgets/question_box.dart | 50 ++++++++++---- .../widgets/questions_list_view.dart | 4 +- .../screens/profile_page/profile_page.dart | 9 ++- .../widgets/question_activity.dart | 14 ++-- 10 files changed, 181 insertions(+), 40 deletions(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart b/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart index 972d7dde..df4083ce 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/exceptions/question_exceptions.dart @@ -2,3 +2,8 @@ class PostQuestionError implements Exception { String message; PostQuestionError({this.message = "Post Question Error"}); } + +class PostAnswerError implements Exception { + String message; + PostAnswerError({this.message = "Post Answer Error"}); +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/main.dart b/project/FrontEnd/collaborative_science_platform/lib/main.dart index 52796773..3ae05133 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/main.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/main.dart @@ -20,7 +20,7 @@ void main() { void configureApp() { if (kIsWeb) { - setUrlStrategy(const PathUrlStrategy()); + setUrlStrategy(PathUrlStrategy()); } } @@ -45,7 +45,7 @@ class MyApp extends StatelessWidget { debugShowCheckedModeBanner: false, title: Constants.appName, theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Color.fromARGB(255, 85, 234, 145)), + colorScheme: ColorScheme.fromSeed(seedColor: const Color.fromARGB(255, 85, 234, 145)), useMaterial3: true, ), ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart index 51627119..c9ea948f 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart @@ -4,16 +4,18 @@ class Question { String content; String createdAt; User? asker; - String answer; + String? answer; User? answerer; - String answeredAt; + String? answeredAt; + int? nodeId; Question({ required this.content, required this.createdAt, + required this.asker, required this.answer, - required this.answeredAt, required this.answerer, - required this.asker, + required this.answeredAt, + required this.nodeId, }); factory Question.fromJson(Map jsonString) { return Question( @@ -26,6 +28,33 @@ class Question { : User.fromJsonforNodeDetailPage(jsonString['answerer']), asker: jsonString['asker'] == null ? null : User.fromJsonforNodeDetailPage(jsonString['asker']), + nodeId: jsonString['node_id'] ?? -1, + ); + } + + factory Question.fromJsonforProfilePage(Map jsonString) { + return Question( + content: jsonString['question_content'] ?? "", + createdAt: jsonString['ask_date'] ?? "", + asker: jsonString.containsKey("asker_id") + ? User( + id: jsonString['asker_id'], + email: jsonString['asker_mail'], + firstName: jsonString['asker_name'], + lastName: jsonString['asker_surname'], + ) + : null, + answer: jsonString.containsKey("answer_content") ? jsonString["answer_content"] : "", + answerer: jsonString.containsKey("answerer") + ? User( + id: jsonString['answerer_id'], + email: jsonString['answerer_mail'], + firstName: jsonString['answerer_name'], + lastName: jsonString['answerer_surname'], + ) + : null, + answeredAt: jsonString.containsKey("answer_date") ? jsonString["answer_date"] as String : "", + nodeId: jsonString['node_id'] ?? -1, ); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/profile_data.dart b/project/FrontEnd/collaborative_science_platform/lib/models/profile_data.dart index be633822..9a8b3199 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/profile_data.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/profile_data.dart @@ -1,3 +1,4 @@ +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; import 'package:collaborative_science_platform/models/user.dart'; class Node { @@ -28,24 +29,33 @@ class ProfileData { String email; String aboutMe; List nodes; - List askedQuestionIDs; - List answeredQuestionIDs; + List askedQuestions; + List answeredQuestions; ProfileData( {this.aboutMe = "", this.email = "", this.name = "", this.surname = "", this.nodes = const [], - this.askedQuestionIDs = const [], - this.answeredQuestionIDs = const []}); + this.askedQuestions = const [], + this.answeredQuestions = const []}); + factory ProfileData.fromJson(Map jsonString) { - var list = jsonString['nodes'] as List; - List nodes = list.map((e) => Node.fromJson(e)).toList(); + var nodeList = jsonString['nodes'] as List; + var askedList = jsonString['asked_questions'] as List; + var answeredList = jsonString['answered_questions'] as List; + + List nodes = nodeList.map((e) => Node.fromJson(e)).toList(); + List asked = askedList.map((e) => Question.fromJsonforProfilePage(e)).toList(); + List answered = answeredList.map((e) => Question.fromJsonforProfilePage(e)).toList(); + return ProfileData( nodes: nodes, name: jsonString['name'], surname: jsonString['surname'], aboutMe: jsonString['bio'], + askedQuestions: asked, + answeredQuestions: answered, ); } @@ -70,8 +80,6 @@ class ProfileData { ], ), ], - askedQuestionIDs: [1, 2, 3, 4, 5], - answeredQuestionIDs: [1, 2, 3, 4, 5], ); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart index ffa3b48c..4eb5e82b 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart @@ -8,10 +8,10 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class Auth with ChangeNotifier { - User? user; + //User? user; BasicUser? basicUser; - //User? user = - // User(email: "utkangezer@gmail.com", firstName: "utkan", lastName: "gezer", token: "token"); + User? user = + User(email: "utkangezer@gmail.com", firstName: "utkan", lastName: "gezer", token: "token"); bool get isSignedIn { return user != null && user!.token.isNotEmpty; diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart new file mode 100644 index 00000000..db9e3a3a --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart @@ -0,0 +1,68 @@ +import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; +import 'package:collaborative_science_platform/providers/question_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AnswerBox extends StatefulWidget { + final int nodeId; + + const AnswerBox({Key? key, required this.nodeId}) : super(key: key); + + @override + State createState() => _AnswerBoxState(); +} + +class _AnswerBoxState extends State { + TextEditingController answerController = TextEditingController(); + bool isLoading = false; + bool error = false; + String errorMessage = ''; + + void answerQuestion() async { + try { + if (answerController.text.isNotEmpty) { + final questionAnswerProvider = Provider.of(context, listen: false); + setState(() { + isLoading = true; + }); + await questionAnswerProvider.postAnswer(answerController.text, widget.nodeId); + answerController.clear(); + } + } on PostAnswerError { + setState(() { + error = true; + errorMessage = PostAnswerError().message; + }); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + } finally { + setState(() { + isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + // Your UI code here + // Example: A text field and a button for answering + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextField( + controller: answerController, + decoration: const InputDecoration(labelText: 'Your Answer'), + ), + const SizedBox(height: 16.0), + ElevatedButton( + onPressed: isLoading ? null : answerQuestion, + child: const Text('Submit Answer'), + ), + if (error) Text(errorMessage, style: const TextStyle(color: Colors.red)), + ], + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart index 763c10f4..0f9c0f69 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart @@ -1,11 +1,19 @@ +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/screens/node_details_page/widgets/answer_box.dart'; import 'package:flutter/material.dart'; -class QuestionBox extends StatelessWidget { - final String question; - final String askedBy; - final String answer; +class QuestionBox extends StatefulWidget { + final Question question; - const QuestionBox({super.key, required this.question, required this.askedBy, this.answer = ''}); + const QuestionBox({super.key, required this.question}); + + @override + State createState() => _QuestionBoxState(); +} + +class _QuestionBoxState extends State { + bool isReplyVisible = false; + TextEditingController answerController = TextEditingController(); @override Widget build(BuildContext context) { @@ -17,24 +25,42 @@ class QuestionBox extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Q: $question', + 'Q: ${widget.question}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8.0), - Text('Asked by: $askedBy'), - Visibility( - visible: answer.isNotEmpty, - child: Column( + Text('Asked by: ${widget.question.asker} at ${widget.question.createdAt}'), + if (widget.question.answer != null) + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 8.0), Text( - 'A: $answer', + 'A: ${widget.question.answer}', style: const TextStyle(fontSize: 16), ), ], ), - ), + if (widget.question.answer == null) + Column( + children: [ + const SizedBox(height: 8.0), + Align( + alignment: Alignment.centerLeft, + child: ElevatedButton( + onPressed: () { + setState(() { + isReplyVisible = !isReplyVisible; + }); + }, + child: Text( + isReplyVisible ? 'Hide Reply' : 'Reply', + ), + ), + ), + if (isReplyVisible) AnswerBox(nodeId: widget.question.nodeId!), + ], + ), ], ), ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart index b8eecb7b..dd08ad98 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart @@ -27,9 +27,7 @@ class QuestionsView extends StatelessWidget { itemCount: questions.length, itemBuilder: (BuildContext context, int index) { return QuestionBox( - question: "Q: ${questions[index].content}", - askedBy: "asked by ${questions[index].asker} at ${questions[index].createdAt}", - answer: "A: ${questions[index].answer}", + question: questions[index], ); }, ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index d89cacf4..51f9e81f 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -198,7 +198,8 @@ class _ProfilePageState extends State { email: "as", lastName: "1", ), - answeredAt: DateTime.now().toString()), + answeredAt: DateTime.now().toString(), + nodeId: 100), ]), ), ), @@ -285,7 +286,8 @@ class _ProfilePageState extends State { email: "as", lastName: "1", ), - answeredAt: DateTime.now().toString()), + answeredAt: DateTime.now().toString(), + nodeId: 100), ]), ), ), @@ -382,7 +384,8 @@ class _ProfilePageState extends State { email: "as", lastName: "1", ), - answeredAt: DateTime.now().toString()), + answeredAt: DateTime.now().toString(), + nodeId: 100), ]), ), ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart index 51e22a9f..6d792c0c 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart @@ -1,10 +1,11 @@ import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/screens/node_details_page/node_details_page.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/question_box.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; class QuestionActivity extends StatelessWidget { final List questions; - const QuestionActivity({Key? key, required this.questions}) : super(key: key); @override @@ -15,10 +16,13 @@ class QuestionActivity extends StatelessWidget { shrinkWrap: true, itemCount: questions.length, itemBuilder: (BuildContext context, int index) { - return QuestionBox( - question: "Q: ${questions[index].content}", - askedBy: "asked by ${questions[index].asker} at ${questions[index].createdAt}", - answer: "A: ${questions[index].answer}", + return ElevatedButton( + child: QuestionBox( + question: questions[index], + ), + onPressed: () { + context.push("${NodeDetailsPage.routeName}/${questions[index].nodeId}"); + }, ); }, ); From d1c681a28e1155e1a46e5a36ec45b85d57b38a99 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Mon, 11 Dec 2023 23:02:26 +0300 Subject: [PATCH 03/22] remove comment --- .../collaborative_science_platform/lib/providers/auth.dart | 6 +++--- .../lib/screens/node_details_page/widgets/answer_box.dart | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart index 4eb5e82b..ffa3b48c 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/auth.dart @@ -8,10 +8,10 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class Auth with ChangeNotifier { - //User? user; + User? user; BasicUser? basicUser; - User? user = - User(email: "utkangezer@gmail.com", firstName: "utkan", lastName: "gezer", token: "token"); + //User? user = + // User(email: "utkangezer@gmail.com", firstName: "utkan", lastName: "gezer", token: "token"); bool get isSignedIn { return user != null && user!.token.isNotEmpty; diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart index db9e3a3a..321ebfde 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart @@ -47,8 +47,6 @@ class _AnswerBoxState extends State { @override Widget build(BuildContext context) { - // Your UI code here - // Example: A text field and a button for answering return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ From e9be9885306d1c0944d13bf9411847fb268971d3 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Tue, 12 Dec 2023 00:02:53 +0300 Subject: [PATCH 04/22] make questionlist stateful and fix provider bugs --- .../models/node_details_page/question.dart | 4 ++ .../lib/models/question.dart | 20 ------- .../lib/providers/question_provider.dart | 58 +++++++++---------- .../node_details_page/widgets/answer_box.dart | 5 +- .../widgets/ask_question_form.dart | 15 +++-- .../widgets/question_box.dart | 2 +- .../widgets/questions_list_view.dart | 31 ++++++++-- .../screens/profile_page/profile_page.dart | 3 + 8 files changed, 77 insertions(+), 61 deletions(-) delete mode 100644 project/FrontEnd/collaborative_science_platform/lib/models/question.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart index c9ea948f..4a90a221 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart @@ -1,6 +1,7 @@ import 'package:collaborative_science_platform/models/user.dart'; class Question { + int id; String content; String createdAt; User? asker; @@ -9,6 +10,7 @@ class Question { String? answeredAt; int? nodeId; Question({ + required this.id, required this.content, required this.createdAt, required this.asker, @@ -19,6 +21,7 @@ class Question { }); factory Question.fromJson(Map jsonString) { return Question( + id: jsonString['id'] ?? -1, content: jsonString['question_content'] ?? "", createdAt: jsonString['created_at'] ?? "", answer: jsonString['answer_content'] ?? "", @@ -34,6 +37,7 @@ class Question { factory Question.fromJsonforProfilePage(Map jsonString) { return Question( + id: jsonString['id'] ?? -1, content: jsonString['question_content'] ?? "", createdAt: jsonString['ask_date'] ?? "", asker: jsonString.containsKey("asker_id") diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/question.dart b/project/FrontEnd/collaborative_science_platform/lib/models/question.dart deleted file mode 100644 index c75ffe16..00000000 --- a/project/FrontEnd/collaborative_science_platform/lib/models/question.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:collaborative_science_platform/models/basic_user.dart'; -import 'package:collaborative_science_platform/models/contributor_user.dart'; - -class Question { - int questionID; - BasicUser askedBy; - String questionContent; - String answer; - DateTime publishDate; - Contributor respondedBy; - - Question({ - required this.questionID, - required this.askedBy, - required this.questionContent, - required this.answer, - required this.publishDate, - required this.respondedBy, - }); -} diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart index 8a364f12..e6d887b9 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; -import 'package:collaborative_science_platform/models/question.dart'; +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/utils/constants.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; @@ -12,36 +13,36 @@ class QuestionAnswerProvider with ChangeNotifier { return [..._questions]; } - Future postQuestion(String questionText, int nodeId) async { + Future postQuestion(String questionText, int nodeId, User user) async { Uri url = Uri.parse("${Constants.apiUrl}/ask_question/"); final Map headers = { "Accept": "application/json", "content-type": "application/json", + "Authorization": "Token ${user.token}", }; - final Map postData = { - "question_text": questionText, - "node_id": nodeId, - }; + final String body = json.encode({ + 'question_content': questionText, + 'node_id': nodeId, + }); try { final response = await http.post( url, headers: headers, - body: json.encode(postData), + body: body, ); - - if (response.statusCode == 200) { - final data = json.decode(response.body); + if (response.statusCode == 201) { _questions.add(Question( - questionID: data['question_id'], - askedBy: data['asked_by'], - questionContent: data['question_content'], - answer: data['answer_content'], - publishDate: data['created_at'], - respondedBy: data['responded_by'], - )); + id: json.decode(response.body)['id'], + answer: null, + content: questionText, + createdAt: DateTime.now().toString(), + asker: user, + answerer: null, + answeredAt: null, + nodeId: nodeId)); notifyListeners(); - } else if (response.statusCode == 400) { + } else if (response.statusCode == 401) { throw PostQuestionError(); } else { throw Exception("Error posting question"); @@ -51,14 +52,15 @@ class QuestionAnswerProvider with ChangeNotifier { } } - Future postAnswer(String answerText, int questionId) async { + Future postAnswer(String answerText, int questionId, User user) async { Uri url = Uri.parse("${Constants.apiUrl}/answer_question/"); final Map headers = { "Accept": "application/json", "content-type": "application/json", + "Authorization": "Token ${user.token}", }; final Map postData = { - "answer_text": answerText, + "answer_content": answerText, "question_id": questionId, }; @@ -69,18 +71,14 @@ class QuestionAnswerProvider with ChangeNotifier { body: json.encode(postData), ); - if (response.statusCode == 200) { + if (response.statusCode == 201) { final data = json.decode(response.body); - _questions.add(Question( - questionID: data['question_id'], - askedBy: data['asked_by'], - questionContent: data['question_content'], - answer: data['answer_content'], - publishDate: data['created_at'], - respondedBy: data['responded_by'], - )); + Question answeredQuestions = _questions.firstWhere((element) => element.id == questionId); + answeredQuestions.answer = data['answer_content']; + answeredQuestions.answeredAt = data['answered_at']; + answeredQuestions.answerer = user; notifyListeners(); - } else if (response.statusCode == 400) { + } else if (response.statusCode == 401) { throw PostQuestionError(); } else { throw Exception("Error posting answer"); diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart index 321ebfde..6113733e 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart @@ -1,4 +1,6 @@ import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; +import 'package:collaborative_science_platform/models/user.dart'; +import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/question_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -22,10 +24,11 @@ class _AnswerBoxState extends State { try { if (answerController.text.isNotEmpty) { final questionAnswerProvider = Provider.of(context, listen: false); + User user = Provider.of(context, listen: false).user!; setState(() { isLoading = true; }); - await questionAnswerProvider.postAnswer(answerController.text, widget.nodeId); + await questionAnswerProvider.postAnswer(answerController.text, widget.nodeId, user); answerController.clear(); } } on PostAnswerError { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart index d3a894ec..8053e346 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart @@ -1,12 +1,16 @@ import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/models/user.dart'; +import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/question_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class AskQuestionForm extends StatefulWidget { final int nodeId; - - const AskQuestionForm({Key? key, required this.nodeId}) : super(key: key); + final Function(List) onQuestionPosted; + const AskQuestionForm({Key? key, required this.nodeId, required this.onQuestionPosted}) + : super(key: key); @override State createState() => _AskQuestionFormState(); @@ -22,11 +26,13 @@ class _AskQuestionFormState extends State { void askQuestion() async { try { if (questionController.text.isNotEmpty) { - final questionProvider = Provider.of(context); + final questionProvider = Provider.of(context, listen: false); + User user = Provider.of(context, listen: false).user!; setState(() { isLoading = true; }); - await questionProvider.postQuestion(questionController.text, widget.nodeId); + await questionProvider.postQuestion(questionController.text, widget.nodeId, user); + widget.onQuestionPosted(questionProvider.questions); questionController.clear(); } } on PostQuestionError { @@ -67,6 +73,7 @@ class _AskQuestionFormState extends State { }, child: const Text('Submit Question'), ), + if (error) Text(errorMessage, style: const TextStyle(color: Colors.red)), const SizedBox(height: 16.0), ], ); diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart index 0f9c0f69..8acfa2b8 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart @@ -25,7 +25,7 @@ class _QuestionBoxState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Q: ${widget.question}', + 'Q: ${widget.question.content}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8.0), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart index dd08ad98..f7ae6df3 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart @@ -1,14 +1,28 @@ +import 'package:flutter/material.dart'; import 'package:collaborative_science_platform/models/node_details_page/question.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/question_box.dart'; -import 'package:flutter/material.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/ask_question_form.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; -class QuestionsView extends StatelessWidget { - final List questions; +class QuestionsView extends StatefulWidget { final int nodeId; + final List questions; + const QuestionsView({Key? key, required this.nodeId, required this.questions}) : super(key: key); - const QuestionsView({Key? key, required this.questions, required this.nodeId}) : super(key: key); + @override + State createState() => _QuestionsViewState(); +} + +class _QuestionsViewState extends State { + List questions = []; + + @override + void initState() { + super.initState(); + setState(() { + questions = widget.questions; + }); + } @override Widget build(BuildContext context) { @@ -20,7 +34,14 @@ class QuestionsView extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - AskQuestionForm(nodeId: nodeId), + AskQuestionForm( + nodeId: widget.nodeId, + onQuestionPosted: (List newQuestions) { + setState(() { + questions = newQuestions; + }); + }, + ), Flexible( child: ListView.builder( shrinkWrap: true, diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index 51f9e81f..b31a2633 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -184,6 +184,7 @@ class _ProfilePageState extends State { // temp question list [ Question( + id: 1, content: "How does Flutter work?", asker: User( firstName: "John Doe", @@ -272,6 +273,7 @@ class _ProfilePageState extends State { // temp question list [ Question( + id: 1, content: "How does Flutter work?", asker: User( firstName: "John Doe", @@ -370,6 +372,7 @@ class _ProfilePageState extends State { // temp question list [ Question( + id: 1, content: "How does Flutter work?", asker: User( firstName: "John Doe", From 6d5b1dc7625b5f1196fffaa717e71166269eb9c0 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Tue, 12 Dec 2023 15:48:37 +0300 Subject: [PATCH 05/22] change nodeid to questionid in answerbox --- .../models/node_details_page/question.dart | 19 ++++++++----------- .../lib/providers/question_provider.dart | 6 +++++- .../node_details_page/widgets/answer_box.dart | 6 +++--- .../widgets/ask_question_form.dart | 5 ++--- .../widgets/question_box.dart | 8 ++++---- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart index 4a90a221..64b97711 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart @@ -4,7 +4,7 @@ class Question { int id; String content; String createdAt; - User? asker; + User asker; String? answer; User? answerer; String? answeredAt; @@ -29,8 +29,7 @@ class Question { answerer: jsonString['answerer'] == null ? null : User.fromJsonforNodeDetailPage(jsonString['answerer']), - asker: - jsonString['asker'] == null ? null : User.fromJsonforNodeDetailPage(jsonString['asker']), + asker: User.fromJsonforNodeDetailPage(jsonString['asker']), nodeId: jsonString['node_id'] ?? -1, ); } @@ -40,14 +39,12 @@ class Question { id: jsonString['id'] ?? -1, content: jsonString['question_content'] ?? "", createdAt: jsonString['ask_date'] ?? "", - asker: jsonString.containsKey("asker_id") - ? User( - id: jsonString['asker_id'], - email: jsonString['asker_mail'], - firstName: jsonString['asker_name'], - lastName: jsonString['asker_surname'], - ) - : null, + asker: User( + id: jsonString['asker_id'], + email: jsonString['asker_mail'], + firstName: jsonString['asker_name'], + lastName: jsonString['asker_surname'], + ), answer: jsonString.containsKey("answer_content") ? jsonString["answer_content"] : "", answerer: jsonString.containsKey("answerer") ? User( diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart index e6d887b9..5a0ee8fc 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart @@ -15,6 +15,8 @@ class QuestionAnswerProvider with ChangeNotifier { Future postQuestion(String questionText, int nodeId, User user) async { Uri url = Uri.parse("${Constants.apiUrl}/ask_question/"); + print(url); + final Map headers = { "Accept": "application/json", "content-type": "application/json", @@ -31,9 +33,11 @@ class QuestionAnswerProvider with ChangeNotifier { headers: headers, body: body, ); + print(response.statusCode); if (response.statusCode == 201) { _questions.add(Question( - id: json.decode(response.body)['id'], + //id: json.decode(response.body)['id'], + id: 98, answer: null, content: questionText, createdAt: DateTime.now().toString(), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart index 6113733e..475729fb 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart @@ -6,9 +6,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class AnswerBox extends StatefulWidget { - final int nodeId; + final int questionId; - const AnswerBox({Key? key, required this.nodeId}) : super(key: key); + const AnswerBox({Key? key, required this.questionId}) : super(key: key); @override State createState() => _AnswerBoxState(); @@ -28,7 +28,7 @@ class _AnswerBoxState extends State { setState(() { isLoading = true; }); - await questionAnswerProvider.postAnswer(answerController.text, widget.nodeId, user); + await questionAnswerProvider.postAnswer(answerController.text, widget.questionId, user); answerController.clear(); } } on PostAnswerError { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart index 8053e346..5d44b8e5 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart @@ -31,6 +31,7 @@ class _AskQuestionFormState extends State { setState(() { isLoading = true; }); + print(questionController.text); await questionProvider.postQuestion(questionController.text, widget.nodeId, user); widget.onQuestionPosted(questionProvider.questions); questionController.clear(); @@ -68,9 +69,7 @@ class _AskQuestionFormState extends State { ), const SizedBox(height: 16.0), ElevatedButton( - onPressed: () { - askQuestion(); - }, + onPressed: isLoading ? null : askQuestion, child: const Text('Submit Question'), ), if (error) Text(errorMessage, style: const TextStyle(color: Colors.red)), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart index 8acfa2b8..2493fc13 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart @@ -29,8 +29,8 @@ class _QuestionBoxState extends State { style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8.0), - Text('Asked by: ${widget.question.asker} at ${widget.question.createdAt}'), - if (widget.question.answer != null) + Text('Asked by: ${widget.question.asker.email} at ${widget.question.createdAt}'), + if (widget.question.answer != null && widget.question.answer!.isNotEmpty) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -41,7 +41,7 @@ class _QuestionBoxState extends State { ), ], ), - if (widget.question.answer == null) + if (widget.question.answer == null || widget.question.answer!.isEmpty) Column( children: [ const SizedBox(height: 8.0), @@ -58,7 +58,7 @@ class _QuestionBoxState extends State { ), ), ), - if (isReplyVisible) AnswerBox(nodeId: widget.question.nodeId!), + if (isReplyVisible) AnswerBox(questionId: widget.question.id), ], ), ], From acf8dcab44b0e084d2f3d9228b4c9e6f7cc805ee Mon Sep 17 00:00:00 2001 From: defabdullah Date: Tue, 12 Dec 2023 20:18:59 +0300 Subject: [PATCH 06/22] questions in state fix --- .../lib/providers/question_provider.dart | 8 ++------ .../node_details_page/widgets/ask_question_form.dart | 5 ++--- .../node_details_page/widgets/questions_list_view.dart | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart index 5a0ee8fc..b192bc18 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart @@ -15,7 +15,6 @@ class QuestionAnswerProvider with ChangeNotifier { Future postQuestion(String questionText, int nodeId, User user) async { Uri url = Uri.parse("${Constants.apiUrl}/ask_question/"); - print(url); final Map headers = { "Accept": "application/json", @@ -33,11 +32,10 @@ class QuestionAnswerProvider with ChangeNotifier { headers: headers, body: body, ); - print(response.statusCode); if (response.statusCode == 201) { _questions.add(Question( //id: json.decode(response.body)['id'], - id: 98, + id: 99, answer: null, content: questionText, createdAt: DateTime.now().toString(), @@ -67,14 +65,12 @@ class QuestionAnswerProvider with ChangeNotifier { "answer_content": answerText, "question_id": questionId, }; - try { final response = await http.post( url, headers: headers, body: json.encode(postData), ); - if (response.statusCode == 201) { final data = json.decode(response.body); Question answeredQuestions = _questions.firstWhere((element) => element.id == questionId); @@ -82,7 +78,7 @@ class QuestionAnswerProvider with ChangeNotifier { answeredQuestions.answeredAt = data['answered_at']; answeredQuestions.answerer = user; notifyListeners(); - } else if (response.statusCode == 401) { + } else if (response.statusCode == 403) { throw PostQuestionError(); } else { throw Exception("Error posting answer"); diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart index 5d44b8e5..7098940b 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart @@ -8,7 +8,7 @@ import 'package:provider/provider.dart'; class AskQuestionForm extends StatefulWidget { final int nodeId; - final Function(List) onQuestionPosted; + final Function(Question) onQuestionPosted; const AskQuestionForm({Key? key, required this.nodeId, required this.onQuestionPosted}) : super(key: key); @@ -31,9 +31,8 @@ class _AskQuestionFormState extends State { setState(() { isLoading = true; }); - print(questionController.text); await questionProvider.postQuestion(questionController.text, widget.nodeId, user); - widget.onQuestionPosted(questionProvider.questions); + widget.onQuestionPosted(questionProvider.questions.last); questionController.clear(); } } on PostQuestionError { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart index f7ae6df3..c73141b7 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart @@ -36,9 +36,9 @@ class _QuestionsViewState extends State { children: [ AskQuestionForm( nodeId: widget.nodeId, - onQuestionPosted: (List newQuestions) { + onQuestionPosted: (Question newQuestion) { setState(() { - questions = newQuestions; + questions.add(newQuestion); }); }, ), From 3a309c224dc5799261688c80b0700b3893b7d11d Mon Sep 17 00:00:00 2001 From: brunettow Date: Sat, 16 Dec 2023 18:26:06 +0300 Subject: [PATCH 07/22] fix the base. Changed base to frontend --- .../lib/screens/auth_screens/signup_page.dart | 77 ++++++++++++++++++- .../widgets/privacy_policy_form.dart | 73 ++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/signup_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/signup_page.dart index d019f6d6..9e8e1550 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/signup_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/signup_page.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:go_router/go_router.dart'; +import 'package:collaborative_science_platform/screens/auth_screens/widgets/privacy_policy_form.dart'; class SignUpPage extends StatefulWidget { static const routeName = '/signup'; @@ -44,6 +45,8 @@ class _SignUpPageState extends State { String errorMessage = ""; + bool isChecked = false; + @override void dispose() { emailController.dispose(); @@ -93,7 +96,8 @@ class _SignUpPageState extends State { passwordController.text, confirmPasswordController.text) && nameController.text.isNotEmpty && surnameController.text.isNotEmpty && - emailController.text.isNotEmpty) { + emailController.text.isNotEmpty && + isChecked) { setState(() { buttonState = true; }); @@ -296,7 +300,76 @@ class _SignUpPageState extends State { style: const TextStyle(color: AppColors.dangerColor), ), ), - const SizedBox(height: 10.0), + Row( + children: [ + Checkbox( + value: isChecked, + activeColor: AppColors.primaryColor, // Set the checkbox color when checked + checkColor: Colors.white, // Set the check mark color + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(2.0), + ), + side: MaterialStateBorderSide.resolveWith( + (states) => const BorderSide( + width: 1.2, + color: AppColors.primaryColor, + ), + ), + onChanged: (bool? value) { + setState(() { + isChecked = value!; + }); + validateStrongPassword(); + }, + ), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: SizedBox( + width: 500, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(width: 1.5, color: Colors.grey), + ), + ), + padding: const EdgeInsets.only( + bottom: 8.0), // Adjust the value as needed + child: const Text( + 'Collaborative Science Platform Privacy Policy', + style: TextStyle(fontSize: 20.0), + ), + )), + const SizedBox(height: 10.0), + ], + ), + ), + backgroundColor: Colors.white, + shadowColor: Colors.white, + content: const PrivacyPolicyForm(), + ), + ); + }, + child: const Text( + "Accept privacy policy.", + style: TextStyle( + fontWeight: FontWeight.bold, + color: AppColors.hyperTextColor, + ), + ), + ), + ), + ], + ), + const SizedBox(height: 15.0), AppButton( onTap: () async { if (await authenticate() && mounted) { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart new file mode 100644 index 00000000..be06de89 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart @@ -0,0 +1,73 @@ +import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; +import 'package:flutter/material.dart'; + +class PrivacyPolicyForm extends StatefulWidget { + const PrivacyPolicyForm({super.key}); + + @override + State createState() => _PrivacyPolicyForm(); +} + +class _PrivacyPolicyForm extends State { + @override + Widget build(BuildContext context) { + return const SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Padding( + padding: EdgeInsets.all(18.0), + + //TODO latex + //TODO use actual text + child: Text('''Privacy Policy for Collobrative Science Platform + + Thank you for choosing this Science Platform. This Privacy Policy is designed to help you understand how we collect, use, and safeguard your personal information when you visit our website or use our services. + + 1. Information We Collect: + a. Personal Information: + We may collect personal information such as your name, email address, and affiliation when you register for an account or subscribe to our services. + + b. Usage Information: + We automatically collect information about your interaction with our platform, including your IP address, device information, and browsing behavior. + + 2. How We Use Your Information: + a. Providing Services: + We use your personal information to provide you with access to our science platform, including personalized content and features based on your preferences. + + b. Communication: + We may use your contact information to send you important updates, newsletters, and information related to our platform. You can opt out of promotional emails at any time. + + c. Improving Services: + We analyze user behavior to improve our platform, enhance user experience, and develop new features. + + 3. Information Sharing: + a. Third-Party Service Providers: + We may share your information with third-party service providers who assist us in delivering and improving our services. + + b. Legal Compliance: + We may disclose your information if required by law or in response to legal requests. + + 4. Data Security: + We employ industry-standard security measures to protect your information from unauthorized access, disclosure, alteration, and destruction. + + 5. Cookies and Tracking Technologies: + We use cookies and similar technologies to collect information about your usage patterns and preferences. You can manage your cookie preferences through your browser settings. + + 6. Your Choices: + You have the right to access, correct, or delete your personal information. You can manage your communication preferences and account settings through your profile. + + 7. Childrens Privacy: + Our platform is not intended for children under the age of 13. We do not knowingly collect personal information from children. + + 8. Changes to This Privacy Policy: + We may update this Privacy Policy to reflect changes in our practices. We encourage you to review this page periodically for the latest information. + + 9. Contact Us: + If you have any questions or concerns about this Privacy Policy, please contact us at some-mail@science.com.tr + By using Science Platform, you agree to the terms outlined in this Privacy Policy. Please review this policy regularly for updates. + '''), + + ), + ); + } +} + From e3632e45c82c1ac0ee6ec9e6cba85c8e668212b0 Mon Sep 17 00:00:00 2001 From: brunettow Date: Sat, 16 Dec 2023 18:29:26 +0300 Subject: [PATCH 08/22] fix wrong base. Changed base to frontend --- .../lib/screens/auth_screens/widgets/privacy_policy_form.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart index be06de89..def90da9 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/privacy_policy_form.dart @@ -1,4 +1,3 @@ -import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; import 'package:flutter/material.dart'; class PrivacyPolicyForm extends StatefulWidget { @@ -65,9 +64,7 @@ class _PrivacyPolicyForm extends State { If you have any questions or concerns about this Privacy Policy, please contact us at some-mail@science.com.tr By using Science Platform, you agree to the terms outlined in this Privacy Policy. Please review this policy regularly for updates. '''), - ), ); } } - From 40d57ff6409e8f90d374008f8b9020adc672936e Mon Sep 17 00:00:00 2001 From: brunettow Date: Sun, 17 Dec 2023 13:38:11 +0300 Subject: [PATCH 09/22] button to the node details page --- .../widgets/node_details.dart | 65 +++++++++++++------ .../lib/widgets/app_button.dart | 10 ++- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index e1a9a948..b3d6d251 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -33,6 +33,7 @@ class NodeDetails extends StatefulWidget { class _NodeDetailsState extends State { int currentIndex = 0; + bool isHidden = false; void updateIndex(int index) { setState(() { @@ -54,22 +55,23 @@ class _NodeDetailsState extends State { primary: false, scrollDirection: Axis.vertical, child: Column( - mainAxisAlignment: MainAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: CardContainer( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: Responsive.isDesktop(context) - ? const EdgeInsets.all(70.0) - : const EdgeInsets.all(10.0), - child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), - textAlign: TextAlign.center, style: TextStyles.title2)), - Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( + child: Column( + //mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: Responsive.isDesktop(context) + ? const EdgeInsets.all(70.0) + : const EdgeInsets.all(10.0), + child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), + textAlign: TextAlign.center, style: TextStyles.title2)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, children: [ SelectableText.rich( TextSpan(children: [ @@ -83,12 +85,37 @@ class _NodeDetailsState extends State { ) ]), ), - ], - ), - Column( - children: [ Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ + isHidden + ? SizedBox( + width: 110, + child: AppButton( + text: "Show", + height: 40, + icon: const Icon( + CupertinoIcons.eye, + size: 16, + color: Colors.white, + ), + type: "safe", + onTap: () {}), + ) + : SizedBox( + width: 110, + child: AppButton( + text: "Hide", + height: 40, + icon: const Icon( + CupertinoIcons.eye_slash, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: () {}), + ), + const SizedBox(width: 10), SizedBox( width: 110, child: AppButton( @@ -123,9 +150,9 @@ class _NodeDetailsState extends State { ) ], ), - ]), - ], - )), + ], + ), + ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), diff --git a/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart b/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart index 53ecd144..e56ea59b 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart @@ -29,8 +29,14 @@ class AppButton extends StatelessWidget { ? ElevatedButton.styleFrom( backgroundColor: isActive ? (type == "primary" - ? AppColors.primaryColor - : (type == "secondary" ? AppColors.secondaryColor : Colors.grey[600])) + ? const Color.fromRGBO(8, 155, 171, 1) + : (type == "secondary" + ? AppColors.secondaryColor + : (type == "danger" + ? AppColors.dangerColor + : (type == "safe" + ? Colors.grey[600] // const Color.fromARGB(255, 111, 221, 112) + : Colors.grey[600])))) : Colors.grey[600], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), From 087bf01fc4953f8b55e0e20e0503604b8afa28ce Mon Sep 17 00:00:00 2001 From: defabdullah Date: Sun, 17 Dec 2023 19:06:06 +0300 Subject: [PATCH 10/22] add contributor control --- .../models/node_details_page/question.dart | 23 +++--- .../lib/providers/question_provider.dart | 7 +- .../widgets/ask_question_form.dart | 17 +++-- .../widgets/node_details.dart | 30 ++++++++ .../widgets/question_box.dart | 7 +- .../widgets/questions_list_view.dart | 28 +++++--- .../screens/profile_page/profile_page.dart | 72 ++----------------- .../widgets/question_activity.dart | 1 + 8 files changed, 87 insertions(+), 98 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart index 64b97711..81cb57d1 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/node_details_page/question.dart @@ -9,16 +9,17 @@ class Question { User? answerer; String? answeredAt; int? nodeId; - Question({ - required this.id, - required this.content, - required this.createdAt, - required this.asker, - required this.answer, - required this.answerer, - required this.answeredAt, - required this.nodeId, - }); + bool isAnswered; + Question( + {required this.id, + required this.content, + required this.createdAt, + required this.asker, + required this.answer, + required this.answerer, + required this.answeredAt, + required this.nodeId, + required this.isAnswered}); factory Question.fromJson(Map jsonString) { return Question( id: jsonString['id'] ?? -1, @@ -31,6 +32,7 @@ class Question { : User.fromJsonforNodeDetailPage(jsonString['answerer']), asker: User.fromJsonforNodeDetailPage(jsonString['asker']), nodeId: jsonString['node_id'] ?? -1, + isAnswered: jsonString['answer_content'] != null, ); } @@ -56,6 +58,7 @@ class Question { : null, answeredAt: jsonString.containsKey("answer_date") ? jsonString["answer_date"] as String : "", nodeId: jsonString['node_id'] ?? -1, + isAnswered: jsonString['is_answered'] == 1, ); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart index b192bc18..852b80f2 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart @@ -34,15 +34,15 @@ class QuestionAnswerProvider with ChangeNotifier { ); if (response.statusCode == 201) { _questions.add(Question( - //id: json.decode(response.body)['id'], - id: 99, + id: json.decode(response.body)['QuestionID'], answer: null, content: questionText, createdAt: DateTime.now().toString(), asker: user, answerer: null, answeredAt: null, - nodeId: nodeId)); + nodeId: nodeId, + isAnswered: false)); notifyListeners(); } else if (response.statusCode == 401) { throw PostQuestionError(); @@ -74,6 +74,7 @@ class QuestionAnswerProvider with ChangeNotifier { if (response.statusCode == 201) { final data = json.decode(response.body); Question answeredQuestions = _questions.firstWhere((element) => element.id == questionId); + answeredQuestions.isAnswered = true; answeredQuestions.answer = data['answer_content']; answeredQuestions.answeredAt = data['answered_at']; answeredQuestions.answerer = user; diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart index 7098940b..0dcec692 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart @@ -27,13 +27,16 @@ class _AskQuestionFormState extends State { try { if (questionController.text.isNotEmpty) { final questionProvider = Provider.of(context, listen: false); - User user = Provider.of(context, listen: false).user!; - setState(() { - isLoading = true; - }); - await questionProvider.postQuestion(questionController.text, widget.nodeId, user); - widget.onQuestionPosted(questionProvider.questions.last); - questionController.clear(); + Auth authProvider = Provider.of(context, listen: false); + if (authProvider.isSignedIn) { + setState(() { + isLoading = true; + }); + await questionProvider.postQuestion( + questionController.text, widget.nodeId, authProvider.user!); + widget.onQuestionPosted(questionProvider.questions.last); + questionController.clear(); + } } } on PostQuestionError { setState(() { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index 4e45e644..c189074a 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -1,5 +1,6 @@ import 'package:collaborative_science_platform/helpers/node_helper.dart'; import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; +import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/screens/graph_page/graph_page.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/contributors_list_view.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/node_details_tab_bar.dart'; @@ -17,6 +18,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_tex/flutter_tex.dart'; import 'package:go_router/go_router.dart'; import 'dart:convert'; +import 'package:provider/provider.dart'; class NodeDetails extends StatefulWidget { final NodeDetailed node; @@ -33,6 +35,22 @@ class NodeDetails extends StatefulWidget { class _NodeDetailsState extends State { int currentIndex = 0; + bool canAnswerQuestions = false; + bool canAskQuestions = false; + + @override + void initState() { + super.initState(); + canAnswer(); + } + + @override + void didUpdateWidget(covariant NodeDetails oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.node != widget.node) { + canAnswer(); + } + } void updateIndex(int index) { setState(() { @@ -40,6 +58,16 @@ class _NodeDetailsState extends State { }); } + void canAnswer() async { + Auth authProvider = Provider.of(context, listen: false); + setState(() { + canAnswerQuestions = authProvider.isSignedIn + ? widget.node.contributors.any((contributor) => contributor.id == authProvider.user!.id) + : false; + canAskQuestions = authProvider.isSignedIn; + }); + } + @override Widget build(BuildContext context) { return Container( @@ -190,6 +218,8 @@ class _NodeDetailsState extends State { child: QuestionsView( questions: widget.node.questions, nodeId: widget.node.nodeId, + canAnswer: canAnswerQuestions, + canAsk: canAskQuestions, ), ), if (currentIndex == 5) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart index 2493fc13..41ce9ecb 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart @@ -4,8 +4,9 @@ import 'package:flutter/material.dart'; class QuestionBox extends StatefulWidget { final Question question; + final bool canAnswer; - const QuestionBox({super.key, required this.question}); + const QuestionBox({super.key, required this.question, required this.canAnswer}); @override State createState() => _QuestionBoxState(); @@ -30,7 +31,7 @@ class _QuestionBoxState extends State { ), const SizedBox(height: 8.0), Text('Asked by: ${widget.question.asker.email} at ${widget.question.createdAt}'), - if (widget.question.answer != null && widget.question.answer!.isNotEmpty) + if (widget.question.isAnswered) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -41,7 +42,7 @@ class _QuestionBoxState extends State { ), ], ), - if (widget.question.answer == null || widget.question.answer!.isEmpty) + if (widget.question.isAnswered == false && widget.canAnswer) Column( children: [ const SizedBox(height: 8.0), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart index c73141b7..59d770db 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart @@ -7,7 +7,15 @@ import 'package:collaborative_science_platform/utils/responsive/responsive.dart' class QuestionsView extends StatefulWidget { final int nodeId; final List questions; - const QuestionsView({Key? key, required this.nodeId, required this.questions}) : super(key: key); + final bool canAnswer; + final bool canAsk; + const QuestionsView( + {Key? key, + required this.nodeId, + required this.questions, + required this.canAnswer, + required this.canAsk}) + : super(key: key); @override State createState() => _QuestionsViewState(); @@ -34,14 +42,15 @@ class _QuestionsViewState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - AskQuestionForm( - nodeId: widget.nodeId, - onQuestionPosted: (Question newQuestion) { - setState(() { - questions.add(newQuestion); - }); - }, - ), + if (widget.canAsk) + AskQuestionForm( + nodeId: widget.nodeId, + onQuestionPosted: (Question newQuestion) { + setState(() { + questions.add(newQuestion); + }); + }, + ), Flexible( child: ListView.builder( shrinkWrap: true, @@ -49,6 +58,7 @@ class _QuestionsViewState extends State { itemBuilder: (BuildContext context, int index) { return QuestionBox( question: questions[index], + canAnswer: widget.canAnswer, ); }, ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index b31a2633..082e3972 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -105,6 +105,9 @@ class _ProfilePageState extends State { @override Widget build(BuildContext context) { final User? user = Provider.of(context).user; + var asked = profileData.askedQuestions.where((element) => element.isAnswered).toList(); + var answered = profileData.answeredQuestions.where((element) => element.isAnswered).toList(); + var questionList = asked + answered; if (user == null) { // guest can see profile pages } else if (user.email == profileData.email) { @@ -180,28 +183,7 @@ class _ProfilePageState extends State { child: CardContainer( child: SizedBox( height: 400, - child: QuestionActivity(questions: - // temp question list - [ - Question( - id: 1, - content: "How does Flutter work?", - asker: User( - firstName: "John Doe", - email: "as", - lastName: "1", - ), - createdAt: DateTime.now().toString(), - answer: - "Flutter works by using the Dart programming language...", - answerer: User( - firstName: "John Doe", - email: "as", - lastName: "1", - ), - answeredAt: DateTime.now().toString(), - nodeId: 100), - ]), + child: QuestionActivity(questions: questionList), ), ), ), @@ -269,28 +251,7 @@ class _ProfilePageState extends State { child: CardContainer( child: SizedBox( height: 400, - child: QuestionActivity(questions: - // temp question list - [ - Question( - id: 1, - content: "How does Flutter work?", - asker: User( - firstName: "John Doe", - email: "as", - lastName: "1", - ), - createdAt: DateTime.now().toString(), - answer: - "Flutter works by using the Dart programming language...", - answerer: User( - firstName: "John Doe", - email: "as", - lastName: "1", - ), - answeredAt: DateTime.now().toString(), - nodeId: 100), - ]), + child: QuestionActivity(questions: questionList), ), ), ), @@ -368,28 +329,7 @@ class _ProfilePageState extends State { child: CardContainer( child: SizedBox( height: 400, - child: QuestionActivity(questions: - // temp question list - [ - Question( - id: 1, - content: "How does Flutter work?", - asker: User( - firstName: "John Doe", - email: "as", - lastName: "1", - ), - createdAt: DateTime.now().toString(), - answer: - "Flutter works by using the Dart programming language...", - answerer: User( - firstName: "John Doe", - email: "as", - lastName: "1", - ), - answeredAt: DateTime.now().toString(), - nodeId: 100), - ]), + child: QuestionActivity(questions: questionList), ), ), ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart index 6d792c0c..c17ebc59 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/question_activity.dart @@ -19,6 +19,7 @@ class QuestionActivity extends StatelessWidget { return ElevatedButton( child: QuestionBox( question: questions[index], + canAnswer: false, ), onPressed: () { context.push("${NodeDetailsPage.routeName}/${questions[index].nodeId}"); From c54881c861b77d10dd61b7b54f3314ea366c5459 Mon Sep 17 00:00:00 2001 From: Omer Date: Sun, 17 Dec 2023 19:15:57 +0300 Subject: [PATCH 11/22] Create annotation provider and model --- .../lib/main.dart | 2 + .../lib/models/annotation.dart | 26 ++++------ .../lib/providers/annotation_provider.dart | 48 +++++++++++++++++++ 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/providers/annotation_provider.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/main.dart b/project/FrontEnd/collaborative_science_platform/lib/main.dart index 98a9f70e..30615f7b 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/main.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:collaborative_science_platform/providers/annotation_provider.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/profile_data_provider.dart'; import 'package:collaborative_science_platform/providers/node_provider.dart'; @@ -35,6 +36,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (context) => NodeProvider()), ChangeNotifierProvider(create: (context) => UserProvider()), ChangeNotifierProvider(create: (context) => WorkspaceProvider()), + ChangeNotifierProvider(create: (context) => AnnotationProvider()), ], // child: MaterialApp( // debugShowCheckedModeBanner: false, diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/annotation.dart b/project/FrontEnd/collaborative_science_platform/lib/models/annotation.dart index fe20d8c4..97f6270c 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/annotation.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/annotation.dart @@ -1,25 +1,19 @@ -import 'package:collaborative_science_platform/models/basic_user.dart'; - class Annotation { - int annotationID; - String annotationType; - String annotationVisibilityType; - BasicUser owner; - Object annotationLocation; + int? annotationID; +// String annotationType; +// String annotationVisibilityType; +// BasicUser owner; +// Object annotationLocation; + String annotationContent; + String annotationAuthor; int startOffset; int endOffset; - DateTime createdAt; - DateTime updatedAt; Annotation({ - required this.annotationID, - required this.annotationType, - required this.annotationVisibilityType, - required this.owner, - required this.annotationLocation, + this.annotationID, required this.startOffset, required this.endOffset, - required this.createdAt, - required this.updatedAt, + required this.annotationContent, + required this.annotationAuthor, }); } diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/annotation_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/annotation_provider.dart new file mode 100644 index 00000000..8744a74a --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/annotation_provider.dart @@ -0,0 +1,48 @@ +import 'package:collaborative_science_platform/models/annotation.dart'; +import 'package:flutter/material.dart'; + +enum AnnotationType { theorem, proof } + +class AnnotationProvider with ChangeNotifier { + final List _annotations = []; + + List get annotations => _annotations; + + Future getAnnotations(AnnotationType annotationType) async { + _annotations.clear(); + if (AnnotationType.theorem == annotationType) { + _annotations.add( + Annotation( + annotationID: 1, + annotationContent: "This is an integral.", + annotationAuthor: "Author", + startOffset: 0, + endOffset: 36, + ), + ); + } else if (AnnotationType.proof == annotationType) { + _annotations.add( + Annotation( + annotationID: 1, + annotationContent: "You need to be carefull here.", + annotationAuthor: "Author", + startOffset: 152, + endOffset: 303, + ), + ); + } + } + + Future addAnnotation(Annotation annotation) async { + annotation.annotationID = _annotations.length + 1; + _annotations.add(annotation); + notifyListeners(); + } + + Future updateAnnotation(Annotation annotation, String text) async { + final index = + _annotations.indexWhere((element) => element.annotationID == annotation.annotationID); + _annotations[index].annotationContent = text; + notifyListeners(); + } +} From f5fa1b8a748b813b15c0aa7ffc205a6e993558d3 Mon Sep 17 00:00:00 2001 From: Omer Date: Sun, 17 Dec 2023 19:16:13 +0300 Subject: [PATCH 12/22] Update annotation text widget --- .../lib/utils/text_styles.dart | 2 +- .../lib/widgets/annotation_text.dart | 367 ++++++++++++++---- 2 files changed, 290 insertions(+), 79 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/utils/text_styles.dart b/project/FrontEnd/collaborative_science_platform/lib/utils/text_styles.dart index 8762e83b..6ec63fed 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/utils/text_styles.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/utils/text_styles.dart @@ -49,7 +49,7 @@ class TextStyles { ); static const TextStyle bodyBlack = TextStyle( - fontSize: 16, + fontSize: 10, ); static const TextStyle bodyGrey = TextStyle( fontSize: 10, diff --git a/project/FrontEnd/collaborative_science_platform/lib/widgets/annotation_text.dart b/project/FrontEnd/collaborative_science_platform/lib/widgets/annotation_text.dart index c09d1a15..628b3ac5 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/widgets/annotation_text.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/widgets/annotation_text.dart @@ -1,45 +1,171 @@ import 'dart:ui'; +import 'package:collaborative_science_platform/models/annotation.dart'; +import 'package:collaborative_science_platform/providers/annotation_provider.dart'; +import 'package:collaborative_science_platform/utils/colors.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; +import 'package:flutter_tex/flutter_tex.dart'; +import 'package:provider/provider.dart'; -class AnnotationText extends StatelessWidget { +class AnnotationText extends StatefulWidget { final String text; + final AnnotationType annotationType; final TextStyle? style; final TextAlign? textAlign; final int? maxLines; - const AnnotationText(this.text, {super.key, this.style, this.textAlign, this.maxLines}); + const AnnotationText( + this.text, { + super.key, + this.style, + this.textAlign, + this.maxLines, + this.annotationType = AnnotationType.theorem, + }); + + @override + State createState() => _AnnotationTextState(); +} + +class _AnnotationTextState extends State { + bool _initState = true; + bool isLoading = true; + bool error = false; + List annotations = []; + List annotationIndices = []; + List textSpans = []; + + @override + void didChangeDependencies() { + if (_initState) { + getAnnotations(); + _initState = false; + } + super.didChangeDependencies(); + } + + void getAnnotations() async { + try { + setState(() { + error = false; + isLoading = true; + }); + final annotationProvider = Provider.of(context); + await annotationProvider.getAnnotations(widget.annotationType); + annotations = annotationProvider.annotations; + for (var element in annotations) { + annotationIndices.add(element.startOffset); + annotationIndices.add(element.endOffset); + } + int textLeftCounter = 0; + int textRightCounter = 0; + int annotationCounter = 0; + while ( + textRightCounter < widget.text.length && annotationCounter < annotationIndices.length) { + if (textRightCounter == annotationIndices[annotationCounter]) { + if (textLeftCounter != textRightCounter) { + textSpans.add(TextSpan( + text: widget.text.substring(textLeftCounter, textRightCounter), + style: widget.style, + )); + } + textSpans.add(TextSpan( + text: widget.text.substring(textRightCounter, annotationIndices[annotationCounter + 1]), + style: TextStyle(backgroundColor: Colors.yellow.withOpacity(0.3)), + )); + textLeftCounter = annotationIndices[annotationCounter + 1]; + textRightCounter = annotationIndices[annotationCounter + 1]; + annotationCounter += 2; + } + textRightCounter++; + } + if (annotationCounter == annotationIndices.length) { + textSpans.add(TextSpan( + text: widget.text.substring(textLeftCounter, widget.text.length), + style: widget.style, + )); + } else if (textLeftCounter != textRightCounter) { + textSpans.add(TextSpan( + text: widget.text.substring(textLeftCounter, textRightCounter), + style: widget.style, + )); + } + if (annotationCounter == 0) { + textSpans.add(TextSpan( + text: widget.text, + style: widget.style, + )); + } + } catch (e) { + setState(() { + error = true; + }); + } finally { + setState(() { + isLoading = false; + }); + } + } @override Widget build(BuildContext context) { - return SelectableText( - text, - style: style, - maxLines: maxLines, - showCursor: true, - textAlign: textAlign, - contextMenuBuilder: (context, editableTextState) { - String selectedText = editableTextState.textEditingValue.selection.textInside(text); - return _MyContextMenu( - anchor: editableTextState.contextMenuAnchors.primaryAnchor, - selectedText: selectedText.trim(), - children: AdaptiveTextSelectionToolbar.getAdaptiveButtons( - context, - editableTextState.contextMenuButtonItems, - ).toList(), - ); - }, - ); + return isLoading + ? const Center(child: CircularProgressIndicator()) + : error + ? const Text( + "An error occured while initiating explanations! Please try again later.", + style: TextStyle(color: AppColors.dangerColor), + ) + : SelectableText.rich( + TextSpan( + children: textSpans, + ), + contextMenuBuilder: (context, editableTextState) { + var selectedIndices = editableTextState.textEditingValue.selection; + String selectedText = + editableTextState.textEditingValue.selection.textInside(widget.text); + for (var element in annotations) { + if (element.startOffset <= selectedIndices.baseOffset && + element.endOffset >= selectedIndices.extentOffset) { + return _MyContextMenu( + anchor: editableTextState.contextMenuAnchors.primaryAnchor, + selectedText: widget.text.substring(element.startOffset, element.endOffset), + annotation: element, + children: AdaptiveTextSelectionToolbar.getAdaptiveButtons( + context, + editableTextState.contextMenuButtonItems, + ).toList(), + ); + } + } + return _MyContextMenu( + anchor: editableTextState.contextMenuAnchors.primaryAnchor, + selectedText: selectedText.trim(), + startOffset: selectedIndices.baseOffset, + endOffset: selectedIndices.extentOffset, + children: AdaptiveTextSelectionToolbar.getAdaptiveButtons( + context, + editableTextState.contextMenuButtonItems, + ).toList(), + ); + }, + ); } } class _MyContextMenu extends StatelessWidget { + final Annotation? annotation; + final int? startOffset; + final int? endOffset; const _MyContextMenu({ required this.anchor, required this.children, required this.selectedText, + this.annotation, + this.startOffset, + this.endOffset, }); final Offset anchor; @@ -63,10 +189,18 @@ class _MyContextMenu extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ //...children, - - AddAnnotationButton(text: selectedText), + AddAnnotationButton( + text: selectedText, + startOffset: startOffset, + endOffset: endOffset, + annotation: annotation), const SizedBox(height: 2), - ShowAnnotationButton(text: selectedText), + annotation != null + ? ShowAnnotationButton( + text: selectedText, + annotation: annotation!, + ) + : const SizedBox(), ], ), ), @@ -78,18 +212,21 @@ class _MyContextMenu extends StatelessWidget { class ShowAnnotationButton extends StatelessWidget { final String text; - const ShowAnnotationButton({super.key, required this.text}); + final Annotation annotation; + const ShowAnnotationButton({super.key, required this.text, required this.annotation}); @override Widget build(BuildContext context) { return Responsive( - mobile: MobileShowAnnotationButton(text), desktop: DesktopShowAnnotationButton(text)); + mobile: MobileShowAnnotationButton(text, annotation), + desktop: DesktopShowAnnotationButton(text, annotation)); } } class MobileShowAnnotationButton extends StatefulWidget { final String text; - const MobileShowAnnotationButton(this.text, {super.key}); + final Annotation annotation; + const MobileShowAnnotationButton(this.text, this.annotation, {super.key}); @override State createState() => _MobileShowAnnotationButtonState(); @@ -128,14 +265,14 @@ class _MobileShowAnnotationButtonState extends State style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500), ), - content: const SizedBox( + content: SizedBox( height: 200, width: 400, child: Column( children: [ SelectableText( - "Automaton is a relatively self-operating machine.", - style: TextStyle(color: Colors.white), + widget.annotation.annotationContent, + style: const TextStyle(color: Colors.white), maxLines: 5, ) ], @@ -153,7 +290,8 @@ class _MobileShowAnnotationButtonState extends State class DesktopShowAnnotationButton extends StatefulWidget { final String text; - const DesktopShowAnnotationButton(this.text, {super.key}); + final Annotation annotation; + const DesktopShowAnnotationButton(this.text, this.annotation, {super.key}); @override State createState() => _DesktopShowAnnotationButtonState(); @@ -220,13 +358,27 @@ class _DesktopShowAnnotationButtonState extends State createState() => _AddAnnotationButtonState(); @@ -250,11 +409,56 @@ class AddAnnotationButton extends StatefulWidget { class _AddAnnotationButtonState extends State { bool isHovering = false; bool isPortalOpen = false; + bool isSaving = false; + bool change = false; final TextEditingController _textEditingController = TextEditingController(); - void _submit() { - print(_textEditingController.text); - ContextMenuController.removeAny(); + @override + void initState() { + if (widget.annotation != null) { + change = true; + _textEditingController.text = widget.annotation!.annotationContent; + } + super.initState(); + } + + _submit(BuildContext context) async { + var scaffoldMessenger = ScaffoldMessenger.of(context); + var provider = Provider.of(context, listen: false); + var navigator = Navigator.of(context); + if (mounted) { + setState(() { + isSaving = true; + }); + } + try { + if (change) { + await provider.updateAnnotation(widget.annotation!, _textEditingController.text); + } else { + Annotation annotation = Annotation( + annotationContent: _textEditingController.text, + startOffset: widget.startOffset!, + endOffset: widget.endOffset!, + annotationAuthor: "test", + ); + await provider.addAnnotation(annotation); + } + } catch (e) { + scaffoldMessenger.showSnackBar( + const SnackBar( + content: Text("Something went wrong!"), + ), + ); + } finally { + if (mounted) { + setState(() { + isSaving = false; + }); + } + navigator.pop(); + } + + //ContextMenuController.removeAny(); } @override @@ -273,49 +477,56 @@ class _AddAnnotationButtonState extends State { onTap: () => showDialog( context: context, builder: (BuildContext context) { - return BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: AlertDialog( - backgroundColor: Colors.grey[800]!.withOpacity(0.7), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6.0), - ), - elevation: 20, - title: Text( - widget.text, - style: const TextStyle( - color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500), - ), - content: SizedBox( - height: 200, - width: 400, - child: Column(children: [ - TextField( - controller: _textEditingController, - style: const TextStyle(color: Colors.white), - decoration: InputDecoration( - border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), - labelText: 'Annotation', - labelStyle: TextStyle(color: Colors.grey[500]), + return isSaving + ? const Center( + child: CircularProgressIndicator(), + ) + : BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: AlertDialog( + backgroundColor: Colors.grey[800]!.withOpacity(0.7), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6.0), + ), + elevation: 20, + title: TeXView( + child: TeXViewDocument( + widget.text, + style: TeXViewStyle( + contentColor: Colors.white, + fontStyle: TeXViewFontStyle(fontSize: 14), ), - maxLines: 5, - ) - ])), - contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), - actions: [ - TextButton( - onPressed: () { - _submit(); - Navigator.of(context).pop(); - }, - child: const Text('Save', style: TextStyle(color: Colors.white)), - ), - ], - ), - ); + )), + content: SizedBox( + height: 200, + width: 400, + child: Column(children: [ + TextField( + controller: _textEditingController, + style: const TextStyle(color: Colors.white), + decoration: InputDecoration( + border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), + labelText: 'Annotation', + labelStyle: TextStyle(color: Colors.grey[500]), + ), + maxLines: 5, + ) + ])), + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + actions: [ + TextButton( + onPressed: () async { + await _submit(context); + }, + child: const Text('Save', style: TextStyle(color: Colors.white)), + ), + ], + ), + ); }, ), - child: AnnotationButtonItem(isHovering: isHovering, text: "Add Annotation"), + child: AnnotationButtonItem( + isHovering: isHovering, text: change ? "Change Annotation" : "Add Annotation"), ), ); } From 96134493d48de48f7a9c45c287011c13305c3021 Mon Sep 17 00:00:00 2001 From: Omer Date: Sun, 17 Dec 2023 19:16:28 +0300 Subject: [PATCH 13/22] Implement annotation widget to various pages --- .../graph_page/widgets/graph_node_popup.dart | 3 +- .../widgets/graph_page_node_card.dart | 3 +- .../widgets/node_details.dart | 33 +++++++++++--- .../widgets/proof_list_view.dart | 43 ++++++++++++++++--- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_node_popup.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_node_popup.dart index c0c1ff3a..e4527f4d 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_node_popup.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_node_popup.dart @@ -8,7 +8,6 @@ import 'package:collaborative_science_platform/models/node_details_page/node_det import 'package:collaborative_science_platform/providers/node_provider.dart'; import 'package:collaborative_science_platform/screens/graph_page/graph_page.dart'; import 'package:collaborative_science_platform/screens/node_details_page/node_details_page.dart'; -import 'package:collaborative_science_platform/widgets/annotation_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tex/flutter_tex.dart'; import 'package:collaborative_science_platform/helpers/date_to_string.dart'; @@ -87,7 +86,7 @@ class _NodeDetailsPopupState extends State { return BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: AlertDialog( - title: AnnotationText( + title: SelectableText( utf8.decode(node.nodeTitle.codeUnits), style: const TextStyle( fontWeight: FontWeight.bold, diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_page_node_card.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_page_node_card.dart index 78cdd1a8..00c27d9d 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_page_node_card.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/graph_page/widgets/graph_page_node_card.dart @@ -1,7 +1,6 @@ import 'package:collaborative_science_platform/helpers/date_to_string.dart'; import 'package:collaborative_science_platform/helpers/node_helper.dart'; import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; -import 'package:collaborative_science_platform/widgets/annotation_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tex/flutter_tex.dart'; import 'dart:convert'; @@ -47,7 +46,7 @@ class GraphPageNodeCard extends StatelessWidget { ), child: Padding( padding: const EdgeInsets.only(bottom: 8.0), - child: AnnotationText( + child: SelectableText( utf8.decode(node.nodeTitle.codeUnits), style: const TextStyle( fontWeight: FontWeight.bold, diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index e1a9a948..00dc5d58 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -1,5 +1,6 @@ import 'package:collaborative_science_platform/helpers/node_helper.dart'; import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; +import 'package:collaborative_science_platform/providers/annotation_provider.dart'; import 'package:collaborative_science_platform/screens/graph_page/graph_page.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/contributors_list_view.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/node_details_tab_bar.dart'; @@ -33,6 +34,7 @@ class NodeDetails extends StatefulWidget { class _NodeDetailsState extends State { int currentIndex = 0; + bool showAnnotations = false; void updateIndex(int index) { setState(() { @@ -66,7 +68,7 @@ class _NodeDetailsState extends State { padding: Responsive.isDesktop(context) ? const EdgeInsets.all(70.0) : const EdgeInsets.all(10.0), - child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), + child: SelectableText(utf8.decode(widget.node.nodeTitle.codeUnits), textAlign: TextAlign.center, style: TextStyles.title2)), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( @@ -144,12 +146,33 @@ class _NodeDetailsState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + width: 180, + child: AppButton( + text: showAnnotations ? "Show Text" : "Show Annotations", + height: 40, + onTap: () { + setState(() { + showAnnotations = !showAnnotations; + }); + })), + ], + ), + const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: TeXView( - renderingEngine: const TeXViewRenderingEngine.katex(), - child: TeXViewDocument( - NodeHelper.getNodeContentLatex(widget.node, "long")))), + child: showAnnotations + ? AnnotationText( + NodeHelper.getNodeContentLatex(widget.node, "long"), + annotationType: AnnotationType.theorem, + ) + : TeXView( + renderingEngine: const TeXViewRenderingEngine.katex(), + child: TeXViewDocument( + NodeHelper.getNodeContentLatex(widget.node, "long")))), SelectableText.rich( textAlign: TextAlign.start, TextSpan(children: [ diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/proof_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/proof_list_view.dart index 148714fd..feb0ae9b 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/proof_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/proof_list_view.dart @@ -1,15 +1,24 @@ import 'package:collaborative_science_platform/models/node_details_page/proof.dart'; +import 'package:collaborative_science_platform/providers/annotation_provider.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; import 'package:collaborative_science_platform/utils/text_styles.dart'; +import 'package:collaborative_science_platform/widgets/annotation_text.dart'; +import 'package:collaborative_science_platform/widgets/app_button.dart'; import 'package:collaborative_science_platform/widgets/card_container.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tex/flutter_tex.dart'; import 'dart:convert'; -class ProofListView extends StatelessWidget { +class ProofListView extends StatefulWidget { final List proof; const ProofListView({super.key, required this.proof}); + @override + State createState() => _ProofListViewState(); +} + +class _ProofListViewState extends State { + bool showAnnotations = false; @override Widget build(BuildContext context) { return Container( @@ -19,7 +28,7 @@ class ProofListView extends StatelessWidget { scrollDirection: Axis.vertical, shrinkWrap: true, padding: const EdgeInsets.all(8), - itemCount: proof.length, + itemCount: widget.proof.length, itemBuilder: (BuildContext context, int index) { return Padding( padding: const EdgeInsets.all(5), @@ -38,9 +47,31 @@ class ProofListView extends StatelessWidget { // style: TextStyles.title4, // textAlign: TextAlign.start, // ), - TeXView( - renderingEngine: const TeXViewRenderingEngine.katex(), - child: TeXViewDocument(utf8.decode(proof[index].proofContent.codeUnits))), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + width: 180, + child: AppButton( + text: showAnnotations ? "Show Text" : "Show Annotations", + height: 40, + onTap: () { + setState(() { + showAnnotations = !showAnnotations; + }); + })), + ], + ), + const SizedBox(height: 8), + showAnnotations + ? AnnotationText( + utf8.decode(widget.proof[index].proofContent.codeUnits), + annotationType: AnnotationType.proof, + ) + : TeXView( + renderingEngine: const TeXViewRenderingEngine.katex(), + child: TeXViewDocument( + utf8.decode(widget.proof[index].proofContent.codeUnits))), // Row( // mainAxisAlignment: MainAxisAlignment.end, @@ -62,7 +93,7 @@ class ProofListView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - proof[index].publishDate.toString(), + widget.proof[index].publishDate.toString(), style: TextStyles.bodyGrey, textAlign: TextAlign.end, ) From 52ebaaf86303bd9231b59440b2ad7a8ec77a9619 Mon Sep 17 00:00:00 2001 From: brunettow Date: Sun, 17 Dec 2023 23:14:27 +0300 Subject: [PATCH 14/22] buttons implemented except q/a --- .../lib/screens/error_page/error_page.dart | 57 +++ .../widgets/confirmation_page.dart | 23 + .../widgets/node_details.dart | 330 +++++++------ .../screens/profile_page/profile_page.dart | 463 ++++++++++-------- .../profile_page/widgets/about_me.dart | 103 +++- .../lib/widgets/app_button.dart | 8 +- 6 files changed, 607 insertions(+), 377 deletions(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart create mode 100644 project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart new file mode 100644 index 00000000..bc78d9b3 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart @@ -0,0 +1,57 @@ +import 'package:collaborative_science_platform/screens/home_page/home_page.dart'; +import 'package:collaborative_science_platform/utils/colors.dart'; +import 'package:collaborative_science_platform/utils/text_styles.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class ErrorPage extends StatelessWidget { + static const routeName = '/page_not_found/'; + const ErrorPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 20), + Container( + margin: const EdgeInsets.fromLTRB(15.0, 0.0, 15.0, 0.0), + child: Text( + "404", + style: TextStyles.title4.copyWith(fontSize: 120), + ), + ), + Container( + margin: const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 0.0), + child: const Text( + "This page doesn't exist.", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: AppColors.secondaryDarkColor, + ), + ), + ), + const SizedBox(height: 8.0), // Add space between texts + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + context.go(HomePage.routeName); + }, + child: const Text( + "Return to main page", + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + color: Color.fromARGB(255, 67, 85, 186), + ), + ), + ), + ), + ], + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart new file mode 100644 index 00000000..02f5f497 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ConfirmationPage extends StatelessWidget { + const ConfirmationPage({super.key}); + + @override + Widget build(BuildContext context) { + return const AlertDialog( + title: SizedBox( + width: 500, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Are you sure?', style: TextStyle(fontSize: 20.0)), + ], + ), + ), + backgroundColor: Colors.white, + shadowColor: Colors.white, + content: null, + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index 9af16a59..ae4fd08f 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -12,6 +12,7 @@ import 'package:collaborative_science_platform/widgets/annotation_text.dart'; import 'package:collaborative_science_platform/widgets/app_button.dart'; import 'package:collaborative_science_platform/widgets/card_container.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; +import 'package:collaborative_science_platform/screens/error_page/error_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tex/flutter_tex.dart'; @@ -33,7 +34,8 @@ class NodeDetails extends StatefulWidget { class _NodeDetailsState extends State { int currentIndex = 0; - bool isHidden = false; + bool isHidden = true; + String userType = "admin"; void updateIndex(int index) { setState(() { @@ -43,177 +45,191 @@ class _NodeDetailsState extends State { @override Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: Colors.grey[200], - ), - width: Responsive.isDesktop(context) - ? Responsive.desktopPageWidth * 0.8 - : Responsive.getGenericPageWidth(context), - height: MediaQuery.of(context).size.height - 60, - child: SingleChildScrollView( - primary: false, - scrollDirection: Axis.vertical, - child: Column( - // mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: Column( - //mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: Responsive.isDesktop(context) - ? const EdgeInsets.all(70.0) - : const EdgeInsets.all(10.0), - child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), - textAlign: TextAlign.center, style: TextStyles.title2)), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SelectableText.rich( - TextSpan( - text: widget.node.publishDateFormatted, - style: TextStyles.bodyBlack, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - isHidden - ? SizedBox( + return (isHidden && userType != "admin") + ? const ErrorPage() + : Container( + decoration: BoxDecoration( + color: Colors.grey[200], + ), + width: Responsive.isDesktop(context) + ? Responsive.desktopPageWidth * 0.8 + : Responsive.getGenericPageWidth(context), + height: MediaQuery.of(context).size.height - 60, + child: SingleChildScrollView( + primary: false, + scrollDirection: Axis.vertical, + child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: Column( + //mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: Responsive.isDesktop(context) + ? const EdgeInsets.all(70.0) + : const EdgeInsets.all(10.0), + child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), + textAlign: TextAlign.center, style: TextStyles.title2)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SelectableText.rich( + TextSpan( + text: widget.node.publishDateFormatted, + style: TextStyles.bodyBlack, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Visibility( + visible: userType == "admin" ? true : false, + child: isHidden + ? SizedBox( + width: 110, + child: AppButton( + text: "Show", + height: 40, + icon: const Icon( + CupertinoIcons.eye, + size: 16, + color: Colors.white, + ), + type: "grey", + onTap: () { + setState(() { + isHidden = false; + }); + }), + ) + : SizedBox( + width: 110, + child: AppButton( + text: "Hide", + height: 40, + icon: const Icon( + CupertinoIcons.eye_slash, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: () { + setState(() { + isHidden = true; + }); + }), + ), + ), + const SizedBox(width: 10), + SizedBox( width: 110, child: AppButton( - text: "Show", + text: "Graph", height: 40, icon: const Icon( - CupertinoIcons.eye, + CupertinoIcons.square_grid_3x2, size: 16, color: Colors.white, ), - type: "safe", - onTap: () {}), - ) - : SizedBox( + type: "secondary", + onTap: () { + context + .push('${GraphPage.routeName}/${widget.node.nodeId}'); + }), + ), + const SizedBox(width: 10), + SizedBox( width: 110, child: AppButton( - text: "Hide", - height: 40, - icon: const Icon( - CupertinoIcons.eye_slash, - size: 16, - color: Colors.white, - ), - type: "danger", - onTap: () {}), + text: "Share", + icon: const Icon( + Icons.share, + size: 16, + color: Colors.white, + ), + height: 40, + type: "primary", + onTap: () => SharePage.shareNodeView(widget.node), + ), ), - const SizedBox(width: 10), - SizedBox( - width: 110, - child: AppButton( - text: "Graph", - height: 40, - icon: const Icon( - CupertinoIcons.square_grid_3x2, - size: 16, - color: Colors.white, - ), - type: "secondary", - onTap: () { - context.push('${GraphPage.routeName}/${widget.node.nodeId}'); - }), - ), - const SizedBox(width: 10), - SizedBox( - width: 110, - child: AppButton( - text: "Share", - icon: const Icon( - Icons.share, - size: 16, - color: Colors.white, + ], + ) + ], + ), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: NodeDetailsTabBar( + callback: updateIndex, + ), + ), + if (currentIndex == 0) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: Container( + width: Responsive.desktopPageWidth, + decoration: BoxDecoration(color: Colors.grey[200]), + child: CardContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: TeXView( + renderingEngine: const TeXViewRenderingEngine.katex(), + child: TeXViewDocument( + NodeHelper.getNodeContentLatex(widget.node, "long")))), + SelectableText.rich( + textAlign: TextAlign.start, + TextSpan( + text: widget.node.publishDateFormatted, + style: TextStyles.bodyBlack, ), - height: 40, - type: "primary", - onTap: () => SharePage.shareNodeView(widget.node), ), - ), - ], - ) - ], + ], + )), + )), + if (currentIndex == 1) + //proofs + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProofListView(proof: widget.node.proof), ), - ], - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: NodeDetailsTabBar( - callback: updateIndex, + if (currentIndex == 2) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ReferencesView(nodes: widget.node.references, ref: true), + ), + if (currentIndex == 3) + //citations + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ReferencesView(nodes: widget.node.citations), + ), + if (currentIndex == 4) + //Q/A + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: QuestionsView(questions: widget.node.questions), + ), + if (currentIndex == 5) + //contributors + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: Contributors( + contributors: widget.node.contributors, controller: widget.controller)), + ], ), ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: Container( - width: Responsive.desktopPageWidth, - decoration: BoxDecoration(color: Colors.grey[200]), - child: CardContainer( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: TeXView( - renderingEngine: const TeXViewRenderingEngine.katex(), - child: TeXViewDocument( - NodeHelper.getNodeContentLatex(widget.node, "long")))), - SelectableText.rich( - textAlign: TextAlign.start, - TextSpan( - text: widget.node.publishDateFormatted, - style: TextStyles.bodyBlack, - ), - ), - ], - )), - )), - if (currentIndex == 1) - //proofs - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProofListView(proof: widget.node.proof), - ), - if (currentIndex == 2) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ReferencesView(nodes: widget.node.references, ref: true), - ), - if (currentIndex == 3) - //citations - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ReferencesView(nodes: widget.node.citations), - ), - if (currentIndex == 4) - //Q/A - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: QuestionsView(questions: widget.node.questions), - ), - if (currentIndex == 5) - //contributors - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: Contributors( - contributors: widget.node.contributors, controller: widget.controller)), - ], - ), - ), - ); + ); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index adc8b502..767d88d9 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -3,6 +3,7 @@ import 'package:collaborative_science_platform/models/profile_data.dart'; import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/profile_data_provider.dart'; +import 'package:collaborative_science_platform/screens/error_page/error_page.dart'; import 'package:collaborative_science_platform/screens/home_page/widgets/home_page_appbar.dart'; import 'package:collaborative_science_platform/screens/node_details_page/node_details_page.dart'; import 'package:collaborative_science_platform/screens/page_with_appbar/page_with_appbar.dart'; @@ -39,6 +40,11 @@ class _ProfilePageState extends State { bool _isFirstTime = true; + bool isBanned = false; + String userType = "admin"; //visitor's user type + bool isValidUser = false; //visited user's type is contributor or reviewer + bool isReviewer = false; + int currentIndex = 0; void updateIndex(int index) { @@ -101,6 +107,18 @@ class _ProfilePageState extends State { } } + void handleButtonIsBanned() { + setState(() { + isBanned = !isBanned; // Toggle the state for example purposes + }); + } + + void handleButtonIsReviewer() { + setState(() { + isReviewer = !isReviewer; // Toggle the state for example purposes + }); + } + @override Widget build(BuildContext context) { final User? user = Provider.of(context).user; @@ -108,231 +126,254 @@ class _ProfilePageState extends State { // guest can see profile pages } else if (user.email == profileData.email) { // own profile page, should be editible - return PageWithAppBar( - appBar: const HomePageAppBar(), - pageColor: Colors.grey.shade200, - child: Responsive( - mobile: SingleChildScrollView( - child: SizedBox( - width: Responsive.getGenericPageWidth(context), - child: isLoading - ? Container( - decoration: const BoxDecoration(color: Colors.white), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Column( - children: [ - AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, + return (isBanned && userType != "admin") + ? const ErrorPage() + : PageWithAppBar( + appBar: const HomePageAppBar(), + pageColor: Colors.grey.shade200, + child: Responsive( + mobile: SingleChildScrollView( + child: SizedBox( + width: Responsive.getGenericPageWidth(context), + child: isLoading + ? Container( + decoration: const BoxDecoration(color: Colors.white), + child: const Center( + child: CircularProgressIndicator(), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), - child: Row( + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Column( children: [ - const Expanded(child: MobileEditProfileButton()), - Expanded(child: LogOutButton()) + AboutMe( + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 30, vertical: 10), + child: Row( + children: [ + const Expanded(child: MobileEditProfileButton()), + Expanded(child: LogOutButton()) + ], + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProfileActivityTabBar( + callback: updateIndex, + ), + ), + if (currentIndex == 0) + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: ListView.builder( + padding: const EdgeInsets.all(0), + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: profileData.nodes.length, + itemBuilder: (context, index) { + return ProfileNodeCard( + profileNode: profileData.nodes.elementAt(index), + onTap: () { + context.push( + '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + }, + ); + }, + ), + ), + ), + if (currentIndex == 1) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: SizedBox( + height: 400, + child: QuestionActivity(), + ), + ), + ), ], ), + ), + ), + desktop: SingleChildScrollView( + child: SizedBox( + width: Responsive.getGenericPageWidth(context), + child: isLoading + ? Container( + decoration: const BoxDecoration(color: Colors.white), + padding: const EdgeInsets.only(top: 20), + child: const Center( + child: CircularProgressIndicator(), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProfileActivityTabBar( - callback: updateIndex, - ), - ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); - }, - ); - }, + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Column( + children: [ + AboutMe( + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: DesktopEditProfileButton(), ), - ), - ), - if (currentIndex == 1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProfileActivityTabBar( + callback: updateIndex, + ), ), - ), + if (currentIndex == 0) + CardContainer( + child: ListView.builder( + padding: const EdgeInsets.all(0), + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: profileData.nodes.length, + itemBuilder: (context, index) { + return ProfileNodeCard( + profileNode: profileData.nodes.elementAt(index), + onTap: () { + context.push( + '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + }, + ); + }, + ), + ), + if (currentIndex == 1) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: SizedBox( + height: 400, + child: QuestionActivity(), + ), + ), + ), + ], ), - ], + ), + ), + ), + ); + } + + // others profile page, will be same both on desktop and mobile + return (isBanned && userType != "admin") + ? const ErrorPage() + : PageWithAppBar( + appBar: const HomePageAppBar(), + pageColor: Colors.grey.shade200, + child: SingleChildScrollView( + child: SizedBox( + width: Responsive.getGenericPageWidth(context), + child: isLoading + ? Container( + decoration: const BoxDecoration(color: Colors.white), + padding: const EdgeInsets.only(top: 20), + child: const Center( + child: CircularProgressIndicator(), ), - ), - ), - desktop: SingleChildScrollView( - child: SizedBox( - width: Responsive.getGenericPageWidth(context), - child: isLoading - ? Container( - decoration: const BoxDecoration(color: Colors.white), - padding: const EdgeInsets.only(top: 20), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Column( - children: [ - AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: DesktopEditProfileButton(), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProfileActivityTabBar( - callback: updateIndex, + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + AboutMe( + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProfileActivityTabBar( + callback: updateIndex, + ), ), - ), - if (currentIndex == 0) - CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + if (currentIndex == 0) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: ListView.builder( + padding: const EdgeInsets.all(0), + physics: + const NeverScrollableScrollPhysics(), // Prevents a conflict with SingleChildScrollView + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: profileData.nodes.length, + itemBuilder: (context, index) { + return ProfileNodeCard( + profileNode: profileData.nodes.elementAt(index), + onTap: () { + context.push( + '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + }, + ); }, - ); - }, + ), + ), ), - ), - if (currentIndex == 1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(), + if (currentIndex == 1) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: SizedBox( + height: 400, + child: QuestionActivity(), + ), ), ), - ), - ], - ), - ), - ), - ), - ); - } - - // others profile page, will be same both on desktop and mobile - return PageWithAppBar( - appBar: const HomePageAppBar(), - pageColor: Colors.grey.shade200, - child: SingleChildScrollView( - child: SizedBox( - width: Responsive.getGenericPageWidth(context), - child: isLoading - ? Container( - decoration: const BoxDecoration(color: Colors.white), - padding: const EdgeInsets.only(top: 20), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProfileActivityTabBar( - callback: updateIndex, + ], ), - ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - physics: - const NeverScrollableScrollPhysics(), // Prevents a conflict with SingleChildScrollView - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); - }, - ); - }, - ), - ), - ), - if (currentIndex == 1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(), - ), - ), - ), - ], - ), - ), - ), - ); + ), + ), + ); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart index 77c7e73b..c7174cc9 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart @@ -1,21 +1,40 @@ import 'package:collaborative_science_platform/utils/colors.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; +import 'package:collaborative_science_platform/widgets/app_button.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class AboutMe extends StatelessWidget { +class AboutMe extends StatefulWidget { final String email; final String name; final String surname; final int noWorks; final String aboutMe; + final bool isBanned; + final bool isReviewer; + final String userType; + final bool isValidUser; + final Function() onTap; + final Function() onTapReviewerButton; const AboutMe( {super.key, required this.email, required this.name, required this.surname, required this.noWorks, - required this.aboutMe}); + required this.aboutMe, + required this.isBanned, + required this.isReviewer, + required this.userType, + required this.isValidUser, + required this.onTap, + required this.onTapReviewerButton}); + @override + State createState() => _AboutMeState(); +} + +class _AboutMeState extends State { @override Widget build(BuildContext context) { return SizedBox( @@ -27,15 +46,87 @@ class AboutMe extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SelectableText( - "$name $surname", + "${widget.name} ${widget.surname}", style: const TextStyle( color: AppColors.primaryDarkColor, fontWeight: FontWeight.bold, fontSize: 40, ), ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Visibility( + visible: ((widget.userType == "admin" ? true : false) && widget.isValidUser), + child: widget.isReviewer + ? SizedBox( + width: 220, + child: AppButton( + text: "Give Reviewer Status", + height: 40, + icon: const Icon( + CupertinoIcons.add_circled_solid, + size: 16, + color: Colors.white, + ), + type: "safe", + onTap: widget.onTapReviewerButton, + ), + ) + : SizedBox( + width: 220, + child: AppButton( + text: "Remove Reviewer Status", + height: 40, + icon: const Icon( + CupertinoIcons.minus_circle_fill, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: widget.onTapReviewerButton, + ), + ), + ), + const SizedBox(height: 10.0), + Visibility( + visible: (widget.userType == "admin" ? true : false), + child: widget.isBanned + ? SizedBox( + width: 220, + child: AppButton( + text: "Unban User", + height: 40, + icon: const Icon( + CupertinoIcons.lock_open, + size: 16, + color: Colors.white, + ), + type: "grey", + onTap: widget.onTap, + ), + ) + : SizedBox( + width: 220, + child: AppButton( + text: "Ban User", + height: 40, + icon: const Icon( + CupertinoIcons.lock, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: widget.onTap, + ), + ), + ), + ], + ), ], ), const SizedBox( @@ -48,7 +139,7 @@ class AboutMe extends StatelessWidget { ? MediaQuery.of(context).size.width * 0.9 : MediaQuery.of(context).size.width * 0.5, child: SelectableText( - aboutMe, + widget.aboutMe, style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 20, @@ -71,7 +162,7 @@ class AboutMe extends StatelessWidget { width: 10, ), SelectableText( - email, + "${widget.email}", style: const TextStyle( fontSize: 20, ), @@ -84,7 +175,7 @@ class AboutMe extends StatelessWidget { Row( children: [ SelectableText( - "Published works: $noWorks", + "Published works: ${widget.noWorks}", style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 20, diff --git a/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart b/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart index e56ea59b..2e5151dd 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart @@ -34,9 +34,11 @@ class AppButton extends StatelessWidget { ? AppColors.secondaryColor : (type == "danger" ? AppColors.dangerColor - : (type == "safe" - ? Colors.grey[600] // const Color.fromARGB(255, 111, 221, 112) - : Colors.grey[600])))) + : (type == "grey" + ? Colors.grey[600] + : (type == "safe" + ? Color.fromARGB(255, 141, 208, 141) + : Colors.grey[600]))))) : Colors.grey[600], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), From 475211fb9c575c1d508d6ae1df7425528f29c3f0 Mon Sep 17 00:00:00 2001 From: brunettow Date: Mon, 18 Dec 2023 00:00:08 +0300 Subject: [PATCH 15/22] Updated basic user model and wrote provider for basic user API --- .../lib/models/basic_user.dart | 4 +- .../lib/providers/basic_user_provider.dart | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart b/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart index a8d635af..182479fa 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart @@ -3,12 +3,14 @@ class BasicUser { String bio; bool emailNotificationPreference; bool showActivity; + String userType; BasicUser({ required this.basicUserId, required this.bio, required this.emailNotificationPreference, required this.showActivity, + required this.userType, }); factory BasicUser.fromJson(Map jsonString) { return BasicUser( @@ -16,7 +18,7 @@ class BasicUser { bio: jsonString["bio"], emailNotificationPreference: jsonString["email_notification_preference"], showActivity: jsonString["show_activity_preference"], + userType: jsonString["user_type"], ); } - } diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart new file mode 100644 index 00000000..3f550b35 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart @@ -0,0 +1,43 @@ +//This provider is for user type + +import 'dart:convert'; +import 'package:collaborative_science_platform/exceptions/search_exceptions.dart'; +import 'package:collaborative_science_platform/utils/constants.dart'; +import 'package:collaborative_science_platform/models/basic_user.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +class BasicUserProvider with ChangeNotifier { + BasicUser? basicUser; + + Future getData(String token) async { + Uri url = Uri.parse("${Constants.apiUrl}/get_authenticated_basic_user/"); + final Map headers = { + "Authorization": token, + }; + + try { + final response = await http.get(url, headers: headers); + + if (response.statusCode == 200) { + final data = json.decode(response.body); + basicUser = BasicUser.fromJson(data); + notifyListeners(); + } else if (response.statusCode == 400) { + throw SearchError(); + } else { + throw Exception("Error"); + } + } catch (e) { + rethrow; + } + } +} + +/*{ + "basic_user_id": 92, + "bio": "Administrator", + "email_notification_preference": false, + "show_activity_preference": true, + "user_type": "admin" +}*/ \ No newline at end of file From 5431003a89fc0ac688b5c147da17715e1ca906a4 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Mon, 18 Dec 2023 02:10:36 +0300 Subject: [PATCH 16/22] answer questions bugs and refresh --- .../lib/providers/question_provider.dart | 7 +++---- .../lib/screens/node_details_page/widgets/answer_box.dart | 6 ++++-- .../node_details_page/widgets/ask_question_form.dart | 1 - .../screens/node_details_page/widgets/node_details.dart | 7 ++++--- .../screens/node_details_page/widgets/question_box.dart | 6 +++++- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart index 852b80f2..b84e840f 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/question_provider.dart @@ -72,11 +72,10 @@ class QuestionAnswerProvider with ChangeNotifier { body: json.encode(postData), ); if (response.statusCode == 201) { - final data = json.decode(response.body); - Question answeredQuestions = _questions.firstWhere((element) => element.id == questionId); + Question answeredQuestions = questions.firstWhere((element) => element.id == questionId); answeredQuestions.isAnswered = true; - answeredQuestions.answer = data['answer_content']; - answeredQuestions.answeredAt = data['answered_at']; + answeredQuestions.answer = answerText; + answeredQuestions.answeredAt = DateTime.now().toString(); answeredQuestions.answerer = user; notifyListeners(); } else if (response.statusCode == 403) { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart index 475729fb..e9477ad9 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/answer_box.dart @@ -7,8 +7,9 @@ import 'package:provider/provider.dart'; class AnswerBox extends StatefulWidget { final int questionId; - - const AnswerBox({Key? key, required this.questionId}) : super(key: key); + final Function() onQuestionAnswered; + const AnswerBox({Key? key, required this.questionId, required this.onQuestionAnswered}) + : super(key: key); @override State createState() => _AnswerBoxState(); @@ -29,6 +30,7 @@ class _AnswerBoxState extends State { isLoading = true; }); await questionAnswerProvider.postAnswer(answerController.text, widget.questionId, user); + widget.onQuestionAnswered(); answerController.clear(); } } on PostAnswerError { diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart index 0dcec692..d93213c5 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/ask_question_form.dart @@ -1,6 +1,5 @@ import 'package:collaborative_science_platform/exceptions/question_exceptions.dart'; import 'package:collaborative_science_platform/models/node_details_page/question.dart'; -import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/question_provider.dart'; import 'package:flutter/material.dart'; diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index c189074a..e6e95ffd 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -61,9 +61,10 @@ class _NodeDetailsState extends State { void canAnswer() async { Auth authProvider = Provider.of(context, listen: false); setState(() { - canAnswerQuestions = authProvider.isSignedIn - ? widget.node.contributors.any((contributor) => contributor.id == authProvider.user!.id) - : false; + canAnswerQuestions = authProvider.isSignedIn && + authProvider.basicUser != null && + widget.node.contributors + .any((contributor) => contributor.id == authProvider.basicUser!.basicUserId); canAskQuestions = authProvider.isSignedIn; }); } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart index 41ce9ecb..5db60c62 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/question_box.dart @@ -59,7 +59,11 @@ class _QuestionBoxState extends State { ), ), ), - if (isReplyVisible) AnswerBox(questionId: widget.question.id), + if (isReplyVisible) + AnswerBox( + questionId: widget.question.id, + onQuestionAnswered: () => setState(() {}), + ), ], ), ], From 556d262cfe4910fb8463253127bc4db29a31786d Mon Sep 17 00:00:00 2001 From: brunettow Date: Mon, 18 Dec 2023 18:34:14 +0300 Subject: [PATCH 17/22] usertype connected to backend --- .../lib/models/basic_user.dart | 10 ++-- .../lib/providers/basic_user_provider.dart | 43 ----------------- .../screens/profile_page/profile_page.dart | 47 ++++++++++++++----- 3 files changed, 41 insertions(+), 59 deletions(-) delete mode 100644 project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart b/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart index 182479fa..a98af6e0 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart @@ -6,11 +6,11 @@ class BasicUser { String userType; BasicUser({ - required this.basicUserId, - required this.bio, - required this.emailNotificationPreference, - required this.showActivity, - required this.userType, + this.basicUserId = 0, + this.bio = "", + this.emailNotificationPreference = true, + this.showActivity = true, + this.userType = "", }); factory BasicUser.fromJson(Map jsonString) { return BasicUser( diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart deleted file mode 100644 index 3f550b35..00000000 --- a/project/FrontEnd/collaborative_science_platform/lib/providers/basic_user_provider.dart +++ /dev/null @@ -1,43 +0,0 @@ -//This provider is for user type - -import 'dart:convert'; -import 'package:collaborative_science_platform/exceptions/search_exceptions.dart'; -import 'package:collaborative_science_platform/utils/constants.dart'; -import 'package:collaborative_science_platform/models/basic_user.dart'; -import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; - -class BasicUserProvider with ChangeNotifier { - BasicUser? basicUser; - - Future getData(String token) async { - Uri url = Uri.parse("${Constants.apiUrl}/get_authenticated_basic_user/"); - final Map headers = { - "Authorization": token, - }; - - try { - final response = await http.get(url, headers: headers); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - basicUser = BasicUser.fromJson(data); - notifyListeners(); - } else if (response.statusCode == 400) { - throw SearchError(); - } else { - throw Exception("Error"); - } - } catch (e) { - rethrow; - } - } -} - -/*{ - "basic_user_id": 92, - "bio": "Administrator", - "email_notification_preference": false, - "show_activity_preference": true, - "user_type": "admin" -}*/ \ No newline at end of file diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index 767d88d9..cad0ffe7 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -1,4 +1,5 @@ import 'package:collaborative_science_platform/exceptions/profile_page_exceptions.dart'; +import 'package:collaborative_science_platform/models/basic_user.dart'; import 'package:collaborative_science_platform/models/profile_data.dart'; import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; @@ -22,7 +23,7 @@ import 'package:go_router/go_router.dart'; class ProfilePage extends StatefulWidget { static const routeName = '/profile'; - final String email; + final String email; //visited page's email const ProfilePage({super.key, required this.email}); @@ -33,15 +34,17 @@ class ProfilePage extends StatefulWidget { // TODO: add optional parameter to ProfilePage to get others profileData class _ProfilePageState extends State { ProfileData profileData = ProfileData(); + BasicUser basicUser = BasicUser(); + //User user = User(); int noWorks = 0; bool error = false; String errorMessage = ""; bool isLoading = false; + bool isAuthLoading = false; bool _isFirstTime = true; bool isBanned = false; - String userType = "admin"; //visitor's user type bool isValidUser = false; //visited user's type is contributor or reviewer bool isReviewer = false; @@ -57,6 +60,7 @@ class _ProfilePageState extends State { void didChangeDependencies() { if (_isFirstTime) { try { + getAuthUser(); getUserData(); } catch (e) { setState(() { @@ -69,6 +73,8 @@ class _ProfilePageState extends State { super.didChangeDependencies(); } + //Visited User's Data + void getUserData() async { try { if (widget.email != "") { @@ -107,6 +113,25 @@ class _ProfilePageState extends State { } } + void getAuthUser() async { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } + } + void handleButtonIsBanned() { setState(() { isBanned = !isBanned; // Toggle the state for example purposes @@ -123,10 +148,10 @@ class _ProfilePageState extends State { Widget build(BuildContext context) { final User? user = Provider.of(context).user; if (user == null) { - // guest can see profile pages + // TODO guest can see profile pages } else if (user.email == profileData.email) { - // own profile page, should be editible - return (isBanned && userType != "admin") + // TODO own profile page, should be editible + return (isBanned && basicUser.userType != "admin") ? const ErrorPage() : PageWithAppBar( appBar: const HomePageAppBar(), @@ -135,7 +160,7 @@ class _ProfilePageState extends State { mobile: SingleChildScrollView( child: SizedBox( width: Responsive.getGenericPageWidth(context), - child: isLoading + child: isLoading && isAuthLoading ? Container( decoration: const BoxDecoration(color: Colors.white), child: const Center( @@ -159,7 +184,7 @@ class _ProfilePageState extends State { isBanned: isBanned, isReviewer: isReviewer, isValidUser: isValidUser, - userType: userType, + userType: basicUser.userType, onTap: handleButtonIsBanned, onTapReviewerButton: handleButtonIsReviewer), Padding( @@ -243,7 +268,7 @@ class _ProfilePageState extends State { isBanned: isBanned, isReviewer: isReviewer, isValidUser: isValidUser, - userType: userType, + userType: basicUser.userType, onTap: handleButtonIsBanned, onTapReviewerButton: handleButtonIsReviewer), const Padding( @@ -294,7 +319,7 @@ class _ProfilePageState extends State { } // others profile page, will be same both on desktop and mobile - return (isBanned && userType != "admin") + return (isBanned && basicUser.userType != "admin") ? const ErrorPage() : PageWithAppBar( appBar: const HomePageAppBar(), @@ -302,7 +327,7 @@ class _ProfilePageState extends State { child: SingleChildScrollView( child: SizedBox( width: Responsive.getGenericPageWidth(context), - child: isLoading + child: isLoading && isAuthLoading ? Container( decoration: const BoxDecoration(color: Colors.white), padding: const EdgeInsets.only(top: 20), @@ -328,7 +353,7 @@ class _ProfilePageState extends State { isBanned: isBanned, isReviewer: isReviewer, isValidUser: isValidUser, - userType: userType, + userType: basicUser.userType, onTap: handleButtonIsBanned, onTapReviewerButton: handleButtonIsReviewer), Padding( From e7ee8d71c0968abc2b26cbfdaf28614a18bc76e2 Mon Sep 17 00:00:00 2001 From: Omer <56688420+omerfaunal@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:45:01 +0300 Subject: [PATCH 18/22] Make texts unselectable (#633) --- .../widgets/please_login_signup.dart | 28 +++++++++-------- .../widgets/node_details_nav_bar_item.dart | 30 ++++++++++--------- .../widgets/top_navigation_bar.dart | 30 ++++++++++--------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/please_login_signup.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/please_login_signup.dart index 51f81617..774b7ed5 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/please_login_signup.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/auth_screens/widgets/please_login_signup.dart @@ -26,12 +26,14 @@ class PleaseLoginSignup extends StatelessWidget { child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'Login', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16.0, + SelectionContainer.disabled( + child: Text( + 'Login', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), ), ), ], @@ -56,12 +58,14 @@ class PleaseLoginSignup extends StatelessWidget { child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'Signup', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16.0, + SelectionContainer.disabled( + child: Text( + 'Signup', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), ), ), ], diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details_nav_bar_item.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details_nav_bar_item.dart index 948e901e..6582336c 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details_nav_bar_item.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details_nav_bar_item.dart @@ -52,20 +52,22 @@ class _NavigationBarItemState extends State { if (!Responsive.isMobile(context)) Padding( padding: const EdgeInsets.only(top: 4.0), - child: Text( - widget.text, - style: TextStyle( - fontSize: 16, - fontWeight: widget.isSelected - ? FontWeight.w700 - : isHovering - ? FontWeight.w600 - : FontWeight.w500, - color: widget.isSelected - ? AppColors.secondaryColor - : isHovering - ? Colors.indigo[200] - : Colors.grey[700], + child: SelectionContainer.disabled( + child: Text( + widget.text, + style: TextStyle( + fontSize: 16, + fontWeight: widget.isSelected + ? FontWeight.w700 + : isHovering + ? FontWeight.w600 + : FontWeight.w500, + color: widget.isSelected + ? AppColors.secondaryColor + : isHovering + ? Colors.indigo[200] + : Colors.grey[700], + ), ), ), ), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/page_with_appbar/widgets/top_navigation_bar.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/page_with_appbar/widgets/top_navigation_bar.dart index 0907a802..ff5d1951 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/page_with_appbar/widgets/top_navigation_bar.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/page_with_appbar/widgets/top_navigation_bar.dart @@ -119,20 +119,22 @@ class _NavigationBarItemState extends State { if (!Responsive.isMobile(context)) Padding( padding: const EdgeInsets.only(top: 4.0), - child: Text( - widget.text, - style: TextStyle( - fontSize: 16, - fontWeight: widget.isSelected - ? FontWeight.w700 - : isHovering - ? FontWeight.w600 - : FontWeight.w500, - color: widget.isSelected - ? Colors.indigo[600] - : isHovering - ? Colors.indigo[200] - : Colors.grey[700], + child: SelectionContainer.disabled( + child: Text( + widget.text, + style: TextStyle( + fontSize: 16, + fontWeight: widget.isSelected + ? FontWeight.w700 + : isHovering + ? FontWeight.w600 + : FontWeight.w500, + color: widget.isSelected + ? Colors.indigo[600] + : isHovering + ? Colors.indigo[200] + : Colors.grey[700], + ), ), ), ), From 4d0128940977e64c3408b78b13045432e0e22349 Mon Sep 17 00:00:00 2001 From: brunettow Date: Mon, 18 Dec 2023 22:50:03 +0300 Subject: [PATCH 19/22] bug fix on guest user on profile view --- .../screens/profile_page/profile_page.dart | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index cad0ffe7..ea822597 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -114,21 +114,24 @@ class _ProfilePageState extends State { } void getAuthUser() async { - try { - final auth = Provider.of(context); - basicUser = (auth.basicUser ?? {} as BasicUser); - isAuthLoading = true; - // user = (auth.user ?? {} as User); - } catch (e) { - setState(() { - error = true; - errorMessage = "Something went wrong!"; - }); - rethrow; - } finally { - setState(() { - isAuthLoading = false; - }); + final User? user = Provider.of(context).user; + if (user != null) { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } } } @@ -148,12 +151,11 @@ class _ProfilePageState extends State { Widget build(BuildContext context) { final User? user = Provider.of(context).user; if (user == null) { - // TODO guest can see profile pages + // guest can see profile pages } else if (user.email == profileData.email) { - // TODO own profile page, should be editible - return (isBanned && basicUser.userType != "admin") - ? const ErrorPage() - : PageWithAppBar( + // profile page, should be editible + return (!isBanned || basicUser.userType == "admin") + ? PageWithAppBar( appBar: const HomePageAppBar(), pageColor: Colors.grey.shade200, child: Responsive( @@ -315,13 +317,13 @@ class _ProfilePageState extends State { ), ), ), - ); + ) + : const ErrorPage(); } // others profile page, will be same both on desktop and mobile - return (isBanned && basicUser.userType != "admin") - ? const ErrorPage() - : PageWithAppBar( + return (!isBanned || basicUser.userType == "admin") + ? PageWithAppBar( appBar: const HomePageAppBar(), pageColor: Colors.grey.shade200, child: SingleChildScrollView( @@ -399,6 +401,7 @@ class _ProfilePageState extends State { ), ), ), - ); + ) + : const ErrorPage(); } } From 16800aa90ebe7cb849dcad5fee5cbc1e9863c8c5 Mon Sep 17 00:00:00 2001 From: defabdullah Date: Mon, 18 Dec 2023 23:41:29 +0300 Subject: [PATCH 20/22] show unanswered questions to only contributors --- .../node_details_page/widgets/questions_list_view.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart index 59d770db..0f270ebe 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/questions_list_view.dart @@ -34,6 +34,10 @@ class _QuestionsViewState extends State { @override Widget build(BuildContext context) { + List filteredQuestions = questions.where((question) { + return question.isAnswered || widget.canAnswer; + }).toList(); + return SingleChildScrollView( child: Container( width: Responsive.desktopPageWidth, @@ -54,10 +58,10 @@ class _QuestionsViewState extends State { Flexible( child: ListView.builder( shrinkWrap: true, - itemCount: questions.length, + itemCount: filteredQuestions.length, itemBuilder: (BuildContext context, int index) { return QuestionBox( - question: questions[index], + question: filteredQuestions[index], canAnswer: widget.canAnswer, ); }, From 8056c9bc49a3e3e74d14d7d6aa3073608ac26ae6 Mon Sep 17 00:00:00 2001 From: brunettow Date: Tue, 19 Dec 2023 03:02:22 +0300 Subject: [PATCH 21/22] implemented admin provider, in process connecting node details to backend --- .../lib/providers/admin_provider.dart | 132 +++++++ .../node_details_page/node_details_page.dart | 202 ++++++++--- .../widgets/node_details.dart | 343 +++++++++--------- 3 files changed, 453 insertions(+), 224 deletions(-) create mode 100644 project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart new file mode 100644 index 00000000..002c7281 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart @@ -0,0 +1,132 @@ +import 'dart:convert'; +import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/models/user.dart'; +import 'package:collaborative_science_platform/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +class AdminProvider with ChangeNotifier { + Future banUser(User? user, User? admin, bool isBanned) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.put( + Uri.parse("${Constants.apiUrl}/update_content_status/"), + headers: header, + body: jsonEncode( + { + 'context': "user", + 'content_id': user!.id.toString(), + 'hide': isBanned.toString() + }, + ), + ); + print(response.statusCode); + return response.statusCode; + } catch (e) { + rethrow; + } + } + + Future hideNode(User? admin, NodeDetailed node, bool isHidden) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.put( + Uri.parse("${Constants.apiUrl}/update_content_status/"), + headers: header, + body: jsonEncode( + { + 'context': "node", + 'content_id': node.nodeId.toString(), + 'hide': isHidden.toString() + }, + ), + ); + print(response.statusCode); + } catch (e) { + rethrow; + } + } + + Future hideQuestion(User? admin, Question question, bool isHidden) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.put( + Uri.parse("${Constants.apiUrl}/update_content_status/"), + headers: header, + body: jsonEncode( + { + 'context': "question", + 'content_id': "-1", // TODO question!.id.toString(). + 'hide': isHidden.toString() + }, + ), + ); + print(response.statusCode); + return response.statusCode; + } catch (e) { + rethrow; + } + } + + Future promoteUser(User? user, User? admin) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.post( + Uri.parse("${Constants.apiUrl}/promote_contributor/"), + headers: header, + body: jsonEncode( + { + 'cont_id': user!.id.toString(), //The basic user id of the contributor + }, + ), + ); + print(response.statusCode); + } catch (e) { + rethrow; + } + } + + Future demoteUser(User? user, User? admin) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.delete( + Uri.parse("${Constants.apiUrl}/demote_reviewer/?${user!.id.toString()}"), + headers: header, + body: jsonEncode( + { + 'reviewer_id': user.id.toString(), //The basic user id of the reviewer + }, + ), + ); + print(response.statusCode); + } catch (e) { + rethrow; + } + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart index a8a6e10d..a24f98fb 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart @@ -1,6 +1,11 @@ import 'package:collaborative_science_platform/exceptions/node_details_exceptions.dart'; +import 'package:collaborative_science_platform/models/basic_user.dart'; import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; +import 'package:collaborative_science_platform/models/user.dart'; +import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/node_provider.dart'; +import 'package:collaborative_science_platform/providers/admin_provider.dart'; +import 'package:collaborative_science_platform/screens/error_page/error_page.dart'; import 'package:collaborative_science_platform/screens/home_page/widgets/home_page_appbar.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/contributors_list_view.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/node_details.dart'; @@ -26,15 +31,21 @@ class _NodeDetailsPageState extends State { ScrollController controller2 = ScrollController(); bool _isFirstTime = true; NodeDetailed node = NodeDetailed(); + BasicUser basicUser = BasicUser(); + + bool isHidden = false; + bool isAuthLoading = false; bool error = false; String errorMessage = ""; + String message = ""; bool isLoading = false; @override void didChangeDependencies() { if (_isFirstTime) { + getAuthUser(); getNodeDetails(); _isFirstTime = false; } @@ -70,32 +81,83 @@ class _NodeDetailsPageState extends State { } } + void getAuthUser() async { + final User? user = Provider.of(context).user; + if (user != null) { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } + } + } + + void changeNodeStatus() async { + try { + final User? user = Provider.of(context, listen: false).user; + final adminProvider = Provider.of(context, listen: false); + await adminProvider.hideNode(user, node, isHidden); + error = false; + message = "Node status updated."; + print(message); + } catch (e) { + setState(() { + error = true; + message = "Something went wrong!"; + print(message); + }); + } + } + + void handleButton() { + setState(() { + isHidden = !isHidden; // Toggle the state for example purposes + }); + // changeNodeStatus(); + } + @override Widget build(BuildContext context) { - return PageWithAppBar( - appBar: const HomePageAppBar(), - pageColor: Colors.grey.shade200, - child: isLoading - ? Container( - padding: const EdgeInsets.only(top: 32), - decoration: const BoxDecoration(color: Colors.white), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Responsive.isDesktop(context) - ? WebNodeDetails(node: node) - : NodeDetails( - node: node, - controller: controller2, + return (!isHidden || basicUser.userType == "admin") + ? PageWithAppBar( + appBar: const HomePageAppBar(), + pageColor: Colors.grey.shade200, + child: isLoading + ? Container( + padding: const EdgeInsets.only(top: 32), + decoration: const BoxDecoration(color: Colors.white), + child: const Center( + child: CircularProgressIndicator(), ), - ); + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Responsive.isDesktop(context) + ? WebNodeDetails(node: node) + : NodeDetails( + node: node, + controller: controller2, + isHidden: isHidden, + userType: basicUser.userType, + onTap: handleButton, + ), + ) + : const ErrorPage(); } } @@ -111,13 +173,20 @@ class WebNodeDetails extends StatefulWidget { class _WebNodeDetailsState extends State { final ScrollController controller1 = ScrollController(); final ScrollController controller2 = ScrollController(); + BasicUser basicUser = BasicUser(); + bool _isFirstTime = true; bool error = false; bool isLoading = false; + bool isAuthLoading = false; + bool isHidden = false; + + String errorMessage = ""; @override void didChangeDependencies() { if (_isFirstTime) { + getAuthUser(); getNodeSuggestions(); _isFirstTime = false; } @@ -147,6 +216,28 @@ class _WebNodeDetailsState extends State { } } + void getAuthUser() async { + final User? user = Provider.of(context).user; + if (user != null) { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } + } + } + @override void dispose() { controller1.dispose(); @@ -161,35 +252,46 @@ class _WebNodeDetailsState extends State { super.initState(); } + void handleButton() { + setState(() { + isHidden = !isHidden; // Toggle the state for example purposes + }); + } + @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Contributors( - contributors: widget.node.contributors, //widget.inputNode.contributors, - controller: controller1, - ), - const SizedBox(width: 12), - Flexible( - child: NodeDetails( - node: widget.node, - controller: controller2, - ), - ), - const SizedBox(width: 12), - SizedBox( - height: MediaQuery.of(context).size.height - 100, - child: YouMayLike( - isLoading: isLoading, - error: error, + return (!isHidden || basicUser.userType == "admin") + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Contributors( + contributors: widget.node.contributors, //widget.inputNode.contributors, + controller: controller1, + ), + const SizedBox(width: 12), + Flexible( + child: NodeDetails( + node: widget.node, + controller: controller2, + isHidden: isHidden, + userType: basicUser.userType, + onTap: handleButton, + ), + ), + const SizedBox(width: 12), + SizedBox( + height: MediaQuery.of(context).size.height - 100, + child: YouMayLike( + isLoading: isLoading, + error: error, + ), + ), + ], ), - ), - ], - ), - ); + ) + : const ErrorPage(); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index ae4fd08f..dc70972a 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -22,10 +22,16 @@ import 'dart:convert'; class NodeDetails extends StatefulWidget { final NodeDetailed node; final ScrollController controller; + final bool isHidden; + final String userType; + final Function() onTap; const NodeDetails({ super.key, required this.node, required this.controller, + required this.isHidden, + required this.userType, + required this.onTap, }); @override @@ -34,8 +40,6 @@ class NodeDetails extends StatefulWidget { class _NodeDetailsState extends State { int currentIndex = 0; - bool isHidden = true; - String userType = "admin"; void updateIndex(int index) { setState(() { @@ -45,191 +49,182 @@ class _NodeDetailsState extends State { @override Widget build(BuildContext context) { - return (isHidden && userType != "admin") - ? const ErrorPage() - : Container( - decoration: BoxDecoration( - color: Colors.grey[200], - ), - width: Responsive.isDesktop(context) - ? Responsive.desktopPageWidth * 0.8 - : Responsive.getGenericPageWidth(context), - height: MediaQuery.of(context).size.height - 60, - child: SingleChildScrollView( - primary: false, - scrollDirection: Axis.vertical, - child: Column( - // mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: Column( - //mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: Responsive.isDesktop(context) - ? const EdgeInsets.all(70.0) - : const EdgeInsets.all(10.0), - child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), - textAlign: TextAlign.center, style: TextStyles.title2)), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SelectableText.rich( - TextSpan( - text: widget.node.publishDateFormatted, - style: TextStyles.bodyBlack, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Visibility( - visible: userType == "admin" ? true : false, - child: isHidden - ? SizedBox( - width: 110, - child: AppButton( - text: "Show", - height: 40, - icon: const Icon( - CupertinoIcons.eye, - size: 16, - color: Colors.white, - ), - type: "grey", - onTap: () { - setState(() { - isHidden = false; - }); - }), - ) - : SizedBox( - width: 110, - child: AppButton( - text: "Hide", - height: 40, - icon: const Icon( - CupertinoIcons.eye_slash, - size: 16, - color: Colors.white, - ), - type: "danger", - onTap: () { - setState(() { - isHidden = true; - }); - }), - ), - ), - const SizedBox(width: 10), - SizedBox( - width: 110, - child: AppButton( - text: "Graph", + return Container( + decoration: BoxDecoration( + color: Colors.grey[200], + ), + width: Responsive.isDesktop(context) + ? Responsive.desktopPageWidth * 0.8 + : Responsive.getGenericPageWidth(context), + height: MediaQuery.of(context).size.height - 60, + child: SingleChildScrollView( + primary: false, + scrollDirection: Axis.vertical, + child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: Column( + //mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: Responsive.isDesktop(context) + ? const EdgeInsets.all(70.0) + : const EdgeInsets.all(10.0), + child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), + textAlign: TextAlign.center, style: TextStyles.title2)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SelectableText.rich( + TextSpan( + text: widget.node.publishDateFormatted, + style: TextStyles.bodyBlack, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Visibility( + visible: widget.userType == "admin" ? true : false, + child: widget.isHidden + ? SizedBox( + width: 110, + child: AppButton( + text: "Show", height: 40, icon: const Icon( - CupertinoIcons.square_grid_3x2, + CupertinoIcons.eye, size: 16, color: Colors.white, ), - type: "secondary", - onTap: () { - context - .push('${GraphPage.routeName}/${widget.node.nodeId}'); - }), - ), - const SizedBox(width: 10), - SizedBox( - width: 110, - child: AppButton( - text: "Share", - icon: const Icon( - Icons.share, - size: 16, - color: Colors.white, + type: "grey", + onTap: widget.onTap, + ), + ) + : SizedBox( + width: 110, + child: AppButton( + text: "Hide", + height: 40, + icon: const Icon( + CupertinoIcons.eye_slash, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: widget.onTap, ), - height: 40, - type: "primary", - onTap: () => SharePage.shareNodeView(widget.node), ), + ), + const SizedBox(width: 10), + SizedBox( + width: 110, + child: AppButton( + text: "Graph", + height: 40, + icon: const Icon( + CupertinoIcons.square_grid_3x2, + size: 16, + color: Colors.white, ), - ], - ) - ], - ), - ], - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: NodeDetailsTabBar( - callback: updateIndex, - ), - ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: Container( - width: Responsive.desktopPageWidth, - decoration: BoxDecoration(color: Colors.grey[200]), - child: CardContainer( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: TeXView( - renderingEngine: const TeXViewRenderingEngine.katex(), - child: TeXViewDocument( - NodeHelper.getNodeContentLatex(widget.node, "long")))), - SelectableText.rich( - textAlign: TextAlign.start, - TextSpan( - text: widget.node.publishDateFormatted, - style: TextStyles.bodyBlack, + type: "secondary", + onTap: () { + context.push('${GraphPage.routeName}/${widget.node.nodeId}'); + }), + ), + const SizedBox(width: 10), + SizedBox( + width: 110, + child: AppButton( + text: "Share", + icon: const Icon( + Icons.share, + size: 16, + color: Colors.white, ), + height: 40, + type: "primary", + onTap: () => SharePage.shareNodeView(widget.node), ), - ], - )), - )), - if (currentIndex == 1) - //proofs - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProofListView(proof: widget.node.proof), + ), + ], + ) + ], ), - if (currentIndex == 2) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ReferencesView(nodes: widget.node.references, ref: true), - ), - if (currentIndex == 3) - //citations - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ReferencesView(nodes: widget.node.citations), - ), - if (currentIndex == 4) - //Q/A - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: QuestionsView(questions: widget.node.questions), - ), - if (currentIndex == 5) - //contributors - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: Contributors( - contributors: widget.node.contributors, controller: widget.controller)), - ], + ], + ), ), ), - ); + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: NodeDetailsTabBar( + callback: updateIndex, + ), + ), + if (currentIndex == 0) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: Container( + width: Responsive.desktopPageWidth, + decoration: BoxDecoration(color: Colors.grey[200]), + child: CardContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: TeXView( + renderingEngine: const TeXViewRenderingEngine.katex(), + child: TeXViewDocument( + NodeHelper.getNodeContentLatex(widget.node, "long")))), + SelectableText.rich( + textAlign: TextAlign.start, + TextSpan( + text: widget.node.publishDateFormatted, + style: TextStyles.bodyBlack, + ), + ), + ], + )), + )), + if (currentIndex == 1) + //proofs + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProofListView(proof: widget.node.proof), + ), + if (currentIndex == 2) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ReferencesView(nodes: widget.node.references, ref: true), + ), + if (currentIndex == 3) + //citations + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ReferencesView(nodes: widget.node.citations), + ), + if (currentIndex == 4) + //Q/A + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: QuestionsView(questions: widget.node.questions), + ), + if (currentIndex == 5) + //contributors + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: Contributors( + contributors: widget.node.contributors, controller: widget.controller)), + ], + ), + ), + ); } } From ab63d64e5b15bbd12aa4598749ac974d807194cc Mon Sep 17 00:00:00 2001 From: brunettow Date: Tue, 19 Dec 2023 12:38:57 +0300 Subject: [PATCH 22/22] resolved new conflicts with frontend branch --- .../widgets/node_details.dart | 14 --- .../screens/profile_page/profile_page.dart | 87 ++++++------------- 2 files changed, 26 insertions(+), 75 deletions(-) diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index aa5a8b9e..a68e4fa1 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -98,7 +98,6 @@ class _NodeDetailsState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: CardContainer( -<<<<<<< HEAD child: Column( //mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -111,19 +110,6 @@ class _NodeDetailsState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, -======= - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: Responsive.isDesktop(context) - ? const EdgeInsets.all(70.0) - : const EdgeInsets.all(10.0), - child: SelectableText(utf8.decode(widget.node.nodeTitle.codeUnits), - textAlign: TextAlign.center, style: TextStyles.title2)), - Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( ->>>>>>> 9adec56d31f6df25629a21d43e29179780a01543 children: [ SelectableText.rich( TextSpan( diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index d2735e66..f3170ca7 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -1,6 +1,5 @@ import 'package:collaborative_science_platform/exceptions/profile_page_exceptions.dart'; import 'package:collaborative_science_platform/models/basic_user.dart'; -import 'package:collaborative_science_platform/models/node_details_page/question.dart'; import 'package:collaborative_science_platform/models/profile_data.dart'; import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; @@ -24,7 +23,7 @@ import 'package:go_router/go_router.dart'; class ProfilePage extends StatefulWidget { static const routeName = '/profile'; - final String email; //visited page's email + final String email; const ProfilePage({super.key, required this.email}); @@ -36,7 +35,6 @@ class ProfilePage extends StatefulWidget { class _ProfilePageState extends State { ProfileData profileData = ProfileData(); BasicUser basicUser = BasicUser(); - //User user = User(); int noWorks = 0; bool error = false; String errorMessage = ""; @@ -74,8 +72,6 @@ class _ProfilePageState extends State { super.didChangeDependencies(); } - //Visited User's Data - void getUserData() async { try { if (widget.email != "") { @@ -157,7 +153,7 @@ class _ProfilePageState extends State { if (user == null) { // guest can see profile pages } else if (user.email == profileData.email) { - // profile page, should be editible + // own profile page, should be editible return (!isBanned || basicUser.userType == "admin") ? PageWithAppBar( appBar: const HomePageAppBar(), @@ -182,17 +178,18 @@ class _ProfilePageState extends State { : Column( children: [ AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, - isBanned: isBanned, - isReviewer: isReviewer, - isValidUser: isValidUser, - userType: basicUser.userType, - onTap: handleButtonIsBanned, - onTapReviewerButton: handleButtonIsReviewer), + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: basicUser.userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer, + ), Padding( padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), @@ -267,17 +264,18 @@ class _ProfilePageState extends State { : Column( children: [ AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, - isBanned: isBanned, - isReviewer: isReviewer, - isValidUser: isValidUser, - userType: basicUser.userType, - onTap: handleButtonIsBanned, - onTapReviewerButton: handleButtonIsReviewer), + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: basicUser.userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer, + ), const Padding( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: DesktopEditProfileButton(), @@ -403,39 +401,6 @@ class _ProfilePageState extends State { ), ), ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - physics: - const NeverScrollableScrollPhysics(), // Prevents a conflict with SingleChildScrollView - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); - }, - ); - }, - ), - ), - ), - if (currentIndex == 1) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(questions: questionList), - ), - ), - ), ], ), ),