diff --git a/lib/base/localization/app_de.arb b/lib/base/localization/app_de.arb index 88114911..f7e1ebfd 100644 --- a/lib/base/localization/app_de.arb +++ b/lib/base/localization/app_de.arb @@ -16,7 +16,7 @@ "hideFailedGrades":"Durchgefallene Noten ausblenden", "defaultMapsApplication":"Standard-Kartenanwendung", "map":"Karte", - "contactUs":"Kontaktiere uns", + "contactMore":"Kontakt & Mehr", "tokenPermissions":"Berechtigungen für Token", "permissionChangePossibleInTUMonline":"Du kannst deine Berechtigungen in TUMOnline ändern", "logout":"Abmelden", @@ -272,5 +272,7 @@ "location":"Standort", "locationOnboarding":"Aktiviere Standorte, um personalisierte Erlebnisse zu erreichen und Deinen Standort auf Raumfinder-Karten zu sehen.", "continueOnboarding":"Weiter", - "showMore":"Mehr Anzeigen" + "showMore":"Mehr Anzeigen", + "licenses":"Lizenzen", + "privacyPolicy":"Datenschutzrichtlinie" } \ No newline at end of file diff --git a/lib/base/localization/app_en.arb b/lib/base/localization/app_en.arb index e304addd..72f0ccf7 100644 --- a/lib/base/localization/app_en.arb +++ b/lib/base/localization/app_en.arb @@ -16,7 +16,7 @@ "hideFailedGrades":"Hide Failed Grades", "defaultMapsApplication":"Default Maps Application", "map":"Map", - "contactUs":"Contact Us", + "contactMore":"Contact & More", "tokenPermissions":"Token Permissions", "permissionChangePossibleInTUMonline":"You can change your permissions on TUMOnline", "logout":"Logout", @@ -272,5 +272,7 @@ "location":"Location", "locationOnboarding":"Enable locations to access personalized experiences and your location on the room finder maps.", "continueOnboarding":"Continue", - "showMore":"Show More" + "showMore":"Show More", + "licenses":"Licenses", + "privacyPolicy":"Privacy Policy" } \ No newline at end of file diff --git a/lib/settingsComponent/views/appearance_settings_view.dart b/lib/settingsComponent/views/appearance_settings_view.dart new file mode 100644 index 00000000..4b27b846 --- /dev/null +++ b/lib/settingsComponent/views/appearance_settings_view.dart @@ -0,0 +1,112 @@ +import 'dart:io'; + +import 'package:campus_flutter/base/enums/appearance.dart'; +import 'package:campus_flutter/base/extensions/context.dart'; +import 'package:campus_flutter/base/helpers/icon_text.dart'; +import 'package:campus_flutter/base/views/seperated_list.dart'; +import 'package:campus_flutter/gradeComponent/viewModels/grade_viewmodel.dart'; +import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; +import 'package:campus_flutter/main.dart'; +import 'package:campus_flutter/settingsComponent/viewModels/user_preferences_viewmodel.dart'; +import 'package:campus_flutter/settingsComponent/views/default_maps_picker_view.dart'; +import 'package:campus_flutter/settingsComponent/views/settings_view.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:map_launcher/map_launcher.dart'; + +class AppearanceSettingsView extends ConsumerWidget { + const AppearanceSettingsView({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return WidgetFrameView( + title: context.localizations.appearance, + child: Card( + child: SeparatedList.widgets( + widgets: [ + _appearanceSelection(context, ref), + if (!kIsWeb && Platform.isIOS) _useWebView(context, ref), + _hideFailedGrades(context, ref), + if (!kIsWeb && getIt.get>().isNotEmpty) + const DefaultMapsPickerView(), + ], + ), + ), + ); + } + + Widget _appearanceSelection(BuildContext context, WidgetRef ref) { + return ListTile( + dense: true, + title: Text( + context.localizations.theme, + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: DropdownButton( + onChanged: (Appearance? newAppearance) { + if (newAppearance != null) { + ref.read(appearance.notifier).state = newAppearance; + ref + .read(userPreferencesViewModel) + .saveUserPreference(UserPreference.theme, newAppearance); + } + }, + value: ref.watch(appearance), + items: Appearance.values + .map( + (e) => DropdownMenuItem( + value: e, + child: IconText( + iconData: e.icon, + iconColor: Theme.of(context).primaryColor, + label: ref.read(locale).languageCode == "de" + ? e.german + : e.english, + ), + ), + ) + .toList(), + ), + ); + } + + Widget _useWebView(BuildContext context, WidgetRef ref) { + return ListTile( + dense: true, + title: Text( + context.localizations.useWebView, + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: Switch( + value: ref.watch(useWebView), + onChanged: (showWebView) { + ref + .read(userPreferencesViewModel) + .saveUserPreference(UserPreference.webView, showWebView); + ref.read(useWebView.notifier).state = showWebView; + }, + ), + ); + } + + Widget _hideFailedGrades(BuildContext context, WidgetRef ref) { + return ListTile( + dense: true, + title: Text( + context.localizations.hideFailedGrades, + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: Switch( + value: ref.watch(hideFailedGrades), + onChanged: (value) { + ref + .read(userPreferencesViewModel) + .saveUserPreference(UserPreference.hideFailedGrades, value); + ref.read(hideFailedGrades.notifier).state = value; + ref.read(gradeViewModel).fetch(false); + }, + ), + ); + } +} diff --git a/lib/settingsComponent/views/contact_view.dart b/lib/settingsComponent/views/contact_view.dart new file mode 100644 index 00000000..f407b93a --- /dev/null +++ b/lib/settingsComponent/views/contact_view.dart @@ -0,0 +1,85 @@ +import 'dart:io'; + +import 'package:campus_flutter/base/extensions/context.dart'; +import 'package:campus_flutter/base/helpers/hyperlink_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:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class ContactView extends ConsumerWidget { + const ContactView({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return WidgetFrameView( + title: context.localizations.contactMore, + child: Card( + child: SeparatedList.widgets( + widgets: [ + if (!kIsWeb) + HyperLinkListTile( + dense: true, + link: _betaTester(), + label: context.localizations.becomeABetaTester, + ), + HyperLinkListTile( + dense: true, + link: "https://github.com/TUM-Dev", + label: context.localizations.usOnGitHub, + ), + const HyperLinkListTile( + dense: true, + link: "https://app.tum.de", + label: "TUM Dev Website", + ), + HyperLinkListTile( + dense: true, + link: "https://www.tum.app/privacy", + label: context.localizations.privacyPolicy, + ), + _licensesButton(context), + _feedbackButton(context), + ], + ), + ), + ); + } + + String _betaTester() { + if (Platform.isAndroid) { + return "https://play.google.com/store/apps/details?id=de.tum.tca_flutter"; + } else { + return "https://testflight.apple.com/join/4Ddi6f2f"; + } + } + + Widget _feedbackButton(BuildContext context) { + return ListTile( + dense: true, + title: Text( + "Feedback", + style: Theme.of(context).textTheme.bodyMedium, + ), + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const FeedbackFormScaffold(), + ), + ), + ); + } + + Widget _licensesButton(BuildContext context) { + return ListTile( + dense: true, + title: Text( + context.localizations.licenses, + style: Theme.of(context).textTheme.bodyMedium, + ), + onTap: () => showLicensePage(context: context), + ); + } +} diff --git a/lib/settingsComponent/views/general_settings_view.dart b/lib/settingsComponent/views/general_settings_view.dart new file mode 100644 index 00000000..50f03c40 --- /dev/null +++ b/lib/settingsComponent/views/general_settings_view.dart @@ -0,0 +1,75 @@ +import 'package:campus_flutter/base/extensions/context.dart'; +import 'package:campus_flutter/base/views/seperated_list.dart'; +import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_view.dart'; +import 'package:campus_flutter/loginComponent/views/permission_check_view.dart'; +import 'package:campus_flutter/base/extensions/locale_fullname.dart'; +import 'package:campus_flutter/main.dart'; +import 'package:campus_flutter/settingsComponent/viewModels/user_preferences_viewmodel.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class GeneralSettingsView extends ConsumerWidget { + const GeneralSettingsView({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return WidgetFrameView( + title: context.localizations.generalSettings, + child: Card( + child: SeparatedList.widgets( + widgets: [ + _tokenPermission(context), + _localeSelection(context, ref), + ], + ), + ), + ); + } + + Widget _tokenPermission(BuildContext context) { + return ListTile( + dense: true, + leading: Icon(Icons.key, size: 20, color: Theme.of(context).primaryColor), + title: Text( + context.localizations.tokenPermissions, + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: const Icon(Icons.arrow_forward_ios, size: 15), + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const PermissionCheckView(isSettingsView: true), + ), + ), + ); + } + + Widget _localeSelection(BuildContext context, WidgetRef ref) { + return ListTile( + dense: true, + leading: Icon( + Icons.language, + size: 20, + color: Theme.of(context).primaryColor, + ), + title: Text( + context.localizations.language, + style: Theme.of(context).textTheme.bodyMedium, + ), + trailing: DropdownButton( + onChanged: (Locale? newLocale) { + if (newLocale != null) { + ref + .read(userPreferencesViewModel) + .saveUserPreference(UserPreference.locale, newLocale); + ref.read(locale.notifier).state = newLocale; + } + }, + value: ref.watch(locale), + items: AppLocalizations.supportedLocales + .map((e) => DropdownMenuItem(value: e, child: Text(e.fullName()))) + .toList(), + ), + ); + } +} diff --git a/lib/settingsComponent/views/settings_view.dart b/lib/settingsComponent/views/settings_view.dart index 8c82307d..68eeea8b 100644 --- a/lib/settingsComponent/views/settings_view.dart +++ b/lib/settingsComponent/views/settings_view.dart @@ -1,26 +1,13 @@ -import 'dart:io'; - -import 'package:campus_flutter/base/enums/appearance.dart'; import 'package:campus_flutter/base/enums/credentials.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'; -import 'package:campus_flutter/feedbackComponent/views/feedback_form_view.dart'; -import 'package:campus_flutter/gradeComponent/viewModels/grade_viewmodel.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'; -import 'package:campus_flutter/main.dart'; -import 'package:campus_flutter/settingsComponent/viewModels/user_preferences_viewmodel.dart'; -import 'package:campus_flutter/settingsComponent/views/default_maps_picker_view.dart'; +import 'package:campus_flutter/settingsComponent/views/appearance_settings_view.dart'; +import 'package:campus_flutter/settingsComponent/views/contact_view.dart'; import 'package:campus_flutter/base/extensions/context.dart'; -import 'package:flutter/foundation.dart'; +import 'package:campus_flutter/settingsComponent/views/general_settings_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:map_launcher/map_launcher.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; final useWebView = StateProvider((ref) => true); final hideFailedGrades = StateProvider((ref) => false); @@ -32,216 +19,16 @@ class SettingsView extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return ListView( children: [ - _generalSettings(context, ref), - _appearance(context, ref), - _contact(context, ref), - _authentication(context, ref), - _versionNumber(), + const GeneralSettingsView(), + const AppearanceSettingsView(), + const ContactView(), + _authenticationButton(context, ref), + _versionNumberText(), ], ); } - Widget _generalSettings(BuildContext context, WidgetRef ref) { - return WidgetFrameView( - title: context.localizations.generalSettings, - child: Card( - child: SeparatedList.widgets( - widgets: [ - _tokenPermission(context), - _localeSelection(context, ref), - ], - ), - ), - ); - } - - Widget _tokenPermission(BuildContext context) { - return ListTile( - dense: true, - leading: Icon(Icons.key, size: 20, color: Theme.of(context).primaryColor), - title: Text( - context.localizations.tokenPermissions, - style: Theme.of(context).textTheme.bodyMedium, - ), - trailing: const Icon(Icons.arrow_forward_ios, size: 15), - onTap: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const PermissionCheckView(isSettingsView: true), - ), - ), - ); - } - - Widget _localeSelection(BuildContext context, WidgetRef ref) { - return ListTile( - dense: true, - leading: Icon( - Icons.language, - size: 20, - color: Theme.of(context).primaryColor, - ), - title: Text( - context.localizations.language, - style: Theme.of(context).textTheme.bodyMedium, - ), - trailing: DropdownButton( - onChanged: (Locale? newLocale) { - if (newLocale != null) { - ref - .read(userPreferencesViewModel) - .saveUserPreference(UserPreference.locale, newLocale); - ref.read(locale.notifier).state = newLocale; - } - }, - value: ref.watch(locale), - items: AppLocalizations.supportedLocales - .map((e) => DropdownMenuItem(value: e, child: Text(e.fullName()))) - .toList(), - ), - ); - } - - Widget _appearance(BuildContext context, WidgetRef ref) { - return WidgetFrameView( - title: context.localizations.appearance, - child: Card( - child: SeparatedList.widgets( - widgets: [ - _appearanceSelection(context, ref), - if (!kIsWeb && Platform.isIOS) _useWebView(context, ref), - _hideFailedGrades(context, ref), - if (!kIsWeb && getIt.get>().isNotEmpty) - const DefaultMapsPickerView(), - ], - ), - ), - ); - } - - Widget _appearanceSelection(BuildContext context, WidgetRef ref) { - return ListTile( - dense: true, - title: Text( - context.localizations.theme, - style: Theme.of(context).textTheme.bodyMedium, - ), - trailing: DropdownButton( - onChanged: (Appearance? newAppearance) { - if (newAppearance != null) { - ref.read(appearance.notifier).state = newAppearance; - ref - .read(userPreferencesViewModel) - .saveUserPreference(UserPreference.theme, newAppearance); - } - }, - value: ref.watch(appearance), - items: Appearance.values - .map( - (e) => DropdownMenuItem( - value: e, - child: IconText( - iconData: e.icon, - iconColor: Theme.of(context).primaryColor, - label: ref.read(locale).languageCode == "de" - ? e.german - : e.english, - ), - ), - ) - .toList(), - ), - ); - } - - Widget _useWebView(BuildContext context, WidgetRef ref) { - return ListTile( - dense: true, - title: Text( - context.localizations.useWebView, - style: Theme.of(context).textTheme.bodyMedium, - ), - trailing: Switch( - value: ref.watch(useWebView), - onChanged: (showWebView) { - ref - .read(userPreferencesViewModel) - .saveUserPreference(UserPreference.webView, showWebView); - ref.read(useWebView.notifier).state = showWebView; - }, - ), - ); - } - - Widget _hideFailedGrades(BuildContext context, WidgetRef ref) { - return ListTile( - dense: true, - title: Text( - context.localizations.hideFailedGrades, - style: Theme.of(context).textTheme.bodyMedium, - ), - trailing: Switch( - value: ref.watch(hideFailedGrades), - onChanged: (value) { - ref - .read(userPreferencesViewModel) - .saveUserPreference(UserPreference.hideFailedGrades, value); - ref.read(hideFailedGrades.notifier).state = value; - ref.read(gradeViewModel).fetch(false); - }, - ), - ); - } - - Widget _contact(BuildContext context, WidgetRef ref) { - return WidgetFrameView( - title: context.localizations.contactUs, - child: Card( - child: SeparatedList.widgets( - widgets: [ - if (!kIsWeb) - HyperLinkListTile( - dense: true, - link: _betaTester(), - label: context.localizations.becomeABetaTester, - ), - HyperLinkListTile( - dense: true, - link: "https://github.com/TUM-Dev", - label: context.localizations.usOnGitHub, - ), - const HyperLinkListTile( - dense: true, - link: "https://app.tum.de", - label: "TUM Dev Website", - ), - ListTile( - dense: true, - title: Text( - "Feedback", - style: Theme.of(context).textTheme.bodyMedium, - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const FeedbackFormScaffold(), - ), - ), - ), - ], - ), - ), - ); - } - - String _betaTester() { - if (Platform.isAndroid) { - return "https://play.google.com/store/apps/details?id=de.tum.tca_flutter"; - } else { - return "https://testflight.apple.com/join/4Ddi6f2f"; - } - } - - Widget _authentication(BuildContext context, WidgetRef ref) { + Widget _authenticationButton(BuildContext context, WidgetRef ref) { final login = ref.read(loginViewModel).credentials.value; return WidgetFrameView( child: GestureDetector( @@ -277,7 +64,7 @@ class SettingsView extends ConsumerWidget { ); } - Widget _versionNumber() { + Widget _versionNumberText() { return Center( child: FutureBuilder( future: PackageInfo.fromPlatform(), diff --git a/pubspec.lock b/pubspec.lock index 127cd8a2..060ff788 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.9" + version: "3.4.10" args: dependency: transitive description: @@ -594,7 +594,7 @@ packages: description: path: "packages/google_maps_flutter/google_maps_flutter_ios" ref: main - resolved-ref: e96708b1c189d2a36e55e70546533c5461c07f9c + resolved-ref: "18aa71f7f6e0b0ac54a81cae35e8f017a74648cf" url: "https://github.com/jakobkoerber/packages.git" source: git version: "2.3.3" @@ -635,7 +635,7 @@ packages: description: path: "." ref: master - resolved-ref: "70b1ac09ca3a725f055843fe498c7f065755c059" + resolved-ref: c4fbd6d0b0ba1c6b0b56c21329fabfc1676dbc1e url: "https://github.com/jakobkoerber/grpc-dart.git" source: git version: "3.2.4" @@ -1272,34 +1272,34 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_calendar - sha256: b31182b348742b0f2285849179490afd89e321514b8e0eadeab6d7377373c9f2 + sha256: ed737f014e870430d211f78e6db0fe6c2ec924d633982ae1e49111e56e2472be url: "https://pub.dev" source: hosted - version: "24.1.43" + version: "24.1.44" syncfusion_flutter_charts: dependency: "direct main" description: name: syncfusion_flutter_charts - sha256: d720751869540bc0b18e063414e9c8d41c924cca01e3aad3538d404c31d00e2d + sha256: "1f5434cb0dd0118a8f3281a0351559772e09066c2adfd69b16098f38f2b909e9" url: "https://pub.dev" source: hosted - version: "24.1.43+1" + version: "24.1.44+1" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core - sha256: "1b40729aa10a727150a6cc56e532c770f4baded83846fca8700efd908d0f4d0a" + sha256: "083d4a0ddb25435d267328a30c3938d09599f7578f5f0d28fbd9fd5424c1cd51" url: "https://pub.dev" source: hosted - version: "24.1.43" + version: "24.1.44" syncfusion_flutter_datepicker: dependency: "direct main" description: name: syncfusion_flutter_datepicker - sha256: "3f9a8e8b585dd992d2321c899ec8d914634fb99eba02cf7a91c48a098bf62820" + sha256: "3bad2603b77d5a65e1a324b09480865531ae8026b95e63b1b253b93c26204f0a" url: "https://pub.dev" source: hosted - version: "24.1.43" + version: "24.1.44" synchronized: dependency: transitive description: