From 7d4860978160264ca2596341ad0ff710a69591a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Sun, 5 Nov 2023 12:45:39 +0100 Subject: [PATCH 1/6] First Draft of Feedback Form --- .gitignore | 2 +- .../services/feedback_service.dart | 11 +++ .../viewModels/feedback_viewmodel.dart | 28 ++++++++ .../views/feedback_checkmark_view.dart | 27 ++++++++ .../views/feedback_form_view.dart | 67 +++++++++++++++++++ .../views/feedback_textfield.dart | 28 ++++++++ lib/navigation_service.dart | 15 +++-- lib/providers_get_it.dart | 4 ++ .../views/settings_scaffold.dart | 29 ++++++++ .../views/settings_view.dart | 20 +++--- 10 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 lib/feedbackComponent/services/feedback_service.dart create mode 100644 lib/feedbackComponent/viewModels/feedback_viewmodel.dart create mode 100644 lib/feedbackComponent/views/feedback_checkmark_view.dart create mode 100644 lib/feedbackComponent/views/feedback_form_view.dart create mode 100644 lib/feedbackComponent/views/feedback_textfield.dart create mode 100644 lib/settingsComponent/views/settings_scaffold.dart diff --git a/.gitignore b/.gitignore index 689d3fa6..1233cc24 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ migrate_working_dir/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. -#.vscode/ +.vscode/ # Flutter/Dart/Pub related **/doc/api/ diff --git a/lib/feedbackComponent/services/feedback_service.dart b/lib/feedbackComponent/services/feedback_service.dart new file mode 100644 index 00000000..96fc8c4e --- /dev/null +++ b/lib/feedbackComponent/services/feedback_service.dart @@ -0,0 +1,11 @@ +import 'package:campus_flutter/base/networking/apis/tumdev/cached_client.dart'; +import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pbgrpc.dart'; +import 'package:campus_flutter/providers_get_it.dart'; + +class FeedbackService { + Future sendFeedback( + CreateFeedbackRequest createFeedbackRequest) async { + CachedCampusClient mainApi = getIt(); + return await mainApi.createFeedback(Stream.value(createFeedbackRequest)); + } +} diff --git a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart new file mode 100644 index 00000000..fbab0f56 --- /dev/null +++ b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart @@ -0,0 +1,28 @@ +import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pb.dart'; +import 'package:campus_flutter/base/services/location_service.dart'; +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:rxdart/rxdart.dart'; + +class FeedbackViewModel { + BehaviorSubject shareLocation = BehaviorSubject.seeded(false); + BehaviorSubject shareDeviceInfos = BehaviorSubject.seeded(false); + final TextEditingController emailAddress = TextEditingController(); + final TextEditingController message = TextEditingController(); + + FeedbackViewModel(); + + Future sendFeedBack() async { + Position? position; + if (shareLocation.value) { + position = await LocationService.getLastKnown(); + } + + final feedback = CreateFeedbackRequest( + recipient: CreateFeedbackRequest_Recipient.TUM_DEV, + fromEmail: emailAddress.text, + message: message.text, + location: Coordinate( + latitude: position?.latitude, longitude: position?.longitude)); + } +} diff --git a/lib/feedbackComponent/views/feedback_checkmark_view.dart b/lib/feedbackComponent/views/feedback_checkmark_view.dart new file mode 100644 index 00000000..34b1fa77 --- /dev/null +++ b/lib/feedbackComponent/views/feedback_checkmark_view.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:rxdart/rxdart.dart'; + +class FeedbackCheckMarkView extends ConsumerWidget { + const FeedbackCheckMarkView( + {super.key, required this.text, required this.isChecked}); + + final String text; + final BehaviorSubject isChecked; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return StreamBuilder( + stream: isChecked, + builder: (context, snapshot) { + return ListTile( + dense: true, + title: Text(text), + trailing: Checkbox( + value: snapshot.data ?? false, + onChanged: (newValue) => isChecked.add(newValue ?? false), + ), + ); + }); + } +} diff --git a/lib/feedbackComponent/views/feedback_form_view.dart b/lib/feedbackComponent/views/feedback_form_view.dart new file mode 100644 index 00000000..f1dc0658 --- /dev/null +++ b/lib/feedbackComponent/views/feedback_form_view.dart @@ -0,0 +1,67 @@ +import 'package:campus_flutter/base/views/seperated_list.dart'; +import 'package:campus_flutter/feedbackComponent/viewModels/feedback_viewmodel.dart'; +import 'package:campus_flutter/feedbackComponent/views/feedback_checkmark_view.dart'; +import 'package:campus_flutter/feedbackComponent/views/feedback_textfield.dart'; +import 'package:campus_flutter/providers_get_it.dart'; +import 'package:campus_flutter/theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class FeedbackFormScaffold extends StatelessWidget { + const FeedbackFormScaffold({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: const BackButton(), + title: const Text("Submit Feedback"), + ), + body: const FeedbackFormView(), + ); + } +} + +class FeedbackFormView extends ConsumerWidget { + const FeedbackFormView({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Form( + child: Column( + children: [ + FeedbackTextField( + title: "Your Email Address", + textEditingController: ref.read(feedbackViewModel).emailAddress, + ), + FeedbackTextField( + title: "Message", + textEditingController: ref.read(feedbackViewModel).message, + expanded: true, + ), + Card( + child: SeparatedList.widgets( + widgets: [ + FeedbackCheckMarkView( + text: "Share your location", + isChecked: ref.read(feedbackViewModel).shareLocation, + ), + FeedbackCheckMarkView( + text: "Share Device's Information", + isChecked: ref.read(feedbackViewModel).shareDeviceInfos, + ), + ], + ), + ), + Padding( + padding: EdgeInsets.all(context.padding), + child: ElevatedButton( + onPressed: () => ref.read(feedbackViewModel).sendFeedBack(), + child: const Text("Submit"), + ), + ), + ], + ), + ); + } +} diff --git a/lib/feedbackComponent/views/feedback_textfield.dart b/lib/feedbackComponent/views/feedback_textfield.dart new file mode 100644 index 00000000..97bbfcd7 --- /dev/null +++ b/lib/feedbackComponent/views/feedback_textfield.dart @@ -0,0 +1,28 @@ +import 'package:campus_flutter/base/helpers/card_with_padding.dart'; +import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; +import 'package:flutter/material.dart'; + +class FeedbackTextField extends StatelessWidget { + const FeedbackTextField( + {super.key, + required this.title, + required this.textEditingController, + this.expanded = false}); + + final String title; + final TextEditingController textEditingController; + final bool expanded; + + @override + Widget build(BuildContext context) { + return WidgetFrameView( + title: title, + child: CardWithPadding( + child: TextFormField( + controller: textEditingController, + minLines: expanded ? 3 : null, + maxLines: expanded ? 6 : null, + ), + )); + } +} diff --git a/lib/navigation_service.dart b/lib/navigation_service.dart index 3915c645..868ac141 100644 --- a/lib/navigation_service.dart +++ b/lib/navigation_service.dart @@ -6,6 +6,7 @@ import 'package:campus_flutter/homeComponent/home_screen.dart'; import 'package:campus_flutter/lectureComponent/views/lectures_view.dart'; import 'package:campus_flutter/placesComponent/views/places_screen.dart'; import 'package:campus_flutter/searchComponent/views/appWideSearch/search_scaffold.dart'; +import 'package:campus_flutter/settingsComponent/views/settings_scaffold.dart'; import 'package:campus_flutter/settingsComponent/views/settings_view.dart'; import 'package:campus_flutter/theme.dart'; import 'package:flutter/foundation.dart'; @@ -75,11 +76,15 @@ class NavigationService { List actions(BuildContext context) { return [ IconButton( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => const SettingsView())); - }, - icon: const Icon(Icons.settings)), + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const SettingsScaffold(), + ), + ); + }, + icon: const Icon(Icons.settings), + ), ]; } diff --git a/lib/providers_get_it.dart b/lib/providers_get_it.dart index dd4dc794..d2352126 100644 --- a/lib/providers_get_it.dart +++ b/lib/providers_get_it.dart @@ -4,6 +4,7 @@ import 'package:campus_flutter/base/enums/appearance.dart'; import 'package:campus_flutter/calendarComponent/model/calendar_event.dart'; import 'package:campus_flutter/calendarComponent/viewModels/calendar_viewmodel.dart'; import 'package:campus_flutter/departuresComponent/viewModel/departures_viewmodel.dart'; +import 'package:campus_flutter/feedbackComponent/viewModels/feedback_viewmodel.dart'; import 'package:campus_flutter/gradeComponent/viewModels/grade_viewmodel.dart'; import 'package:campus_flutter/homeComponent/split_view_viewmodel.dart'; import 'package:campus_flutter/homeComponent/widgetComponent/viewModels/recommenderViewModel.dart'; @@ -134,6 +135,9 @@ final settingsViewModel = Provider((ref) => SettingsViewModel(ref)); final userPreferencesViewModel = Provider((ref) => UserPreferencesViewModel(ref)); +/// view model for feedback +final feedbackViewModel = Provider((ref) => FeedbackViewModel()); + /// search view models final searchViewModel = Provider((ref) => GlobalSearchViewModel(ref)); final gradesSearchViewModel = Provider((ref) => GradesSearchViewModel()); diff --git a/lib/settingsComponent/views/settings_scaffold.dart b/lib/settingsComponent/views/settings_scaffold.dart new file mode 100644 index 00000000..6e3e5ebf --- /dev/null +++ b/lib/settingsComponent/views/settings_scaffold.dart @@ -0,0 +1,29 @@ +import 'package:campus_flutter/feedbackComponent/views/feedback_form_view.dart'; +import 'package:campus_flutter/settingsComponent/views/settings_view.dart'; +import 'package:campus_flutter/theme.dart'; +import 'package:flutter/material.dart'; + +class SettingsScaffold extends StatelessWidget { + const SettingsScaffold({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: const BackButton(), + title: Text(context.localizations.settings), + actions: [ + IconButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const FeedbackFormScaffold())), + icon: Icon( + Icons.help, + color: context.theme.primaryColor, + )) + ], + ), + body: const SettingsView()); + } +} diff --git a/lib/settingsComponent/views/settings_view.dart b/lib/settingsComponent/views/settings_view.dart index 0def9851..5172c08e 100644 --- a/lib/settingsComponent/views/settings_view.dart +++ b/lib/settingsComponent/views/settings_view.dart @@ -5,6 +5,7 @@ import 'package:campus_flutter/base/extensions/locale+fullname.dart'; import 'package:campus_flutter/base/helpers/hyperlink_text.dart'; import 'package:campus_flutter/base/helpers/icon_text.dart'; import 'package:campus_flutter/base/views/seperated_list.dart'; +import 'package:campus_flutter/feedbackComponent/views/feedback_form_view.dart'; import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; import 'package:campus_flutter/loginComponent/viewModels/login_viewmodel.dart'; import 'package:campus_flutter/loginComponent/views/permission_check_view.dart'; @@ -24,18 +25,13 @@ class SettingsView extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return Scaffold( - appBar: AppBar( - leading: const BackButton(), - title: Text(context.localizations.settings), - ), - body: ListView(children: [ - _generalSettings(context, ref), - _appearance(context, ref), - _contact(context, ref), - _authentication(context, ref), - _versionNumber() - ])); + return ListView(children: [ + _generalSettings(context, ref), + _appearance(context, ref), + _contact(context, ref), + _authentication(context, ref), + _versionNumber() + ]); } Widget _generalSettings(BuildContext context, WidgetRef ref) { From e8c20acfcff3d876e9ed7d9201b722ef7e0fe91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Sun, 5 Nov 2023 12:54:44 +0100 Subject: [PATCH 2/6] Update Settings Icon and Title --- lib/base/localization/l10n/app_de.arb | 1 + lib/base/localization/l10n/app_en.arb | 1 + lib/feedbackComponent/views/feedback_form_view.dart | 1 - lib/navigation_service.dart | 3 +-- lib/settingsComponent/views/settings_scaffold.dart | 3 ++- lib/settingsComponent/views/settings_view.dart | 1 - 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/base/localization/l10n/app_de.arb b/lib/base/localization/l10n/app_de.arb index 609e7617..cfe42fc4 100644 --- a/lib/base/localization/l10n/app_de.arb +++ b/lib/base/localization/l10n/app_de.arb @@ -6,6 +6,7 @@ "identification":"Identifikation", "places":"Orte", "settings":"Einstellungen", + "settingsAndFeedback": "Einstellungen & Feedback", "generalSettings":"Allgemeine Einstellungen", "comingSoon":"demnächst verfügbar", "appearance":"Darstellung", diff --git a/lib/base/localization/l10n/app_en.arb b/lib/base/localization/l10n/app_en.arb index 4567a918..df021840 100644 --- a/lib/base/localization/l10n/app_en.arb +++ b/lib/base/localization/l10n/app_en.arb @@ -6,6 +6,7 @@ "identification":"Identification", "places":"Places", "settings":"Settings", + "settingsAndFeedback": "Settings & Feedback", "generalSettings":"General Settings", "comingSoon":"Coming Soon", "language":"Language", diff --git a/lib/feedbackComponent/views/feedback_form_view.dart b/lib/feedbackComponent/views/feedback_form_view.dart index f1dc0658..ef5ef7d2 100644 --- a/lib/feedbackComponent/views/feedback_form_view.dart +++ b/lib/feedbackComponent/views/feedback_form_view.dart @@ -1,5 +1,4 @@ import 'package:campus_flutter/base/views/seperated_list.dart'; -import 'package:campus_flutter/feedbackComponent/viewModels/feedback_viewmodel.dart'; import 'package:campus_flutter/feedbackComponent/views/feedback_checkmark_view.dart'; import 'package:campus_flutter/feedbackComponent/views/feedback_textfield.dart'; import 'package:campus_flutter/providers_get_it.dart'; diff --git a/lib/navigation_service.dart b/lib/navigation_service.dart index 868ac141..4f00eaed 100644 --- a/lib/navigation_service.dart +++ b/lib/navigation_service.dart @@ -7,7 +7,6 @@ import 'package:campus_flutter/lectureComponent/views/lectures_view.dart'; import 'package:campus_flutter/placesComponent/views/places_screen.dart'; import 'package:campus_flutter/searchComponent/views/appWideSearch/search_scaffold.dart'; import 'package:campus_flutter/settingsComponent/views/settings_scaffold.dart'; -import 'package:campus_flutter/settingsComponent/views/settings_view.dart'; import 'package:campus_flutter/theme.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -83,7 +82,7 @@ class NavigationService { ), ); }, - icon: const Icon(Icons.settings), + icon: const Icon(Icons.menu), ), ]; } diff --git a/lib/settingsComponent/views/settings_scaffold.dart b/lib/settingsComponent/views/settings_scaffold.dart index 6e3e5ebf..7b9aac6e 100644 --- a/lib/settingsComponent/views/settings_scaffold.dart +++ b/lib/settingsComponent/views/settings_scaffold.dart @@ -11,7 +11,8 @@ class SettingsScaffold extends StatelessWidget { return Scaffold( appBar: AppBar( leading: const BackButton(), - title: Text(context.localizations.settings), + titleSpacing: 0, + title: Text(context.localizations.settingsAndFeedback), actions: [ IconButton( onPressed: () => Navigator.push( diff --git a/lib/settingsComponent/views/settings_view.dart b/lib/settingsComponent/views/settings_view.dart index 5172c08e..d2a72709 100644 --- a/lib/settingsComponent/views/settings_view.dart +++ b/lib/settingsComponent/views/settings_view.dart @@ -5,7 +5,6 @@ import 'package:campus_flutter/base/extensions/locale+fullname.dart'; import 'package:campus_flutter/base/helpers/hyperlink_text.dart'; import 'package:campus_flutter/base/helpers/icon_text.dart'; import 'package:campus_flutter/base/views/seperated_list.dart'; -import 'package:campus_flutter/feedbackComponent/views/feedback_form_view.dart'; import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; import 'package:campus_flutter/loginComponent/viewModels/login_viewmodel.dart'; import 'package:campus_flutter/loginComponent/views/permission_check_view.dart'; From f0c982f265910cd9b9352f29220a62df81b90a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:58:13 +0100 Subject: [PATCH 3/6] Add working UI for Feedback --- lib/base/localization/app_de.arb | 18 +++- lib/base/localization/app_en.arb | 18 +++- .../services/feedback_service.dart | 2 +- .../viewModels/feedback_viewmodel.dart | 80 ++++++++++++++++- .../views/feedback_form_view.dart | 86 ++++++++++++++++--- .../views/feedback_textfield.dart | 41 ++++++--- lib/providers_get_it.dart | 2 +- pubspec.yaml | 2 +- 8 files changed, 214 insertions(+), 35 deletions(-) diff --git a/lib/base/localization/app_de.arb b/lib/base/localization/app_de.arb index cfe42fc4..ff5b94f2 100644 --- a/lib/base/localization/app_de.arb +++ b/lib/base/localization/app_de.arb @@ -6,7 +6,7 @@ "identification":"Identifikation", "places":"Orte", "settings":"Einstellungen", - "settingsAndFeedback": "Einstellungen & Feedback", + "settingsAndFeedback":"Einstellungen & Feedback", "generalSettings":"Allgemeine Einstellungen", "comingSoon":"demnächst verfügbar", "appearance":"Darstellung", @@ -201,7 +201,7 @@ }, "noRoomsFound":"Keine Räume gefunden", "noMealPlanFound":"Kein Essensplan gefunden", - "mealPlan": "Essensplan", + "mealPlan":"Essensplan", "noEntriesFoundSearch":"Keine {searchCategory} gefunden", "@noEntriesFoundSearch":{ "description":"Title of Search Category", @@ -252,5 +252,17 @@ } }, "closed":"Geschlossen", - "closedToday":"Heute geschlossen" + "closedToday":"Heute geschlossen", + "submitFeedback":"Feedback einreichen", + "yourEmailAddress":"Deine E-Mail-Adresse", + "message":"Nachricht", + "shareLocation":"Aktuellen Standort teilen", + "shareDeviceInformation":"Informationen über das Gerät teilen", + "submit":"Senden", + "yourMessage":"Deine Nachricht...", + "yourEmail":"deineemail@tum.de", + "invalidEmail":"Ungültige E-Mail-Adresse", + "invalidMessage":"Ungültige Nachricht", + "unableToSend":"Nachricht kann nicht gesendet werden!", + "successfullySent":"Nachricht erfolgreich gesendet!\n Danke für Dein Feedback!" } \ No newline at end of file diff --git a/lib/base/localization/app_en.arb b/lib/base/localization/app_en.arb index df021840..7564ff9a 100644 --- a/lib/base/localization/app_en.arb +++ b/lib/base/localization/app_en.arb @@ -6,7 +6,7 @@ "identification":"Identification", "places":"Places", "settings":"Settings", - "settingsAndFeedback": "Settings & Feedback", + "settingsAndFeedback":"Settings & Feedback", "generalSettings":"General Settings", "comingSoon":"Coming Soon", "language":"Language", @@ -201,7 +201,7 @@ }, "noRoomsFound":"No Rooms Found", "noMealPlanFound":"No Meal Plan Found", - "mealPlan": "Meal Plan", + "mealPlan":"Meal Plan", "noEntriesFoundSearch":"No {searchCategory} Found", "@noEntriesFoundSearch":{ "description":"Title of Search Category", @@ -252,5 +252,17 @@ } }, "closed":"Closed", - "closedToday":"Closed Today" + "closedToday":"Closed Today", + "submitFeedback":"Submit Feedback", + "yourEmailAddress":"Your Email Address", + "message":"Message", + "shareLocation":"Share Current Location", + "shareDeviceInformation":"Share Device's Information", + "submit":"Submit", + "yourMessage":"Your Message", + "yourEmail":"youremail@tum.de", + "invalidEmail":"Invalid Email Address", + "invalidMessage":"Invalid Message", + "unableToSend":"Unable to Send Message!", + "successfullySent":"Message Successfully Sent!\n Thanks for Your Feedback!" } \ No newline at end of file diff --git a/lib/feedbackComponent/services/feedback_service.dart b/lib/feedbackComponent/services/feedback_service.dart index 082fda25..62a17143 100644 --- a/lib/feedbackComponent/services/feedback_service.dart +++ b/lib/feedbackComponent/services/feedback_service.dart @@ -3,7 +3,7 @@ import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pbgrpc import 'package:campus_flutter/providers_get_it.dart'; class FeedbackService { - Future sendFeedback( + static Future sendFeedback( CreateFeedbackRequest createFeedbackRequest, ) async { CachedCampusClient mainApi = getIt(); diff --git a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart index 18e2b4ee..c53f8d26 100644 --- a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart +++ b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart @@ -1,16 +1,33 @@ +import 'dart:developer'; + import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pb.dart'; import 'package:campus_flutter/base/services/location_service.dart'; +import 'package:campus_flutter/feedbackComponent/services/feedback_service.dart'; +import 'package:campus_flutter/providers_get_it.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geolocator/geolocator.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:rxdart/rxdart.dart'; class FeedbackViewModel { BehaviorSubject shareLocation = BehaviorSubject.seeded(false); BehaviorSubject shareDeviceInfos = BehaviorSubject.seeded(false); + BehaviorSubject activeButton = BehaviorSubject.seeded(false); + BehaviorSubject validEmail = BehaviorSubject.seeded(null); + BehaviorSubject validMessage = BehaviorSubject.seeded(null); + BehaviorSubject successfullySent = BehaviorSubject.seeded(null); final TextEditingController emailAddress = TextEditingController(); final TextEditingController message = TextEditingController(); - FeedbackViewModel(); + FeedbackViewModel(Ref ref) { + final email = ref.read(profileDetailsViewModel).personDetails.value?.email; + if (email != null) { + emailAddress.text = email; + validEmail.add(true); + } + } Future sendFeedBack() async { Position? position; @@ -18,6 +35,17 @@ class FeedbackViewModel { position = await LocationService.getLastKnown(); } + String? deviceInfos; + if (shareDeviceInfos.value) { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + final baseInfo = await deviceInfo.deviceInfo; + deviceInfos = + "${baseInfo.data["machine"] ?? baseInfo.data["model"]} - ${baseInfo.data["systemVersion"]}"; + } + + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + String packageInfos = packageInfo.version; + final feedback = CreateFeedbackRequest( recipient: CreateFeedbackRequest_Recipient.TUM_DEV, fromEmail: emailAddress.text, @@ -26,6 +54,56 @@ class FeedbackViewModel { latitude: position?.latitude, longitude: position?.longitude, ), + osVersion: deviceInfos, + appVersion: packageInfos, + ); + + FeedbackService.sendFeedback(feedback).then( + (value) => successfullySent.add(true), + onError: (error) { + final errors = error as StateError; + log(errors.stackTrace.toString()); + successfullySent.addError(error); + }, ); } + + void checkMessageValidity() { + if (message.value.text.isNotEmpty) { + validMessage.add(true); + } else { + validMessage.add(false); + } + checkButton(); + } + + void checkEmailValidity() { + final RegExp validEmailRegex = RegExp( + r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+", + ); + if (emailAddress.value.text.isNotEmpty) { + if (validEmailRegex.hasMatch(emailAddress.value.text)) { + validEmail.add(true); + } else { + validEmail.add(false); + } + } + checkButton(); + } + + void checkButton() { + activeButton + .add((validEmail.value ?? false) && (validMessage.value ?? false)); + } + + void clearForm() { + emailAddress.text = ""; + message.text = ""; + shareLocation.add(false); + shareDeviceInfos.add(false); + activeButton.add(false); + validMessage.add(null); + validEmail.add(null); + successfullySent.add(null); + } } diff --git a/lib/feedbackComponent/views/feedback_form_view.dart b/lib/feedbackComponent/views/feedback_form_view.dart index 820f6d5f..26801b7a 100644 --- a/lib/feedbackComponent/views/feedback_form_view.dart +++ b/lib/feedbackComponent/views/feedback_form_view.dart @@ -1,4 +1,6 @@ +import 'package:campus_flutter/base/enums/error_handling_view_type.dart'; import 'package:campus_flutter/base/extensions/context.dart'; +import 'package:campus_flutter/base/views/error_handling_view.dart'; import 'package:campus_flutter/base/views/seperated_list.dart'; import 'package:campus_flutter/feedbackComponent/views/feedback_checkmark_view.dart'; import 'package:campus_flutter/feedbackComponent/views/feedback_textfield.dart'; @@ -6,15 +8,20 @@ import 'package:campus_flutter/providers_get_it.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -class FeedbackFormScaffold extends StatelessWidget { +class FeedbackFormScaffold extends ConsumerWidget { const FeedbackFormScaffold({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( - leading: const BackButton(), - title: const Text("Submit Feedback"), + leading: BackButton( + onPressed: () { + Navigator.pop(context); + ref.read(feedbackViewModel).clearForm(); + }, + ), + title: Text(context.localizations.submitFeedback), ), body: const FeedbackFormView(), ); @@ -30,34 +37,85 @@ class FeedbackFormView extends ConsumerWidget { child: Column( children: [ FeedbackTextField( - title: "Your Email Address", + title: context.localizations.yourEmailAddress, textEditingController: ref.read(feedbackViewModel).emailAddress, + validInput: ref.watch(feedbackViewModel).validEmail, + onChanged: (text) => + ref.read(feedbackViewModel).checkEmailValidity(), + invalidMessage: context.localizations.invalidEmail, + decorationMessage: context.localizations.yourEmail, ), FeedbackTextField( - title: "Message", + title: context.localizations.message, textEditingController: ref.read(feedbackViewModel).message, + validInput: ref.watch(feedbackViewModel).validMessage, + onChanged: (text) => + ref.read(feedbackViewModel).checkMessageValidity(), + invalidMessage: context.localizations.invalidMessage, + decorationMessage: context.localizations.yourMessage, expanded: true, ), Card( child: SeparatedList.widgets( widgets: [ FeedbackCheckMarkView( - text: "Share your location", + text: context.localizations.shareLocation, isChecked: ref.read(feedbackViewModel).shareLocation, ), FeedbackCheckMarkView( - text: "Share Device's Information", + text: context.localizations.shareDeviceInformation, isChecked: ref.read(feedbackViewModel).shareDeviceInfos, ), ], ), ), - Padding( - padding: EdgeInsets.all(context.padding), - child: ElevatedButton( - onPressed: () => ref.read(feedbackViewModel).sendFeedBack(), - child: const Text("Submit"), - ), + StreamBuilder( + stream: ref.watch(feedbackViewModel).activeButton, + builder: (context, snapshot) { + return Padding( + padding: EdgeInsets.all(context.padding), + child: ElevatedButton( + onPressed: (snapshot.data != null && snapshot.data!) + ? () => ref.read(feedbackViewModel).sendFeedBack() + : null, + child: Text(context.localizations.submit), + ), + ); + }, + ), + StreamBuilder( + stream: ref.watch(feedbackViewModel).successfullySent, + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text( + context.localizations.successfullySent, + style: context.theme.textTheme.bodyLarge + ?.copyWith(color: Colors.green), + ); + } else if (snapshot.hasError) { + return Column( + children: [ + Text( + context.localizations.unableToSend, + style: context.theme.textTheme.bodyLarge + ?.copyWith(color: Colors.redAccent), + ), + Padding( + padding: EdgeInsets.symmetric( + vertical: context.halfPadding, + ), + ), + ErrorHandlingView( + error: snapshot.error!, + errorHandlingViewType: + ErrorHandlingViewType.descriptionOnly, + ), + ], + ); + } else { + return const SizedBox(); + } + }, ), ], ), diff --git a/lib/feedbackComponent/views/feedback_textfield.dart b/lib/feedbackComponent/views/feedback_textfield.dart index c6eda36a..fb885088 100644 --- a/lib/feedbackComponent/views/feedback_textfield.dart +++ b/lib/feedbackComponent/views/feedback_textfield.dart @@ -1,30 +1,49 @@ import 'package:campus_flutter/base/helpers/card_with_padding.dart'; import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class FeedbackTextField extends StatelessWidget { +class FeedbackTextField extends ConsumerWidget { const FeedbackTextField({ super.key, required this.title, required this.textEditingController, + required this.validInput, + required this.onChanged, + required this.invalidMessage, + required this.decorationMessage, this.expanded = false, }); final String title; final TextEditingController textEditingController; + final Stream validInput; + final void Function(String)? onChanged; + final String invalidMessage; + final String decorationMessage; final bool expanded; @override - Widget build(BuildContext context) { - return WidgetFrameView( - title: title, - child: CardWithPadding( - child: TextFormField( - controller: textEditingController, - minLines: expanded ? 3 : null, - maxLines: expanded ? 6 : null, - ), - ), + Widget build(BuildContext context, WidgetRef ref) { + return StreamBuilder( + stream: validInput, + builder: (context, snapshot) { + return WidgetFrameView( + title: title, + child: CardWithPadding( + child: TextFormField( + controller: textEditingController, + minLines: expanded ? 3 : null, + maxLines: expanded ? 6 : null, + onChanged: onChanged, + decoration: InputDecoration( + hintText: decorationMessage, + errorText: (snapshot.data ?? true) ? null : invalidMessage, + ), + ), + ), + ); + }, ); } } diff --git a/lib/providers_get_it.dart b/lib/providers_get_it.dart index 27159018..9df4f52f 100644 --- a/lib/providers_get_it.dart +++ b/lib/providers_get_it.dart @@ -137,7 +137,7 @@ final userPreferencesViewModel = Provider((ref) => UserPreferencesViewModel(ref)); /// view model for feedback -final feedbackViewModel = Provider((ref) => FeedbackViewModel()); +final feedbackViewModel = Provider((ref) => FeedbackViewModel(ref)); /// search view models final searchViewModel = Provider((ref) => GlobalSearchViewModel(ref)); diff --git a/pubspec.yaml b/pubspec.yaml index 9bec49ea..6e9c053a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: campus_flutter description: A new Flutter project. publish_to: 'none' -version: 0.3.2 +version: 0.3.3 environment: sdk: '>=3.0.0 <4.0.0' From 39d62d5c5b64666bf84cdeaa3deefbfbbd3bd716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:05:58 +0100 Subject: [PATCH 4/6] Feedback Form now working, Caching for Streams disabled --- ios/build/.last_build_id | 2 +- .../apis/tumdev/cache_interceptor.dart | 40 +++---------------- .../services/feedback_service.dart | 2 + .../viewModels/feedback_viewmodel.dart | 6 ++- .../views/feedback_form_view.dart | 20 ++++++++-- .../views/settings_view.dart | 15 +++++-- 6 files changed, 41 insertions(+), 44 deletions(-) diff --git a/ios/build/.last_build_id b/ios/build/.last_build_id index 953e56fc..bdeefb47 100644 --- a/ios/build/.last_build_id +++ b/ios/build/.last_build_id @@ -1 +1 @@ -d4fe773e5af8aea054184ce4dc69442d \ No newline at end of file +4988aa35441b901c4e8c4b76a3fe883f \ No newline at end of file diff --git a/lib/base/networking/apis/tumdev/cache_interceptor.dart b/lib/base/networking/apis/tumdev/cache_interceptor.dart index 1fa0d41a..93bfa6fd 100644 --- a/lib/base/networking/apis/tumdev/cache_interceptor.dart +++ b/lib/base/networking/apis/tumdev/cache_interceptor.dart @@ -30,37 +30,7 @@ class CacheInterceptor implements ClientInterceptor { CallOptions options, ClientStreamingInvoker invoker, ) { - final key = "${method.path}?${requests.toString()}"; - final (bool, dynamic) cachedResponse; - if (kIsWeb) { - cachedResponse = memCacheStore.get(key); - } else { - cachedResponse = hiveCacheStore.get(key); - } - final factory = _getFactory(); - if (cachedResponse.$1 && cachedResponse.$2 != null && factory != null) { - final data = factory(cachedResponse.$2!.data); - return ResponseStream( - ClientCall( - ClientMethod(method.path, (value) => [], (value) => data), - requests, - options, - null, - true, - ), - ); - } - - final response = invoker(method, requests, options); - response.listen((data) { - if (kIsWeb) { - memCacheStore.put(key, _convertToBuffer(data)); - } else { - hiveCacheStore.put(key, _convertToBuffer(data)); - } - }); - - return response; + return invoker(method, requests, options); } @override @@ -107,10 +77,10 @@ class CacheInterceptor implements ClientInterceptor { // TODO: figure out nicer solution or add missing classes Uint8List? _convertToBuffer(R data) { switch (R) { - case ListNewsReply: + case ListNewsReply _: final listData = data as ListNewsReply; return listData.writeToBuffer(); - case ListMoviesReply: + case ListMoviesReply _: final movieData = data as ListMoviesReply; return movieData.writeToBuffer(); default: @@ -121,12 +91,12 @@ class CacheInterceptor implements ClientInterceptor { // TODO: figure out nicer solution or add missing classes R Function(List, [ExtensionRegistry])? _getFactory() { switch (R) { - case ListNewsReply: + case ListNewsReply _: return ListNewsReply.fromBuffer as R Function( List, [ ExtensionRegistry, ]); - case ListMoviesReply: + case ListMoviesReply _: return ListMoviesReply.fromBuffer as R Function( List, [ ExtensionRegistry, diff --git a/lib/feedbackComponent/services/feedback_service.dart b/lib/feedbackComponent/services/feedback_service.dart index 62a17143..f1cfc3f7 100644 --- a/lib/feedbackComponent/services/feedback_service.dart +++ b/lib/feedbackComponent/services/feedback_service.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:campus_flutter/base/networking/apis/tumdev/cached_client.dart'; import 'package:campus_flutter/base/networking/apis/tumdev/campus_backend.pbgrpc.dart'; import 'package:campus_flutter/providers_get_it.dart'; diff --git a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart index c53f8d26..ca9c8962 100644 --- a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart +++ b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart @@ -21,7 +21,11 @@ class FeedbackViewModel { final TextEditingController emailAddress = TextEditingController(); final TextEditingController message = TextEditingController(); - FeedbackViewModel(Ref ref) { + final Ref ref; + + FeedbackViewModel(this.ref); + + initForm() { final email = ref.read(profileDetailsViewModel).personDetails.value?.email; if (email != null) { emailAddress.text = email; diff --git a/lib/feedbackComponent/views/feedback_form_view.dart b/lib/feedbackComponent/views/feedback_form_view.dart index 26801b7a..0c841b09 100644 --- a/lib/feedbackComponent/views/feedback_form_view.dart +++ b/lib/feedbackComponent/views/feedback_form_view.dart @@ -28,11 +28,23 @@ class FeedbackFormScaffold extends ConsumerWidget { } } -class FeedbackFormView extends ConsumerWidget { +class FeedbackFormView extends ConsumerStatefulWidget { const FeedbackFormView({super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => + _FeedbackFormViewState(); +} + +class _FeedbackFormViewState extends ConsumerState { + @override + void initState() { + ref.read(feedbackViewModel).initForm(); + super.initState(); + } + + @override + Widget build(BuildContext context) { return Form( child: Column( children: [ @@ -90,7 +102,7 @@ class FeedbackFormView extends ConsumerWidget { return Text( context.localizations.successfullySent, style: context.theme.textTheme.bodyLarge - ?.copyWith(color: Colors.green), + ?.copyWith(color: context.theme.primaryColor), ); } else if (snapshot.hasError) { return Column( @@ -98,7 +110,7 @@ class FeedbackFormView extends ConsumerWidget { Text( context.localizations.unableToSend, style: context.theme.textTheme.bodyLarge - ?.copyWith(color: Colors.redAccent), + ?.copyWith(color: context.theme.primaryColor), ), Padding( padding: EdgeInsets.symmetric( diff --git a/lib/settingsComponent/views/settings_view.dart b/lib/settingsComponent/views/settings_view.dart index 93445fb3..040e129e 100644 --- a/lib/settingsComponent/views/settings_view.dart +++ b/lib/settingsComponent/views/settings_view.dart @@ -5,6 +5,7 @@ import 'package:campus_flutter/base/extensions/locale+fullname.dart'; import 'package:campus_flutter/base/helpers/hyperlink_text.dart'; import 'package:campus_flutter/base/helpers/icon_text.dart'; import 'package:campus_flutter/base/views/seperated_list.dart'; +import 'package:campus_flutter/feedbackComponent/views/feedback_form_view.dart'; import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; import 'package:campus_flutter/loginComponent/viewModels/login_viewmodel.dart'; import 'package:campus_flutter/loginComponent/views/permission_check_view.dart'; @@ -208,10 +209,18 @@ class SettingsView extends ConsumerWidget { link: "https://app.tum.de", label: "TUM Dev Website", ), - HyperLinkListTile( + ListTile( dense: true, - uri: _feedbackEmail(), - label: "Feedback", + title: Text( + "Feedback", + style: Theme.of(context).textTheme.bodyMedium, + ), + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const FeedbackFormScaffold(), + ), + ), ), ], ), From 047c5dddc6835c0997b739cf75885e70d9292a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:22:10 +0100 Subject: [PATCH 5/6] Fix all Analyzer errors --- analysis_options.yaml | 8 ++++++++ .../{locale+fullname.dart => locale_fullname.dart} | 0 lib/base/helpers/placeholder_text.dart | 6 +++--- lib/gradeComponent/model/average_grade.g.dart | 9 +++------ ...derViewModel.dart => recommender_viewmodel.dart} | 0 lib/lectureComponent/model/lecture.dart | 4 ++-- lib/lectureComponent/model/lecture_details.dart | 9 +++++---- lib/lectureComponent/model/lecture_details.g.dart | 9 +++------ .../views/basic_lecture_info_view.dart | 2 +- lib/main.dart | 2 +- ...avigatum_navigation_additional_properties.g.dart | 13 ++++--------- .../model/navigatum_navigation_details.g.dart | 12 ++++-------- .../model/navigatum_navigation_entity.g.dart | 6 ++---- .../model/navigatum_roomfinder_map.g.dart | 6 ++---- .../viewModels/navigatum_search_viewmodel.dart | 2 +- .../viewModel/person_search_viewmodel.dart | 2 +- .../model/studyRooms/study_room.dart | 10 ++++++---- .../model/studyRooms/study_room.g.dart | 8 ++++---- ...{mapThemeService.dart => map_theme_service.dart} | 0 lib/placesComponent/views/map_widget.dart | 2 +- .../views/studyGroups/study_room_row_view.dart | 2 +- lib/providers_get_it.dart | 2 +- lib/settingsComponent/views/settings_view.dart | 9 +-------- 23 files changed, 54 insertions(+), 69 deletions(-) rename lib/base/extensions/{locale+fullname.dart => locale_fullname.dart} (100%) rename lib/homeComponent/widgetComponent/viewModels/{recommenderViewModel.dart => recommender_viewmodel.dart} (100%) rename lib/placesComponent/services/{mapThemeService.dart => map_theme_service.dart} (100%) diff --git a/analysis_options.yaml b/analysis_options.yaml index 141a382c..f4ae45cb 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,3 +7,11 @@ linter: analyzer: errors: invalid_annotation_target: ignore + exclude: + - '**/*.g.dart' + - lib/base/networking/apis/tumdev/campus_backend.pb.dart + - lib/base/networking/apis/tumdev/campus_backend.pbenum.dart + - lib/base/networking/apis/tumdev/campus_backend.pbgrpc.dart + - lib/base/networking/apis/tumdev/campus_backend.pbjson.dart + - lib/base/networking/apis/google/protobuf/empty.pbjson.dart + - lib/base/networking/apis/google/protobuf/timestamp.pb.dart \ No newline at end of file diff --git a/lib/base/extensions/locale+fullname.dart b/lib/base/extensions/locale_fullname.dart similarity index 100% rename from lib/base/extensions/locale+fullname.dart rename to lib/base/extensions/locale_fullname.dart diff --git a/lib/base/helpers/placeholder_text.dart b/lib/base/helpers/placeholder_text.dart index 70a13faa..aa39b717 100644 --- a/lib/base/helpers/placeholder_text.dart +++ b/lib/base/helpers/placeholder_text.dart @@ -8,7 +8,7 @@ class PlaceholderText extends StatelessWidget { @override Widget build(BuildContext context) { - var textSize = _calcTextSize(); + var textSize = _calcTextSize(context); return Container( height: textSize.height, width: textSize.width, @@ -17,11 +17,11 @@ class PlaceholderText extends StatelessWidget { } /// found on: https://stackoverflow.com/a/63133637 - Size _calcTextSize() { + Size _calcTextSize(BuildContext context) { final TextPainter textPainter = TextPainter( text: TextSpan(text: text, style: style), textDirection: TextDirection.ltr, - textScaleFactor: WidgetsBinding.instance.window.textScaleFactor, + textScaleFactor: MediaQuery.textScaleFactorOf(context), )..layout(); return textPainter.size; } diff --git a/lib/gradeComponent/model/average_grade.g.dart b/lib/gradeComponent/model/average_grade.g.dart index bb478e26..55cfaae5 100644 --- a/lib/gradeComponent/model/average_grade.g.dart +++ b/lib/gradeComponent/model/average_grade.g.dart @@ -10,8 +10,7 @@ AverageGrade _$AverageGradeFromJson(Map json) => AverageGrade( id: json['studidf'] as String, studyDesignation: json['studbez'] as String, averageGrade: StringParser.stringToDouble( - json['avg_grade_weighted_by_credits'] as String?, - ), + json['avg_grade_weighted_by_credits'] as String?), ); Map _$AverageGradeToJson(AverageGrade instance) => @@ -22,8 +21,7 @@ Map _$AverageGradeToJson(AverageGrade instance) => }; AverageGradeResponse _$AverageGradeResponseFromJson( - Map json, -) => + Map json) => AverageGradeResponse( averageGradeData: json['studien'] == null ? null @@ -31,8 +29,7 @@ AverageGradeResponse _$AverageGradeResponseFromJson( ); Map _$AverageGradeResponseToJson( - AverageGradeResponse instance, -) => + AverageGradeResponse instance) => { 'studien': instance.averageGradeData, }; diff --git a/lib/homeComponent/widgetComponent/viewModels/recommenderViewModel.dart b/lib/homeComponent/widgetComponent/viewModels/recommender_viewmodel.dart similarity index 100% rename from lib/homeComponent/widgetComponent/viewModels/recommenderViewModel.dart rename to lib/homeComponent/widgetComponent/viewModels/recommender_viewmodel.dart diff --git a/lib/lectureComponent/model/lecture.dart b/lib/lectureComponent/model/lecture.dart index 2fd613a9..bd3f2644 100644 --- a/lib/lectureComponent/model/lecture.dart +++ b/lib/lectureComponent/model/lecture.dart @@ -17,7 +17,7 @@ class Lecture extends Searchable { @JsonKey(name: "dauer_info") final String duration; @JsonKey(name: "stp_sp_sst") - final String stp_sp_sst; + final String stpSpSst; @JsonKey(name: "stp_lv_art_name") final String eventTypeDefault; @JsonKey(name: "stp_lv_art_kurz") @@ -77,7 +77,7 @@ class Lecture extends Searchable { required this.lvNumber, required this.title, required this.duration, - required this.stp_sp_sst, + required this.stpSpSst, required this.eventTypeDefault, required this.eventTypeTag, required this.semesterYear, diff --git a/lib/lectureComponent/model/lecture_details.dart b/lib/lectureComponent/model/lecture_details.dart index 4e535f07..f51bdf7d 100644 --- a/lib/lectureComponent/model/lecture_details.dart +++ b/lib/lectureComponent/model/lecture_details.dart @@ -16,7 +16,7 @@ class LectureDetails { @JsonKey(name: "dauer_info") final String duration; @JsonKey(name: "stp_sp_sst") - final String stp_sp_sst; + final String stpSpSst; @JsonKey(name: "stp_lv_art_name") final String eventTypeDefault; @JsonKey(name: "stp_lv_art_kurz") @@ -45,7 +45,8 @@ class LectureDetails { final String? courseObjective; @JsonKey(name: "lehrmethode") final String? teachingMethod; - final String? anmeld_lv; + @JsonKey(name: "anmeld_lv") + final String? signUpLV; @JsonKey(name: "ersttermin") final String? firstScheduledDate; @JsonKey(name: "pruefmodus") @@ -84,7 +85,7 @@ class LectureDetails { required this.lvNumber, required this.title, required this.duration, - required this.stp_sp_sst, + required this.stpSpSst, required this.eventTypeDefault, required this.eventTypeTag, required this.semester, @@ -99,7 +100,7 @@ class LectureDetails { this.requirements, this.courseObjective, this.teachingMethod, - this.anmeld_lv, + this.signUpLV, this.firstScheduledDate, this.examinationMode, this.studienbehelfe, diff --git a/lib/lectureComponent/model/lecture_details.g.dart b/lib/lectureComponent/model/lecture_details.g.dart index f799e157..52d38744 100644 --- a/lib/lectureComponent/model/lecture_details.g.dart +++ b/lib/lectureComponent/model/lecture_details.g.dart @@ -72,8 +72,7 @@ Map _$LectureDetailsToJson(LectureDetails instance) => LectureDetailsData _$LectureDetailsDataFromJson(Map json) => LectureDetailsData( lectureDetailsAttribute: LectureDetailsElement.fromJson( - json['rowset'] as Map, - ), + json['rowset'] as Map), ); Map _$LectureDetailsDataToJson(LectureDetailsData instance) => @@ -82,16 +81,14 @@ Map _$LectureDetailsDataToJson(LectureDetailsData instance) => }; LectureDetailsElement _$LectureDetailsElementFromJson( - Map json, -) => + Map json) => LectureDetailsElement( lectureDetails: LectureDetailsElement._lectureDetailsFromJson(json['row']), ); Map _$LectureDetailsElementToJson( - LectureDetailsElement instance, -) => + LectureDetailsElement instance) => { 'row': instance.lectureDetails, }; diff --git a/lib/lectureComponent/views/basic_lecture_info_view.dart b/lib/lectureComponent/views/basic_lecture_info_view.dart index aac556dd..4ec28e56 100644 --- a/lib/lectureComponent/views/basic_lecture_info_view.dart +++ b/lib/lectureComponent/views/basic_lecture_info_view.dart @@ -18,7 +18,7 @@ class BasicLectureInfoView extends ConsumerWidget { title: context.localizations.basicLectureInformation, widgets: [ BasicLectureInfoRowView( - information: "${lectureDetails.stp_sp_sst} SWS", + information: "${lectureDetails.stpSpSst} SWS", iconData: Icons.hourglass_top, ), BasicLectureInfoRowView( diff --git a/lib/main.dart b/lib/main.dart index 5a3b64c2..fe136340 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,7 @@ import 'package:campus_flutter/base/theme/light_theme.dart'; import 'package:campus_flutter/calendarComponent/services/calendar_view_service.dart'; import 'package:campus_flutter/loginComponent/views/confirm_view.dart'; import 'package:campus_flutter/navigation_service.dart'; -import 'package:campus_flutter/placesComponent/services/mapThemeService.dart'; +import 'package:campus_flutter/placesComponent/services/map_theme_service.dart'; import 'package:campus_flutter/providers_get_it.dart'; import 'package:campus_flutter/routes.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; diff --git a/lib/navigaTumComponent/model/details/navigatum_navigation_additional_properties.g.dart b/lib/navigaTumComponent/model/details/navigatum_navigation_additional_properties.g.dart index 4f7f8bbd..b01cee37 100644 --- a/lib/navigaTumComponent/model/details/navigatum_navigation_additional_properties.g.dart +++ b/lib/navigaTumComponent/model/details/navigatum_navigation_additional_properties.g.dart @@ -8,21 +8,16 @@ part of 'navigatum_navigation_additional_properties.dart'; NavigaTumNavigationAdditionalProperties _$NavigaTumNavigationAdditionalPropertiesFromJson( - Map json, -) => + Map json) => NavigaTumNavigationAdditionalProperties( (json['computed'] as List) - .map( - (e) => NavigaTumNavigationProperty.fromJson( - e as Map, - ), - ) + .map((e) => NavigaTumNavigationProperty.fromJson( + e as Map)) .toList(), ); Map _$NavigaTumNavigationAdditionalPropertiesToJson( - NavigaTumNavigationAdditionalProperties instance, -) => + NavigaTumNavigationAdditionalProperties instance) => { 'computed': instance.properties, }; diff --git a/lib/navigaTumComponent/model/navigatum_navigation_details.g.dart b/lib/navigaTumComponent/model/navigatum_navigation_details.g.dart index 10d93882..baf7168e 100644 --- a/lib/navigaTumComponent/model/navigatum_navigation_details.g.dart +++ b/lib/navigaTumComponent/model/navigatum_navigation_details.g.dart @@ -7,8 +7,7 @@ part of 'navigatum_navigation_details.dart'; // ************************************************************************** NavigaTumNavigationDetails _$NavigaTumNavigationDetailsFromJson( - Map json, -) => + Map json) => NavigaTumNavigationDetails( json['id'] as String, json['name'] as String, @@ -16,17 +15,14 @@ NavigaTumNavigationDetails _$NavigaTumNavigationDetailsFromJson( json['type'] as String, json['type_common_name'] as String, NavigaTumNavigationAdditionalProperties.fromJson( - json['props'] as Map, - ), + json['props'] as Map), NavigaTumNavigationCoordinates.fromJson( - json['coords'] as Map, - ), + json['coords'] as Map), NavigaTumNavigationMaps.fromJson(json['maps'] as Map), ); Map _$NavigaTumNavigationDetailsToJson( - NavigaTumNavigationDetails instance, -) => + NavigaTumNavigationDetails instance) => { 'id': instance.id, 'name': instance.name, diff --git a/lib/navigaTumComponent/model/navigatum_navigation_entity.g.dart b/lib/navigaTumComponent/model/navigatum_navigation_entity.g.dart index ccf475f5..4721cfa1 100644 --- a/lib/navigaTumComponent/model/navigatum_navigation_entity.g.dart +++ b/lib/navigaTumComponent/model/navigatum_navigation_entity.g.dart @@ -7,8 +7,7 @@ part of 'navigatum_navigation_entity.dart'; // ************************************************************************** NavigaTumNavigationEntity _$NavigaTumNavigationEntityFromJson( - Map json, -) => + Map json) => NavigaTumNavigationEntity( id: json['id'] as String, type: json['type'] as String, @@ -18,8 +17,7 @@ NavigaTumNavigationEntity _$NavigaTumNavigationEntityFromJson( ); Map _$NavigaTumNavigationEntityToJson( - NavigaTumNavigationEntity instance, -) => + NavigaTumNavigationEntity instance) => { 'id': instance.id, 'type': instance.type, diff --git a/lib/navigaTumComponent/model/navigatum_roomfinder_map.g.dart b/lib/navigaTumComponent/model/navigatum_roomfinder_map.g.dart index 3b0e7419..893cf5a0 100644 --- a/lib/navigaTumComponent/model/navigatum_roomfinder_map.g.dart +++ b/lib/navigaTumComponent/model/navigatum_roomfinder_map.g.dart @@ -7,8 +7,7 @@ part of 'navigatum_roomfinder_map.dart'; // ************************************************************************** NavigaTumRoomFinderMap _$NavigaTumRoomFinderMapFromJson( - Map json, -) => + Map json) => NavigaTumRoomFinderMap( id: json['id'] as String, name: json['name'] as String, @@ -21,8 +20,7 @@ NavigaTumRoomFinderMap _$NavigaTumRoomFinderMapFromJson( ); Map _$NavigaTumRoomFinderMapToJson( - NavigaTumRoomFinderMap instance, -) => + NavigaTumRoomFinderMap instance) => { 'id': instance.id, 'name': instance.name, diff --git a/lib/navigaTumComponent/viewModels/navigatum_search_viewmodel.dart b/lib/navigaTumComponent/viewModels/navigatum_search_viewmodel.dart index 1aba370f..66a74175 100644 --- a/lib/navigaTumComponent/viewModels/navigatum_search_viewmodel.dart +++ b/lib/navigaTumComponent/viewModels/navigatum_search_viewmodel.dart @@ -5,7 +5,7 @@ import 'package:collection/collection.dart'; import 'package:rxdart/rxdart.dart'; class NavigaTumSearchViewModel - extends SearchViewModel { + implements SearchViewModel { @override BehaviorSubject?> searchResults = BehaviorSubject.seeded(null); diff --git a/lib/personSearchComponent/viewModel/person_search_viewmodel.dart b/lib/personSearchComponent/viewModel/person_search_viewmodel.dart index 3ea7e7c4..c24046c9 100644 --- a/lib/personSearchComponent/viewModel/person_search_viewmodel.dart +++ b/lib/personSearchComponent/viewModel/person_search_viewmodel.dart @@ -3,7 +3,7 @@ import 'package:campus_flutter/personSearchComponent/services/person_search_serv import 'package:campus_flutter/searchComponent/protocols/search_viewmodel.dart'; import 'package:rxdart/rxdart.dart'; -class PersonSearchViewModel extends SearchViewModel { +class PersonSearchViewModel implements SearchViewModel { @override BehaviorSubject?> searchResults = BehaviorSubject.seeded(null); diff --git a/lib/placesComponent/model/studyRooms/study_room.dart b/lib/placesComponent/model/studyRooms/study_room.dart index fd43f9df..c9037bd4 100644 --- a/lib/placesComponent/model/studyRooms/study_room.dart +++ b/lib/placesComponent/model/studyRooms/study_room.dart @@ -33,8 +33,10 @@ class StudyRoom extends Searchable { final int occupiedIn; @JsonKey(name: "belegung_bis", fromJson: DateTime.tryParse) final DateTime? occupiedUntil; - final String? raum_nr_architekt; - final int res_nr; + @JsonKey(name: "raum_nr_architekt") + final String? roomNoArchitect; + @JsonKey(name: "res_nr") + final int resNo; final String? status; @JsonKey(name: "attribute") final List? attributes; @@ -89,8 +91,8 @@ class StudyRoom extends Searchable { this.occupiedFrom, required this.occupiedIn, this.occupiedUntil, - this.raum_nr_architekt, - required this.res_nr, + this.roomNoArchitect, + required this.resNo, this.status, this.attributes, }); diff --git a/lib/placesComponent/model/studyRooms/study_room.g.dart b/lib/placesComponent/model/studyRooms/study_room.g.dart index 3837062c..a1694afb 100644 --- a/lib/placesComponent/model/studyRooms/study_room.g.dart +++ b/lib/placesComponent/model/studyRooms/study_room.g.dart @@ -19,8 +19,8 @@ StudyRoom _$StudyRoomFromJson(Map json) => StudyRoom( occupiedFrom: DateTime.tryParse(json['belegung_ab'] as String), occupiedIn: json['belegung_in'] as int, occupiedUntil: DateTime.tryParse(json['belegung_bis'] as String), - raum_nr_architekt: json['raum_nr_architekt'] as String?, - res_nr: json['res_nr'] as int, + roomNoArchitect: json['raum_nr_architekt'] as String?, + resNo: json['res_nr'] as int, status: json['status'] as String?, attributes: (json['attribute'] as List?) ?.map((e) => StudyRoomAttribute.fromJson(e as Map)) @@ -40,8 +40,8 @@ Map _$StudyRoomToJson(StudyRoom instance) => { 'belegung_ab': instance.occupiedFrom?.toIso8601String(), 'belegung_in': instance.occupiedIn, 'belegung_bis': instance.occupiedUntil?.toIso8601String(), - 'raum_nr_architekt': instance.raum_nr_architekt, - 'res_nr': instance.res_nr, + 'raum_nr_architekt': instance.roomNoArchitect, + 'res_nr': instance.resNo, 'status': instance.status, 'attribute': instance.attributes, }; diff --git a/lib/placesComponent/services/mapThemeService.dart b/lib/placesComponent/services/map_theme_service.dart similarity index 100% rename from lib/placesComponent/services/mapThemeService.dart rename to lib/placesComponent/services/map_theme_service.dart diff --git a/lib/placesComponent/views/map_widget.dart b/lib/placesComponent/views/map_widget.dart index 713bfd49..aab113d8 100644 --- a/lib/placesComponent/views/map_widget.dart +++ b/lib/placesComponent/views/map_widget.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:campus_flutter/placesComponent/services/mapThemeService.dart'; +import 'package:campus_flutter/placesComponent/services/map_theme_service.dart'; import 'package:campus_flutter/providers_get_it.dart'; import 'package:campus_flutter/base/extensions/context.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/placesComponent/views/studyGroups/study_room_row_view.dart b/lib/placesComponent/views/studyGroups/study_room_row_view.dart index aae64831..12aaa11c 100644 --- a/lib/placesComponent/views/studyGroups/study_room_row_view.dart +++ b/lib/placesComponent/views/studyGroups/study_room_row_view.dart @@ -30,7 +30,7 @@ class StudyRoomRowView extends ConsumerWidget { context, MaterialPageRoute( builder: (context) => NavigaTumRoomScaffold( - id: studyRoom.raum_nr_architekt ?? "null", + id: studyRoom.roomNoArchitect ?? "null", ), ), ); diff --git a/lib/providers_get_it.dart b/lib/providers_get_it.dart index 9df4f52f..8dd0eeee 100644 --- a/lib/providers_get_it.dart +++ b/lib/providers_get_it.dart @@ -7,7 +7,7 @@ import 'package:campus_flutter/departuresComponent/viewModel/departures_viewmode import 'package:campus_flutter/feedbackComponent/viewModels/feedback_viewmodel.dart'; import 'package:campus_flutter/gradeComponent/viewModels/grade_viewmodel.dart'; import 'package:campus_flutter/homeComponent/split_view_viewmodel.dart'; -import 'package:campus_flutter/homeComponent/widgetComponent/viewModels/recommenderViewModel.dart'; +import 'package:campus_flutter/homeComponent/widgetComponent/viewModels/recommender_viewmodel.dart'; import 'package:campus_flutter/lectureComponent/model/lecture.dart'; import 'package:campus_flutter/lectureComponent/viewModels/lecture_details_viewmodel.dart'; import 'package:campus_flutter/lectureComponent/viewModels/lecture_viewmodel.dart'; diff --git a/lib/settingsComponent/views/settings_view.dart b/lib/settingsComponent/views/settings_view.dart index 040e129e..81f9b0e8 100644 --- a/lib/settingsComponent/views/settings_view.dart +++ b/lib/settingsComponent/views/settings_view.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:campus_flutter/base/enums/appearance.dart'; -import 'package:campus_flutter/base/extensions/locale+fullname.dart'; +import 'package:campus_flutter/base/extensions/locale_fullname.dart'; import 'package:campus_flutter/base/helpers/hyperlink_text.dart'; import 'package:campus_flutter/base/helpers/icon_text.dart'; import 'package:campus_flutter/base/views/seperated_list.dart'; @@ -236,13 +236,6 @@ class SettingsView extends ConsumerWidget { } } - Uri _feedbackEmail() { - final operatingSystem = kIsWeb ? "Web App" : Platform.operatingSystem; - String email = Uri.encodeComponent("app@tum.de"); - String subject = Uri.encodeComponent("[$operatingSystem - Feedback]"); - return Uri.parse("mailto:$email?subject=$subject"); - } - Widget _authentication(BuildContext context, WidgetRef ref) { final login = ref.read(loginViewModel).credentials.value; return WidgetFrameView( From a4c864fdf9156ce05b3276faa62367202ea84ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:26:17 +0100 Subject: [PATCH 6/6] Update Model --- lib/lectureComponent/model/lecture.g.dart | 4 ++-- lib/lectureComponent/model/lecture_details.g.dart | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/lectureComponent/model/lecture.g.dart b/lib/lectureComponent/model/lecture.g.dart index 0ef9780e..82c723fe 100644 --- a/lib/lectureComponent/model/lecture.g.dart +++ b/lib/lectureComponent/model/lecture.g.dart @@ -11,7 +11,7 @@ Lecture _$LectureFromJson(Map json) => Lecture( lvNumber: json['stp_lv_nr'] as String, title: json['stp_sp_titel'] as String, duration: json['dauer_info'] as String, - stp_sp_sst: json['stp_sp_sst'] as String, + stpSpSst: json['stp_sp_sst'] as String, eventTypeDefault: json['stp_lv_art_name'] as String, eventTypeTag: json['stp_lv_art_kurz'] as String, semesterYear: json['sj_name'] as String, @@ -29,7 +29,7 @@ Map _$LectureToJson(Lecture instance) => { 'stp_lv_nr': instance.lvNumber, 'stp_sp_titel': instance.title, 'dauer_info': instance.duration, - 'stp_sp_sst': instance.stp_sp_sst, + 'stp_sp_sst': instance.stpSpSst, 'stp_lv_art_name': instance.eventTypeDefault, 'stp_lv_art_kurz': instance.eventTypeTag, 'sj_name': instance.semesterYear, diff --git a/lib/lectureComponent/model/lecture_details.g.dart b/lib/lectureComponent/model/lecture_details.g.dart index 52d38744..e19298f9 100644 --- a/lib/lectureComponent/model/lecture_details.g.dart +++ b/lib/lectureComponent/model/lecture_details.g.dart @@ -12,7 +12,7 @@ LectureDetails _$LectureDetailsFromJson(Map json) => lvNumber: StringParser.stringToInt(json['stp_lv_nr'] as String?), title: json['stp_sp_titel'] as String, duration: json['dauer_info'] as String, - stp_sp_sst: json['stp_sp_sst'] as String, + stpSpSst: json['stp_sp_sst'] as String, eventTypeDefault: json['stp_lv_art_name'] as String, eventTypeTag: json['stp_lv_art_kurz'] as String, semester: json['semester_name'] as String, @@ -28,7 +28,7 @@ LectureDetails _$LectureDetailsFromJson(Map json) => requirements: json['voraussetzung_lv'] as String?, courseObjective: json['lehrziel'] as String?, teachingMethod: json['lehrmethode'] as String?, - anmeld_lv: json['anmeld_lv'] as String?, + signUpLV: json['anmeld_lv'] as String?, firstScheduledDate: json['ersttermin'] as String?, examinationMode: json['pruefmodus'] as String?, studienbehelfe: json['studienbehelfe'] as String?, @@ -44,7 +44,7 @@ Map _$LectureDetailsToJson(LectureDetails instance) => 'stp_lv_nr': instance.lvNumber, 'stp_sp_titel': instance.title, 'dauer_info': instance.duration, - 'stp_sp_sst': instance.stp_sp_sst, + 'stp_sp_sst': instance.stpSpSst, 'stp_lv_art_name': instance.eventTypeDefault, 'stp_lv_art_kurz': instance.eventTypeTag, 'semester_name': instance.semester, @@ -59,7 +59,7 @@ Map _$LectureDetailsToJson(LectureDetails instance) => 'voraussetzung_lv': instance.requirements, 'lehrziel': instance.courseObjective, 'lehrmethode': instance.teachingMethod, - 'anmeld_lv': instance.anmeld_lv, + 'anmeld_lv': instance.signUpLV, 'ersttermin': instance.firstScheduledDate, 'pruefmodus': instance.examinationMode, 'studienbehelfe': instance.studienbehelfe,