From 7b4c4e6494338de06e17c26cbfb1f5b304bec138 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Fri, 8 Dec 2023 19:46:48 +0000 Subject: [PATCH 01/13] Bootstrap settings page and change homepage edit button style --- uni/lib/l10n/intl_en.arb | 2 + uni/lib/l10n/intl_pt_PT.arb | 2 + uni/lib/utils/drawer_items.dart | 1 + .../pages_layouts/general/general.dart | 4 +- .../general/widgets/navigation_drawer.dart | 66 ++++--------------- .../view/home/widgets/main_cards_list.dart | 10 +-- uni/lib/view/settings/settings.dart | 58 ++++++++++++++++ .../widgets/locale_switch_button.dart | 32 +++++++++ .../settings/widgets/theme_switch_button.dart | 23 +++++++ 9 files changed, 136 insertions(+), 62 deletions(-) create mode 100644 uni/lib/view/settings/settings.dart create mode 100644 uni/lib/view/settings/widgets/locale_switch_button.dart create mode 100644 uni/lib/view/settings/widgets/theme_switch_button.dart diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index c1e22210e..5252b6ea9 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -144,6 +144,8 @@ "@loading_terms": {}, "login": "Login", "@login": {}, + "settings": "Settings", + "@settings": {}, "logout": "Log out", "@logout": {}, "menus": "Menus", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 26cef1c70..f77e5d802 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -1,5 +1,7 @@ { "@@locale": "pt_PT", + "settings": "Definições", + "@settings": {}, "exit_confirm": "Tem a certeza de que pretende sair?", "@exit_confirm": {}, "no": "Não", diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index ca70697f0..b983afeab 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -15,6 +15,7 @@ enum DrawerItem { navLogOut('Terminar sessão'); const DrawerItem(this.title, {this.faculties}); + final String title; final Set? faculties; diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 0314f2eb4..313b729bd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -64,9 +64,7 @@ abstract class GeneralPageViewState extends State { ); } - Widget getBody(BuildContext context) { - return Container(); - } + Widget getBody(BuildContext context); Future buildProfileDecorationImage( BuildContext context, { diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 18572148d..911831298 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -6,8 +6,11 @@ import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/theme_notifier.dart'; +import '../../../../settings/settings.dart'; + class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); + final BuildContext parentContext; @override @@ -31,7 +34,6 @@ class AppNavigationDrawerState extends State { } } - // Callback Functions String getCurrentRoute() => ModalRoute.of(widget.parentContext)!.settings.name == null ? drawerItems.keys.toList()[0].title @@ -52,8 +54,6 @@ class AppNavigationDrawerState extends State { .pushNamedAndRemoveUntil('/$key', (Route route) => false); } - // End of Callback Functions - BoxDecoration? _getSelectionDecoration(String name) { return (name == getCurrentRoute()) ? BoxDecoration( @@ -89,52 +89,6 @@ class AppNavigationDrawerState extends State { ); } - Widget createLocaleBtn() { - return Consumer( - builder: (context, localeNotifier, _) { - return TextButton( - onPressed: () => localeNotifier.setNextLocale(), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), - ), - ); - }, - ); - } - - Widget createThemeSwitchBtn() { - Icon getThemeIcon(ThemeMode theme) { - switch (theme) { - case ThemeMode.light: - return const Icon(Icons.wb_sunny); - case ThemeMode.dark: - return const Icon(Icons.nightlight_round); - case ThemeMode.system: - return const Icon(Icons.brightness_6); - } - } - - return Consumer( - builder: (context, themeNotifier, _) { - return IconButton( - icon: getThemeIcon(themeNotifier.getTheme()), - onPressed: themeNotifier.setNextTheme, - ); - }, - ); - } - Widget createDrawerNavigationOption(DrawerItem d) { return DecoratedBox( decoration: _getSelectionDecoration(d.title) ?? const BoxDecoration(), @@ -161,7 +115,7 @@ class AppNavigationDrawerState extends State { @override Widget build(BuildContext context) { final drawerOptions = []; - final userSession = Provider.of(context).session; + final userSession = context.read().session; for (final key in drawerItems.keys) { if (key.isVisible(userSession.faculties)) { @@ -187,11 +141,15 @@ class AppNavigationDrawerState extends State { child: createLogoutBtn(), ), ), - Align( - alignment: Alignment.centerRight, - child: createLocaleBtn(), + IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const SettingsPage(), + ), + ), ), - createThemeSwitchBtn(), ], ), ], diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 241f6a2f1..1d63f27b5 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -150,7 +150,7 @@ class MainCardsList extends StatelessWidget { HomePageProvider editingModeProvider, ) { return Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 5), + padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -159,16 +159,16 @@ class MainCardsList extends StatelessWidget { center: false, pad: false, ), - GestureDetector( - onTap: () => Provider.of(context, listen: false) - .setHomePageEditingMode( + OutlinedButton( + onPressed: () => + Provider.of(context, listen: false) + .setHomePageEditingMode( editingMode: !editingModeProvider.isEditing, ), child: Text( editingModeProvider.isEditing ? S.of(context).edit_on : S.of(context).edit_off, - style: Theme.of(context).textTheme.bodySmall, ), ), ], diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart new file mode 100644 index 000000000..f767081dc --- /dev/null +++ b/uni/lib/view/settings/settings.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; + +import 'package:uni/generated/l10n.dart'; + +class SettingsPage extends StatefulWidget { + const SettingsPage({super.key}); + + @override + State createState() { + return SettingsPageState(); + } +} + +class SettingsPageState extends GeneralPageViewState { + @override + Future onRefresh(BuildContext context) async {} + + @override + Widget getBody(BuildContext context) { + return Column( + children: [ + PageTitle(name: "defs"), + const Padding(padding: EdgeInsets.only(top: 10)), + Expanded( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 20), + child: ListView( + children: [ + ListTile( + title: const Text('Language'), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.pushNamed(context, '/settings/language'); + }, + ), + ListTile( + title: const Text('Theme'), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.pushNamed(context, '/settings/theme'); + }, + ), + ListTile( + title: const Text('About'), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.pushNamed(context, '/settings/about'); + }, + ), + ], + ), + )), + ], + ); + } +} diff --git a/uni/lib/view/settings/widgets/locale_switch_button.dart b/uni/lib/view/settings/widgets/locale_switch_button.dart new file mode 100644 index 000000000..c6c40b5f8 --- /dev/null +++ b/uni/lib/view/settings/widgets/locale_switch_button.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/view/locale_notifier.dart'; + +class LocaleSwitchButton extends StatelessWidget { + const LocaleSwitchButton({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, localeNotifier, _) { + return TextButton( + onPressed: () => localeNotifier.setNextLocale(), + style: TextButton.styleFrom( + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 5), + ), + child: Container( + padding: const EdgeInsets.all(15), + child: Text( + localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: Theme.of(context).primaryColor), + ), + ), + ); + }, + ); + } +} diff --git a/uni/lib/view/settings/widgets/theme_switch_button.dart b/uni/lib/view/settings/widgets/theme_switch_button.dart new file mode 100644 index 000000000..ddabc3b69 --- /dev/null +++ b/uni/lib/view/settings/widgets/theme_switch_button.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/view/theme_notifier.dart'; + +class ThemeSwitchButton extends StatelessWidget { + const ThemeSwitchButton({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, themeNotifier, _) { + return IconButton( + icon: switch (themeNotifier.getTheme()) { + ThemeMode.light => const Icon(Icons.wb_sunny), + ThemeMode.dark => const Icon(Icons.nightlight_round), + ThemeMode.system => const Icon(Icons.brightness_6), + }, + onPressed: themeNotifier.setNextTheme, + ); + }, + ); + } +} From 230b469bc1e85807a15d5cc430a0b526ea0bbf61 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Fri, 8 Dec 2023 22:08:46 +0000 Subject: [PATCH 02/13] Finish settings page and remove drawer options --- .../local_storage/app_shared_preferences.dart | 13 ++ uni/lib/generated/intl/messages_en.dart | 15 ++- uni/lib/generated/intl/messages_pt_PT.dart | 17 ++- uni/lib/generated/l10n.dart | 88 +++++++++++++- uni/lib/l10n/intl_en.arb | 22 +++- uni/lib/l10n/intl_pt_PT.arb | 22 +++- uni/lib/main.dart | 31 +---- uni/lib/utils/drawer_items.dart | 10 +- uni/lib/view/about/about.dart | 9 +- uni/lib/view/bug_report/bug_report.dart | 9 +- uni/lib/view/bug_report/widgets/form.dart | 106 +++++++--------- .../general/widgets/navigation_drawer.dart | 50 -------- .../pages_layouts/secondary/secondary.dart | 5 + .../request_dependent_widget_builder.dart | 8 +- .../view/home/widgets/main_cards_list.dart | 32 +++-- uni/lib/view/navigation_service.dart | 7 +- uni/lib/view/profile/profile.dart | 15 ++- .../profile/widgets/account_info_card.dart | 24 ---- uni/lib/view/settings/settings.dart | 115 ++++++++++++------ .../widgets/locale_switch_button.dart | 17 +-- .../widgets/logout_confirm_dialog.dart | 28 +++++ .../widgets/notifications_dialog.dart | 24 ++++ .../settings/widgets/theme_switch_button.dart | 13 +- .../widgets/tuition_notification_switch.dart | 0 .../settings/widgets/usage_stats_switch.dart | 39 ++++++ .../src/exams_page_test.mocks.dart | 14 +++ .../src/schedule_page_test.mocks.dart | 43 ++++++- .../providers/exams_provider_test.mocks.dart | 15 +++ .../lecture_provider_test.mocks.dart | 16 +++ 29 files changed, 535 insertions(+), 272 deletions(-) create mode 100644 uni/lib/view/settings/widgets/logout_confirm_dialog.dart create mode 100644 uni/lib/view/settings/widgets/notifications_dialog.dart rename uni/lib/view/{profile => settings}/widgets/tuition_notification_switch.dart (100%) create mode 100644 uni/lib/view/settings/widgets/usage_stats_switch.dart diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index 8c458cbd2..e2b60e5d7 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -26,6 +26,7 @@ class AppSharedPreferences { static const String areTermsAndConditionsAcceptedKey = 'is_t&c_accepted'; static const String tuitionNotificationsToggleKey = 'tuition_notification_toogle'; + static const String usageStatsToggleKey = 'usage_stats_toogle'; static const String themeMode = 'theme_mode'; static const String locale = 'app_locale'; static const String favoriteCards = 'favorite_cards'; @@ -293,4 +294,16 @@ class AppSharedPreferences { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(tuitionNotificationsToggleKey, value); } + + static Future getUsageStatsToggle() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getBool(usageStatsToggleKey) ?? true; + } + + static Future setUsageStatsToggle({ + required bool value, + }) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(usageStatsToggleKey, value); + } } diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 2256f8e76..aec972737 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -36,13 +36,12 @@ class MessageLookup extends MessageLookupByLibrary { 'calendario': 'Calendar', 'biblioteca': 'Library', 'uteis': 'Utils', - 'sobre': 'About', - 'bugs': 'Bugs/Suggestions', 'other': 'Other', })}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { + "about": MessageLookupByLibrary.simpleMessage("About us"), "academic_services": MessageLookupByLibrary.simpleMessage("Academic services"), "account_card_title": @@ -80,6 +79,8 @@ class MessageLookup extends MessageLookupByLibrary { "Check your internet connection"), "class_registration": MessageLookupByLibrary.simpleMessage("Class Registration"), + "collect_usage_stats": + MessageLookupByLibrary.simpleMessage("Collect usage statistics"), "college": MessageLookupByLibrary.simpleMessage("College: "), "college_select": MessageLookupByLibrary.simpleMessage("select your college(s)"), @@ -87,6 +88,8 @@ class MessageLookup extends MessageLookupByLibrary { "configured_buses": MessageLookupByLibrary.simpleMessage("Configured Buses"), "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), + "confirm_logout": MessageLookupByLibrary.simpleMessage( + "Do you really want to log out? Your local data will be deleted and you will have to log in again."), "consent": MessageLookupByLibrary.simpleMessage( "I consent to this information being reviewed by NIAEFEUP and may be deleted at my request."), "contact": MessageLookupByLibrary.simpleMessage("Contact (optional)"), @@ -122,7 +125,7 @@ class MessageLookup extends MessageLookupByLibrary { "fee_date": MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), "fee_notification": - MessageLookupByLibrary.simpleMessage("Notify next deadline:"), + MessageLookupByLibrary.simpleMessage("Fee deadline"), "first_year_registration": MessageLookupByLibrary.simpleMessage( "Year of first registration: "), "floor": MessageLookupByLibrary.simpleMessage("Floor"), @@ -139,6 +142,7 @@ class MessageLookup extends MessageLookupByLibrary { "invalid_credentials": MessageLookupByLibrary.simpleMessage("Invalid credentials"), "keep_login": MessageLookupByLibrary.simpleMessage("Stay signed in"), + "language": MessageLookupByLibrary.simpleMessage("Language"), "last_refresh_time": m0, "last_timestamp": m1, "library_occupation": @@ -195,6 +199,7 @@ class MessageLookup extends MessageLookupByLibrary { "There are no course units to display"), "no_selected_exams": MessageLookupByLibrary.simpleMessage( "There are no exams to present"), + "notifications": MessageLookupByLibrary.simpleMessage("Notifications"), "occurrence_type": MessageLookupByLibrary.simpleMessage("Type of occurrence"), "other_links": MessageLookupByLibrary.simpleMessage("Other links"), @@ -217,6 +222,8 @@ class MessageLookup extends MessageLookupByLibrary { "Reference created successfully!"), "remove": MessageLookupByLibrary.simpleMessage("Delete"), "report_error": MessageLookupByLibrary.simpleMessage("Report error"), + "report_error_suggestion": + MessageLookupByLibrary.simpleMessage("Report error/suggestion"), "restaurant_main_page": MessageLookupByLibrary.simpleMessage( "Do you want to see your favorite restaurants in the main page?"), "room": MessageLookupByLibrary.simpleMessage("Room"), @@ -226,6 +233,7 @@ class MessageLookup extends MessageLookupByLibrary { "send": MessageLookupByLibrary.simpleMessage("Send"), "sent_error": MessageLookupByLibrary.simpleMessage( "An error occurred in sending"), + "settings": MessageLookupByLibrary.simpleMessage("Settings"), "some_error": MessageLookupByLibrary.simpleMessage("Some error!"), "stcp_stops": MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"), @@ -238,6 +246,7 @@ class MessageLookup extends MessageLookupByLibrary { "Face-to-face and telephone assistance"), "telephone": MessageLookupByLibrary.simpleMessage("Telephone"), "terms": MessageLookupByLibrary.simpleMessage("Terms and Conditions"), + "theme": MessageLookupByLibrary.simpleMessage("Theme"), "title": MessageLookupByLibrary.simpleMessage("Title"), "unavailable": MessageLookupByLibrary.simpleMessage("Unavailable"), "valid_email": diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 4e7a23288..bb6a83237 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -36,13 +36,12 @@ class MessageLookup extends MessageLookupByLibrary { 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'uteis': 'Úteis', - 'sobre': 'Sobre', - 'bugs': 'Bugs e Sugestões', 'other': 'Outros', })}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { + "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), "academic_services": MessageLookupByLibrary.simpleMessage("Serviços académicos"), "account_card_title": @@ -80,6 +79,8 @@ class MessageLookup extends MessageLookupByLibrary { "Verifica a tua ligação à internet"), "class_registration": MessageLookupByLibrary.simpleMessage("Inscrição de Turmas"), + "collect_usage_stats": MessageLookupByLibrary.simpleMessage( + "Partilhar estatísticas de uso"), "college": MessageLookupByLibrary.simpleMessage("Faculdade: "), "college_select": MessageLookupByLibrary.simpleMessage( "seleciona a(s) tua(s) faculdade(s)"), @@ -87,6 +88,8 @@ class MessageLookup extends MessageLookupByLibrary { "configured_buses": MessageLookupByLibrary.simpleMessage("Autocarros Configurados"), "confirm": MessageLookupByLibrary.simpleMessage("Confirmar"), + "confirm_logout": MessageLookupByLibrary.simpleMessage( + "Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente."), "consent": MessageLookupByLibrary.simpleMessage( "Consinto que esta informação seja revista pelo NIAEFEUP, podendo ser eliminada a meu pedido."), "contact": MessageLookupByLibrary.simpleMessage("Contacto (opcional)"), @@ -120,8 +123,8 @@ class MessageLookup extends MessageLookupByLibrary { "failed_login": MessageLookupByLibrary.simpleMessage("O login falhou"), "fee_date": MessageLookupByLibrary.simpleMessage( "Data limite próxima prestação:"), - "fee_notification": MessageLookupByLibrary.simpleMessage( - "Notificar próxima data limite:"), + "fee_notification": + MessageLookupByLibrary.simpleMessage("Data limite de propina"), "first_year_registration": MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), "floor": MessageLookupByLibrary.simpleMessage("Piso"), @@ -139,6 +142,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Credenciais inválidas"), "keep_login": MessageLookupByLibrary.simpleMessage("Manter sessão iniciada"), + "language": MessageLookupByLibrary.simpleMessage("Idioma"), "last_refresh_time": m0, "last_timestamp": m1, "library_occupation": @@ -196,6 +200,7 @@ class MessageLookup extends MessageLookupByLibrary { "Não existem cadeiras para apresentar"), "no_selected_exams": MessageLookupByLibrary.simpleMessage( "Não existem exames para apresentar"), + "notifications": MessageLookupByLibrary.simpleMessage("Notificações"), "occurrence_type": MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), "other_links": MessageLookupByLibrary.simpleMessage("Outros links"), @@ -218,6 +223,8 @@ class MessageLookup extends MessageLookupByLibrary { "Referência criada com sucesso!"), "remove": MessageLookupByLibrary.simpleMessage("Remover"), "report_error": MessageLookupByLibrary.simpleMessage("Reportar erro"), + "report_error_suggestion": + MessageLookupByLibrary.simpleMessage("Reportar erro/sugestão"), "restaurant_main_page": MessageLookupByLibrary.simpleMessage( "Queres ver os teus restaurantes favoritos na página principal?"), "room": MessageLookupByLibrary.simpleMessage("Sala"), @@ -227,6 +234,7 @@ class MessageLookup extends MessageLookupByLibrary { "send": MessageLookupByLibrary.simpleMessage("Enviar"), "sent_error": MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"), + "settings": MessageLookupByLibrary.simpleMessage("Definições"), "some_error": MessageLookupByLibrary.simpleMessage("Algum erro!"), "stcp_stops": MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"), @@ -239,6 +247,7 @@ class MessageLookup extends MessageLookupByLibrary { "Atendimento presencial e telefónico"), "telephone": MessageLookupByLibrary.simpleMessage("Telefone"), "terms": MessageLookupByLibrary.simpleMessage("Termos e Condições"), + "theme": MessageLookupByLibrary.simpleMessage("Tema"), "title": MessageLookupByLibrary.simpleMessage("Título"), "unavailable": MessageLookupByLibrary.simpleMessage("Indisponível"), "valid_email": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index c73c45a2d..deaa16010 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -80,6 +80,56 @@ class S { ); } + /// `About us` + String get about { + return Intl.message( + 'About us', + name: 'about', + desc: '', + args: [], + ); + } + + /// `Report error/suggestion` + String get report_error_suggestion { + return Intl.message( + 'Report error/suggestion', + name: 'report_error_suggestion', + desc: '', + args: [], + ); + } + + /// `Language` + String get language { + return Intl.message( + 'Language', + name: 'language', + desc: '', + args: [], + ); + } + + /// `Theme` + String get theme { + return Intl.message( + 'Theme', + name: 'theme', + desc: '', + args: [], + ); + } + + /// `Notifications` + String get notifications { + return Intl.message( + 'Notifications', + name: 'notifications', + desc: '', + args: [], + ); + } + /// `Academic services` String get academic_services { return Intl.message( @@ -560,10 +610,10 @@ class S { ); } - /// `Notify next deadline:` + /// `Fee deadline` String get fee_notification { return Intl.message( - 'Notify next deadline:', + 'Fee deadline', name: 'fee_notification', desc: '', args: [], @@ -733,6 +783,16 @@ class S { ); } + /// `Settings` + String get settings { + return Intl.message( + 'Settings', + name: 'settings', + desc: '', + args: [], + ); + } + /// `Log out` String get logout { return Intl.message( @@ -773,7 +833,7 @@ class S { ); } - /// `{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} sobre{About} bugs{Bugs/Suggestions} other{Other}}` + /// `{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} other{Other}}` String nav_title(Object title) { return Intl.select( title, @@ -788,8 +848,6 @@ class S { 'calendario': 'Calendar', 'biblioteca': 'Library', 'uteis': 'Utils', - 'sobre': 'About', - 'bugs': 'Bugs/Suggestions', 'other': 'Other', }, name: 'nav_title', @@ -1347,6 +1405,26 @@ class S { args: [], ); } + + /// `Do you really want to log out? Your local data will be deleted and you will have to log in again.` + String get confirm_logout { + return Intl.message( + 'Do you really want to log out? Your local data will be deleted and you will have to log in again.', + name: 'confirm_logout', + desc: '', + args: [], + ); + } + + /// `Collect usage statistics` + String get collect_usage_stats { + return Intl.message( + 'Collect usage statistics', + name: 'collect_usage_stats', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 5252b6ea9..501c6d1d8 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -6,6 +6,16 @@ "@no": {}, "yes": "Yes", "@yes": {}, + "about": "About us", + "@about": {}, + "report_error_suggestion": "Report error/suggestion", + "@report_error_suggestion": {}, + "language": "Language", + "@language": {}, + "theme": "Theme", + "@theme": {}, + "notifications": "Notifications", + "@notifications": {}, "academic_services": "Academic services", "@academic_services": {}, "account_card_title": "Checking account", @@ -102,7 +112,7 @@ "@failed_login": {}, "fee_date": "Deadline for next fee:", "@fee_date": {}, - "fee_notification": "Notify next deadline:", + "fee_notification": "Fee deadline", "@fee_notification": {}, "first_year_registration": "Year of first registration: ", "@first_year_registration": {}, @@ -145,7 +155,7 @@ "login": "Login", "@login": {}, "settings": "Settings", - "@settings": {}, + "@settings": {}, "logout": "Log out", "@logout": {}, "menus": "Menus", @@ -154,7 +164,7 @@ "@min_value_reference": {}, "multimedia_center": "Multimedia center", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} sobre{About} bugs{Bugs/Suggestions} other{Other}}", + "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} other{Other}}", "@nav_title": {}, "news": "News", "@news": {}, @@ -265,5 +275,9 @@ "widget_prompt": "Choose a widget to add to your personal area:", "@widget_prompt": {}, "year": "Year", - "@year": {} + "@year": {}, + "confirm_logout": "Do you really want to log out? Your local data will be deleted and you will have to log in again.", + "@confirm_logout": {}, + "collect_usage_stats": "Collect usage statistics", + "@usage_stats": {} } \ No newline at end of file diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index f77e5d802..5e42488f3 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -1,13 +1,23 @@ { "@@locale": "pt_PT", "settings": "Definições", - "@settings": {}, + "@settings": {}, "exit_confirm": "Tem a certeza de que pretende sair?", "@exit_confirm": {}, + "about": "Sobre nós", + "@about": {}, "no": "Não", "@no": {}, "yes": "Sim", "@yes": {}, + "report_error_suggestion": "Reportar erro/sugestão", + "@report_error_suggestion": {}, + "language": "Idioma", + "@language": {}, + "theme": "Tema", + "@theme": {}, + "notifications": "Notificações", + "@notifications": {}, "academic_services": "Serviços académicos", "@academic_services": {}, "account_card_title": "Conta Corrente", @@ -104,7 +114,7 @@ "@failed_login": {}, "fee_date": "Data limite próxima prestação:", "@fee_date": {}, - "fee_notification": "Notificar próxima data limite:", + "fee_notification": "Data limite de propina", "@fee_notification": {}, "first_year_registration": "Ano da primeira inscrição: ", "@first_year_registration": {}, @@ -154,7 +164,7 @@ "@min_value_reference": {}, "multimedia_center": "Centro de multimédia", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} uteis{Úteis} sobre{Sobre} bugs{Bugs e Sugestões} other{Outros}}", + "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} uteis{Úteis} other{Outros}}", "@nav_title": {}, "news": "Notícias", "@news": {}, @@ -265,5 +275,9 @@ "widget_prompt": "Escolhe um widget para adicionares à tua área pessoal:", "@widget_prompt": {}, "year": "Ano", - "@year": {} + "@year": {}, + "confirm_logout": "Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente.", + "@confirm_logout": {}, + "collect_usage_stats": "Partilhar estatísticas de uso", + "@collect_usage_stats": {} } \ No newline at end of file diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 01fac7edd..d77c467c3 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -26,8 +26,6 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_providers.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/about/about.dart'; -import 'package:uni/view/bug_report/bug_report.dart'; import 'package:uni/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart'; import 'package:uni/view/calendar/calendar.dart'; import 'package:uni/view/common_widgets/page_transition.dart'; @@ -38,7 +36,6 @@ import 'package:uni/view/library/library.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/locations/locations.dart'; import 'package:uni/view/login/login.dart'; -import 'package:uni/view/navigation_service.dart'; import 'package:uni/view/restaurant/restaurant_page_view.dart'; import 'package:uni/view/schedule/schedule.dart'; import 'package:uni/view/theme.dart'; @@ -50,15 +47,15 @@ SentryEvent? beforeSend(SentryEvent event) { return event.level == SentryLevel.info ? event : null; } -Future firstRoute() async { +Future firstRoute() async { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); if (userPersistentInfo != null) { - return '/${DrawerItem.navPersonalArea.title}'; + return const HomePageView(); } await acceptTermsAndConditions(); - return '/${DrawerItem.navLogIn.title}'; + return const LoginPageView(); } Future main() async { @@ -164,9 +161,9 @@ Future main() async { /// This class is necessary to track the app's state for /// the current execution. class Application extends StatefulWidget { - const Application(this.initialRoute, {super.key}); + const Application(this.initialWidget, {super.key}); - final String initialRoute; + final Widget initialWidget; static GlobalKey navigatorKey = GlobalKey(); @@ -196,7 +193,7 @@ class ApplicationState extends State { GlobalCupertinoLocalizations.delegate, ], supportedLocales: S.delegate.supportedLocales, - initialRoute: widget.initialRoute, + home: widget.initialWidget, onGenerateRoute: (RouteSettings settings) { final transitions = { '/${DrawerItem.navPersonalArea.title}': @@ -247,22 +244,6 @@ class ApplicationState extends State { page: const UsefulInfoPageView(), settings: settings, ), - '/${DrawerItem.navAbout.title}': PageTransition.makePageTransition( - page: const AboutPageView(), - settings: settings, - ), - '/${DrawerItem.navBugReport.title}': - PageTransition.makePageTransition( - page: const BugReportPageView(), - settings: settings, - maintainState: false, - ), - '/${DrawerItem.navLogIn.title}': PageTransition.makePageTransition( - page: const LoginPageView(), - settings: settings, - ), - '/${DrawerItem.navLogOut.title}': - NavigationService.buildLogoutRoute(), }; return transitions[settings.name]; }, diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index b983afeab..09d1e6f7d 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -8,11 +8,7 @@ enum DrawerItem { navRestaurants('restaurantes'), navCalendar('calendario'), navLibrary('biblioteca', faculties: {'feup'}), - navUsefulInfo('uteis', faculties: {'feup'}), - navAbout('sobre'), - navBugReport('bugs'), - navLogIn('Iniciar sessão'), - navLogOut('Terminar sessão'); + navUsefulInfo('uteis', faculties: {'feup'}); const DrawerItem(this.title, {this.faculties}); @@ -20,10 +16,6 @@ enum DrawerItem { final Set? faculties; bool isVisible(List userFaculties) { - if (this == DrawerItem.navLogIn || this == DrawerItem.navLogOut) { - return false; - } - if (faculties == null) { return true; } diff --git a/uni/lib/view/about/about.dart b/uni/lib/view/about/about.dart index af9f90eb7..513ff4a98 100644 --- a/uni/lib/view/about/about.dart +++ b/uni/lib/view/about/about.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:uni/view/about/widgets/terms_and_conditions.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; class AboutPageView extends StatefulWidget { const AboutPageView({super.key}); @@ -11,7 +11,7 @@ class AboutPageView extends StatefulWidget { } /// Manages the 'about' section of the app. -class AboutPageViewState extends GeneralPageViewState { +class AboutPageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { final queryData = MediaQuery.of(context); @@ -37,4 +37,9 @@ class AboutPageViewState extends GeneralPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + Widget getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/bug_report/bug_report.dart b/uni/lib/view/bug_report/bug_report.dart index f9f7a5c82..b04005058 100644 --- a/uni/lib/view/bug_report/bug_report.dart +++ b/uni/lib/view/bug_report/bug_report.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:uni/view/bug_report/widgets/form.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; class BugReportPageView extends StatefulWidget { const BugReportPageView({super.key}); @@ -11,7 +11,7 @@ class BugReportPageView extends StatefulWidget { } /// Manages the 'Bugs and sugestions' section of the app. -class BugReportPageViewState extends GeneralPageViewState { +class BugReportPageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return Container( @@ -22,4 +22,9 @@ class BugReportPageViewState extends GeneralPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + Container getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/bug_report/widgets/form.dart b/uni/lib/view/bug_report/widgets/form.dart index 18ec82a16..672d7d8b3 100644 --- a/uni/lib/view/bug_report/widgets/form.dart +++ b/uni/lib/view/bug_report/widgets/form.dart @@ -8,7 +8,6 @@ import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/bug_report.dart'; -import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/bug_report/widgets/text_field.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/toast_message.dart'; @@ -81,69 +80,52 @@ class BugReportFormState extends State { Widget build(BuildContext context) { return Form( key: _formKey, - child: ListView(children: getFormWidget(context)), - ); - } - - List getFormWidget(BuildContext context) { - return [ - bugReportTitle(context), - bugReportIntro(context), - dropdownBugSelectWidget(context), - FormTextField( - titleController, - Icons.title, - maxLines: 2, - description: S.of(context).title, - labelText: S.of(context).problem_id, - bottomMargin: 30, - ), - FormTextField( - descriptionController, - Icons.description, - maxLines: 30, - description: S.of(context).description, - labelText: S.of(context).bug_description, - bottomMargin: 30, - ), - FormTextField( - emailController, - Icons.mail, - maxLines: 2, - description: S.of(context).contact, - labelText: S.of(context).desired_email, - bottomMargin: 30, - isOptional: true, - formatValidator: (String? value) { - if (value == null || value.isEmpty) { - return null; - } - - return EmailValidator.validate(value) - ? null - : S.of(context).valid_email; - }, - ), - consentBox(context), - submitButton(context), - ]; - } - - /// Returns a widget for the title of the bug report form - Widget bugReportTitle(BuildContext context) { - return Container( - margin: const EdgeInsets.symmetric(vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Icon(Icons.bug_report, size: 40), + child: ListView( + children: [ + const Padding(padding: EdgeInsets.only(bottom: 10)), PageTitle( - name: S.of(context).nav_title( - DrawerItem.navBugReport.title, - ), - center: false, + name: S.of(context).report_error_suggestion, + pad: false, + ), + const Padding(padding: EdgeInsets.only(bottom: 10)), + bugReportIntro(context), + dropdownBugSelectWidget(context), + FormTextField( + titleController, + Icons.title, + maxLines: 2, + description: S.of(context).title, + labelText: S.of(context).problem_id, + bottomMargin: 30, + ), + FormTextField( + descriptionController, + Icons.description, + maxLines: 30, + description: S.of(context).description, + labelText: S.of(context).bug_description, + bottomMargin: 30, + ), + FormTextField( + emailController, + Icons.mail, + maxLines: 2, + description: S.of(context).contact, + labelText: S.of(context).desired_email, + bottomMargin: 30, + isOptional: true, + formatValidator: (String? value) { + if (value == null || value.isEmpty) { + return null; + } + + return EmailValidator.validate(value) + ? null + : S.of(context).valid_email; + }, ), - const Icon(Icons.bug_report, size: 40), + consentBox(context), + submitButton(context), ], ), ); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 911831298..ad5fd1d74 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -3,10 +3,6 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/locale_notifier.dart'; -import 'package:uni/view/theme_notifier.dart'; - -import '../../../../settings/settings.dart'; class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); @@ -41,19 +37,12 @@ class AppNavigationDrawerState extends State { void _onSelectPage(String key) { final prev = getCurrentRoute(); - Navigator.of(context).pop(); - if (prev != key) { Navigator.pushNamed(context, '/$key'); } } - void _onLogOut(String key) { - Navigator.of(context) - .pushNamedAndRemoveUntil('/$key', (Route route) => false); - } - BoxDecoration? _getSelectionDecoration(String name) { return (name == getCurrentRoute()) ? BoxDecoration( @@ -68,27 +57,6 @@ class AppNavigationDrawerState extends State { : null; } - Widget createLogoutBtn() { - final logOutText = DrawerItem.navLogOut.title; - return TextButton( - onPressed: () => _onLogOut(logOutText), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - S.of(context).logout, - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), - ), - ); - } - Widget createDrawerNavigationOption(DrawerItem d) { return DecoratedBox( decoration: _getSelectionDecoration(d.title) ?? const BoxDecoration(), @@ -134,24 +102,6 @@ class AppNavigationDrawerState extends State { ), ), ), - Row( - children: [ - Expanded( - child: Align( - child: createLogoutBtn(), - ), - ), - IconButton( - icon: const Icon(Icons.settings), - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const SettingsPage(), - ), - ), - ), - ], - ), ], ), ); diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 473fb3299..9266ffc0c 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -11,4 +11,9 @@ abstract class SecondaryPageViewState body: refreshState(context, body), ); } + + @override + Widget getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart index 5efd0dfe6..924320dd1 100644 --- a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart +++ b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/request_status.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; /// Wraps content given its fetch data from the redux store, /// hydrating the component, displaying an empty message, @@ -93,9 +93,11 @@ class RequestDependentWidgetBuilder extends StatelessWidget { ), ), OutlinedButton( - onPressed: () => Navigator.pushNamed( + onPressed: () => Navigator.push( context, - '/${DrawerItem.navBugReport.title}', + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), ), child: Text(S.of(context).report_error), ), diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 1d63f27b5..2eede80c4 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -159,18 +159,28 @@ class MainCardsList extends StatelessWidget { center: false, pad: false, ), - OutlinedButton( - onPressed: () => - Provider.of(context, listen: false) - .setHomePageEditingMode( - editingMode: !editingModeProvider.isEditing, - ), - child: Text( - editingModeProvider.isEditing - ? S.of(context).edit_on - : S.of(context).edit_off, + if (editingModeProvider.isEditing) + ElevatedButton( + onPressed: () => + Provider.of(context, listen: false) + .setHomePageEditingMode( + editingMode: false, + ), + child: Text( + S.of(context).edit_on, + ), + ) + else + OutlinedButton( + onPressed: () => + Provider.of(context, listen: false) + .setHomePageEditingMode( + editingMode: true, + ), + child: Text( + S.of(context).edit_off, + ), ), - ), ], ), ); diff --git a/uni/lib/view/navigation_service.dart b/uni/lib/view/navigation_service.dart index 903f5410e..e896d904d 100644 --- a/uni/lib/view/navigation_service.dart +++ b/uni/lib/view/navigation_service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:uni/controller/cleanup.dart'; import 'package:uni/main.dart'; -import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/login/login.dart'; /// Manages the navigation logic @@ -13,9 +12,11 @@ class NavigationService { unawaited(cleanupStoredData(context)); - Navigator.pushNamedAndRemoveUntil( + Navigator.pushAndRemoveUntil( context, - '/${DrawerItem.navLogIn.title}', + MaterialPageRoute( + builder: (context) => const LoginPageView(), + ), (route) => false, ); } diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 6e23db12a..4ae9bc379 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -7,6 +7,7 @@ import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; import 'package:uni/view/profile/widgets/course_info_card.dart'; import 'package:uni/view/profile/widgets/profile_overview.dart'; +import 'package:uni/view/settings/settings.dart'; class ProfilePageView extends StatefulWidget { const ProfilePageView({super.key}); @@ -35,6 +36,7 @@ class ProfilePageViewState extends SecondaryPageViewState { return ListView( children: [ const Padding(padding: EdgeInsets.all(5)), + const Padding(padding: EdgeInsets.all(10)), ProfileOverview( profile: profile, getProfileDecorationImage: getProfileDecorationImage, @@ -52,7 +54,18 @@ class ProfilePageViewState extends SecondaryPageViewState { @override Widget getTopRightButton(BuildContext context) { - return Container(); + return Container( + padding: const EdgeInsets.fromLTRB(0, 10, 20, 10), + child: IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const SettingsPage(), + ), + ), + ), + ); } @override diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index 9e5162fa3..31fe34b0f 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -8,7 +8,6 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/reference_section.dart'; -import 'package:uni/view/profile/widgets/tuition_notification_switch.dart'; /// Manages the 'Current account' section inside the user's page (accessible /// through the top-right widget with the user picture) @@ -95,29 +94,6 @@ class AccountInfoCard extends GenericCard { ), ], ), - TableRow( - children: [ - Container( - margin: const EdgeInsets.only( - top: 8, - bottom: 20, - left: 20, - ), - child: Text( - S.of(context).fee_notification, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - Container( - margin: const EdgeInsets.only( - top: 8, - bottom: 20, - left: 20, - ), - child: const TuitionNotificationSwitch(), - ), - ], - ), ], ), Container( diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart index f767081dc..8360b80d2 100644 --- a/uni/lib/view/settings/settings.dart +++ b/uni/lib/view/settings/settings.dart @@ -1,8 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; - import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/about/about.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; +import 'package:uni/view/settings/widgets/locale_switch_button.dart'; +import 'package:uni/view/settings/widgets/logout_confirm_dialog.dart'; +import 'package:uni/view/settings/widgets/notifications_dialog.dart'; +import 'package:uni/view/settings/widgets/theme_switch_button.dart'; +import 'package:uni/view/settings/widgets/usage_stats_switch.dart'; class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); @@ -13,46 +19,85 @@ class SettingsPage extends StatefulWidget { } } -class SettingsPageState extends GeneralPageViewState { - @override - Future onRefresh(BuildContext context) async {} - +class SettingsPageState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return Column( children: [ - PageTitle(name: "defs"), + PageTitle(name: S.of(context).settings), const Padding(padding: EdgeInsets.only(top: 10)), Expanded( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 20), - child: ListView( - children: [ - ListTile( - title: const Text('Language'), - trailing: const Icon(Icons.arrow_forward_ios), - onTap: () { - Navigator.pushNamed(context, '/settings/language'); - }, - ), - ListTile( - title: const Text('Theme'), - trailing: const Icon(Icons.arrow_forward_ios), - onTap: () { - Navigator.pushNamed(context, '/settings/theme'); - }, - ), - ListTile( - title: const Text('About'), - trailing: const Icon(Icons.arrow_forward_ios), - onTap: () { - Navigator.pushNamed(context, '/settings/about'); - }, - ), - ], + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ListView( + children: [ + ListTile( + title: Text(S.of(context).language), + trailing: const LocaleSwitchButton(), + ), + ListTile( + title: Text(S.of(context).theme), + trailing: const ThemeSwitchButton(), + ), + ListTile( + title: Text(S.of(context).collect_usage_stats), + trailing: const UsageStatsSwitch(), + ), + ListTile( + title: Text(S.of(context).notifications), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + showDialog( + context: context, + builder: (context) => const NotificationsDialog(), + ); + }, + ), + ListTile( + title: Text(S.of(context).report_error_suggestion), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), + ); + }, + ), + ListTile( + title: Text(S.of(context).about), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AboutPageView(), + ), + ); + }, + ), + ListTile( + title: Text(S.of(context).logout), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () => showDialog( + context: context, + builder: (context) => const LogoutConfirmDialog(), + ), + ), + ], + ), ), - )), + ), ], ); } + + @override + Future onRefresh(BuildContext context) async {} + + @override + Widget getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/settings/widgets/locale_switch_button.dart b/uni/lib/view/settings/widgets/locale_switch_button.dart index c6c40b5f8..5b1963cbb 100644 --- a/uni/lib/view/settings/widgets/locale_switch_button.dart +++ b/uni/lib/view/settings/widgets/locale_switch_button.dart @@ -9,21 +9,10 @@ class LocaleSwitchButton extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, localeNotifier, _) { - return TextButton( + return ElevatedButton( onPressed: () => localeNotifier.setNextLocale(), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), + child: Text( + localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), ), ); }, diff --git a/uni/lib/view/settings/widgets/logout_confirm_dialog.dart b/uni/lib/view/settings/widgets/logout_confirm_dialog.dart new file mode 100644 index 000000000..2f632886f --- /dev/null +++ b/uni/lib/view/settings/widgets/logout_confirm_dialog.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/navigation_service.dart'; + +class LogoutConfirmDialog extends StatelessWidget { + const LogoutConfirmDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).logout), + content: Text(S.of(context).confirm_logout), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(S.of(context).no), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + NavigationService.logoutAndPopHistory(); + }, + child: Text(S.of(context).yes), + ), + ], + ); + } +} diff --git a/uni/lib/view/settings/widgets/notifications_dialog.dart b/uni/lib/view/settings/widgets/notifications_dialog.dart new file mode 100644 index 000000000..f7bea693d --- /dev/null +++ b/uni/lib/view/settings/widgets/notifications_dialog.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/settings/widgets/tuition_notification_switch.dart'; + +class NotificationsDialog extends StatelessWidget { + const NotificationsDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).notifications), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text(S.of(context).fee_notification), + trailing: const TuitionNotificationSwitch(), + ), + ], + ), + ); + } +} diff --git a/uni/lib/view/settings/widgets/theme_switch_button.dart b/uni/lib/view/settings/widgets/theme_switch_button.dart index ddabc3b69..21537c75c 100644 --- a/uni/lib/view/settings/widgets/theme_switch_button.dart +++ b/uni/lib/view/settings/widgets/theme_switch_button.dart @@ -9,13 +9,14 @@ class ThemeSwitchButton extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, themeNotifier, _) { - return IconButton( - icon: switch (themeNotifier.getTheme()) { - ThemeMode.light => const Icon(Icons.wb_sunny), - ThemeMode.dark => const Icon(Icons.nightlight_round), - ThemeMode.system => const Icon(Icons.brightness_6), - }, + final icon = switch (themeNotifier.getTheme()) { + ThemeMode.light => const Icon(Icons.wb_sunny), + ThemeMode.dark => const Icon(Icons.nightlight_round), + ThemeMode.system => const Icon(Icons.brightness_6), + }; + return ElevatedButton( onPressed: themeNotifier.setNextTheme, + child: icon, ); }, ); diff --git a/uni/lib/view/profile/widgets/tuition_notification_switch.dart b/uni/lib/view/settings/widgets/tuition_notification_switch.dart similarity index 100% rename from uni/lib/view/profile/widgets/tuition_notification_switch.dart rename to uni/lib/view/settings/widgets/tuition_notification_switch.dart diff --git a/uni/lib/view/settings/widgets/usage_stats_switch.dart b/uni/lib/view/settings/widgets/usage_stats_switch.dart new file mode 100644 index 000000000..54d1baa5b --- /dev/null +++ b/uni/lib/view/settings/widgets/usage_stats_switch.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:uni/controller/local_storage/app_shared_preferences.dart'; + +class UsageStatsSwitch extends StatefulWidget { + const UsageStatsSwitch({super.key}); + + @override + State createState() => _UsageStatsSwitchState(); +} + +class _UsageStatsSwitchState extends State { + bool usageStatsToggle = true; + + @override + void initState() { + super.initState(); + getUsageStatsToggle(); + } + + Future getUsageStatsToggle() async { + await AppSharedPreferences.getUsageStatsToggle() + .then((value) => setState(() => usageStatsToggle = value)); + } + + Future saveUsageStatsToggle({required bool value}) async { + await AppSharedPreferences.setUsageStatsToggle(value: value); + setState(() { + usageStatsToggle = value; + }); + } + + @override + Widget build(BuildContext context) { + return Switch.adaptive( + value: usageStatsToggle, + onChanged: (value) => saveUsageStatsToggle(value: value), + ); + } +} diff --git a/uni/test/mocks/integration/src/exams_page_test.mocks.dart b/uni/test/mocks/integration/src/exams_page_test.mocks.dart index e28b49f59..40a40fedc 100644 --- a/uni/test/mocks/integration/src/exams_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/exams_page_test.mocks.dart @@ -77,6 +77,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> get( Uri? url, { @@ -106,6 +107,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post( Uri? url, { @@ -149,6 +151,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> put( Uri? url, { @@ -192,6 +195,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -235,6 +239,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -278,6 +283,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future read( Uri? url, { @@ -292,6 +298,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); + @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -307,6 +314,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); + @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -331,6 +339,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -351,30 +360,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index e5e3b4548..e03c840a2 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -93,6 +93,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> get( Uri? url, { @@ -122,6 +123,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> post( Uri? url, { @@ -165,6 +167,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> put( Uri? url, { @@ -208,6 +211,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> patch( Uri? url, { @@ -251,6 +255,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> delete( Uri? url, { @@ -294,6 +299,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future read( Uri? url, { @@ -308,6 +314,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); + @override _i4.Future<_i6.Uint8List> readBytes( Uri? url, { @@ -323,6 +330,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), ) as _i4.Future<_i6.Uint8List>); + @override _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -347,6 +355,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -367,30 +376,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i6.Uint8List(0), returnValueForMissingStub: _i6.Uint8List(0), ) as _i6.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), @@ -415,12 +429,14 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { Invocation.getter(#session), ), ) as _i3.Session); + @override bool get dependsOnSession => (super.noSuchMethod( Invocation.getter(#dependsOnSession), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override set dependsOnSession(bool? _dependsOnSession) => super.noSuchMethod( Invocation.setter( @@ -429,6 +445,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override set cacheDuration(Duration? _cacheDuration) => super.noSuchMethod( Invocation.setter( @@ -437,18 +454,21 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override _i8.RequestStatus get status => (super.noSuchMethod( Invocation.getter(#status), returnValue: _i8.RequestStatus.none, returnValueForMissingStub: _i8.RequestStatus.none, ) as _i8.RequestStatus); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override _i4.Future loadFromStorage() => (super.noSuchMethod( Invocation.method( @@ -458,6 +478,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future loadFromRemote( _i3.Session? session, @@ -474,6 +495,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void restoreSession( String? username, @@ -491,6 +513,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override _i4.Future postAuthentication( _i10.BuildContext? context, @@ -513,22 +536,25 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override - void markAsNotInitialized() => super.noSuchMethod( + void markAsInitialized() => super.noSuchMethod( Invocation.method( - #markAsNotInitialized, + #markAsInitialized, [], ), returnValueForMissingStub: null, ); + @override - void updateStatus(_i8.RequestStatus? status) => super.noSuchMethod( + void markAsNotInitialized() => super.noSuchMethod( Invocation.method( - #updateStatus, - [status], + #markAsNotInitialized, + [], ), returnValueForMissingStub: null, ); + @override _i4.Future forceRefresh(_i10.BuildContext? context) => (super.noSuchMethod( @@ -539,6 +565,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitialized(_i10.BuildContext? context) => (super.noSuchMethod( @@ -549,6 +576,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitializedFromRemote(_i10.BuildContext? context) => (super.noSuchMethod( @@ -559,6 +587,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitializedFromStorage() => (super.noSuchMethod( Invocation.method( @@ -568,6 +597,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -576,6 +606,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -584,6 +615,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -592,6 +624,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart index fd78853db..9f2e5d8ed 100644 --- a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart @@ -80,6 +80,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> get( Uri? url, { @@ -109,6 +110,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post( Uri? url, { @@ -152,6 +154,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> put( Uri? url, { @@ -195,6 +198,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -238,6 +242,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -281,6 +286,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future read( Uri? url, { @@ -295,6 +301,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); + @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -310,6 +317,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); + @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -334,6 +342,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -357,6 +366,7 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { returnValue: '', returnValueForMissingStub: '', ) as String); + @override _i3.Future> parseExams( _i2.Response? response, @@ -386,30 +396,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart index 3fd2208a6..49010fdd7 100644 --- a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart @@ -80,6 +80,7 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { returnValueForMissingStub: _i4.Future>.value(<_i5.Lecture>[]), ) as _i4.Future>); + @override _i2.Dates getDates() => (super.noSuchMethod( Invocation.method( @@ -101,6 +102,7 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { ), ), ) as _i2.Dates); + @override List getEndpoints(_i6.Session? session) => (super.noSuchMethod( Invocation.method( @@ -145,6 +147,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> get( Uri? url, { @@ -174,6 +177,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> post( Uri? url, { @@ -217,6 +221,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> put( Uri? url, { @@ -260,6 +265,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> patch( Uri? url, { @@ -303,6 +309,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> delete( Uri? url, { @@ -346,6 +353,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future read( Uri? url, { @@ -360,6 +368,7 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); + @override _i4.Future<_i9.Uint8List> readBytes( Uri? url, { @@ -375,6 +384,7 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValueForMissingStub: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), ) as _i4.Future<_i9.Uint8List>); + @override _i4.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => (super.noSuchMethod( @@ -399,6 +409,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -419,30 +430,35 @@ class MockResponse extends _i1.Mock implements _i3.Response { returnValue: _i9.Uint8List(0), returnValueForMissingStub: _i9.Uint8List(0), ) as _i9.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), From 3d1073de30405990c566d617dadd708de38e3f8c Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Fri, 8 Dec 2023 22:27:53 +0000 Subject: [PATCH 03/13] Allow opening drawer via left swipe or logo tap --- .../pages_layouts/general/general.dart | 47 ++++++++++++------- .../src/exams_page_test.mocks.dart | 14 ------ .../src/schedule_page_test.mocks.dart | 43 ++--------------- .../providers/exams_provider_test.mocks.dart | 15 ------ .../lecture_provider_test.mocks.dart | 16 ------- 5 files changed, 35 insertions(+), 100 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 313b729bd..bf601badd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -101,7 +101,16 @@ abstract class GeneralPageViewState extends State { Provider.of(context, listen: false).session, forceRetrieval: true, ).then((value) => onRefresh(context)), - child: child, + child: Builder( + builder: (context) => GestureDetector( + onHorizontalDragEnd: (dragDetails) { + if (dragDetails.primaryVelocity! > 2) { + Scaffold.of(context).openDrawer(); + } + }, + child: child, + ), + ), ); } @@ -136,23 +145,27 @@ abstract class GeneralPageViewState extends State { title: ButtonTheme( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const RoundedRectangleBorder(), - child: TextButton( - onPressed: () { - final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != DrawerItem.navPersonalArea.title) { - Navigator.pushNamed( - context, - '/${DrawerItem.navPersonalArea.title}', - ); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, + child: Builder( + builder: (context) => TextButton( + onPressed: () { + final currentRouteName = ModalRoute.of(context)!.settings.name; + if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { + Navigator.pushNamed( + context, + '/${DrawerItem.navPersonalArea.title}', + ); + } else { + Scaffold.of(context).openDrawer(); + } + }, + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, + ), + 'assets/images/logo_dark.svg', + height: queryData.size.height / 25, ), - 'assets/images/logo_dark.svg', - height: queryData.size.height / 25, ), ), ), diff --git a/uni/test/mocks/integration/src/exams_page_test.mocks.dart b/uni/test/mocks/integration/src/exams_page_test.mocks.dart index 40a40fedc..e28b49f59 100644 --- a/uni/test/mocks/integration/src/exams_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/exams_page_test.mocks.dart @@ -77,7 +77,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> get( Uri? url, { @@ -107,7 +106,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> post( Uri? url, { @@ -151,7 +149,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> put( Uri? url, { @@ -195,7 +192,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -239,7 +235,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -283,7 +278,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future read( Uri? url, { @@ -298,7 +292,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); - @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -314,7 +307,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); - @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -339,7 +331,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -360,35 +351,30 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index e03c840a2..e5e3b4548 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -93,7 +93,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> get( Uri? url, { @@ -123,7 +122,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> post( Uri? url, { @@ -167,7 +165,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> put( Uri? url, { @@ -211,7 +208,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> patch( Uri? url, { @@ -255,7 +251,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> delete( Uri? url, { @@ -299,7 +294,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future read( Uri? url, { @@ -314,7 +308,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); - @override _i4.Future<_i6.Uint8List> readBytes( Uri? url, { @@ -330,7 +323,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), ) as _i4.Future<_i6.Uint8List>); - @override _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -355,7 +347,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -376,35 +367,30 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i6.Uint8List(0), returnValueForMissingStub: _i6.Uint8List(0), ) as _i6.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), @@ -429,14 +415,12 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { Invocation.getter(#session), ), ) as _i3.Session); - @override bool get dependsOnSession => (super.noSuchMethod( Invocation.getter(#dependsOnSession), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override set dependsOnSession(bool? _dependsOnSession) => super.noSuchMethod( Invocation.setter( @@ -445,7 +429,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override set cacheDuration(Duration? _cacheDuration) => super.noSuchMethod( Invocation.setter( @@ -454,21 +437,18 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override _i8.RequestStatus get status => (super.noSuchMethod( Invocation.getter(#status), returnValue: _i8.RequestStatus.none, returnValueForMissingStub: _i8.RequestStatus.none, ) as _i8.RequestStatus); - @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override _i4.Future loadFromStorage() => (super.noSuchMethod( Invocation.method( @@ -478,7 +458,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future loadFromRemote( _i3.Session? session, @@ -495,7 +474,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override void restoreSession( String? username, @@ -513,7 +491,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override _i4.Future postAuthentication( _i10.BuildContext? context, @@ -536,25 +513,22 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override - void markAsInitialized() => super.noSuchMethod( + void markAsNotInitialized() => super.noSuchMethod( Invocation.method( - #markAsInitialized, + #markAsNotInitialized, [], ), returnValueForMissingStub: null, ); - @override - void markAsNotInitialized() => super.noSuchMethod( + void updateStatus(_i8.RequestStatus? status) => super.noSuchMethod( Invocation.method( - #markAsNotInitialized, - [], + #updateStatus, + [status], ), returnValueForMissingStub: null, ); - @override _i4.Future forceRefresh(_i10.BuildContext? context) => (super.noSuchMethod( @@ -565,7 +539,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future ensureInitialized(_i10.BuildContext? context) => (super.noSuchMethod( @@ -576,7 +549,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future ensureInitializedFromRemote(_i10.BuildContext? context) => (super.noSuchMethod( @@ -587,7 +559,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future ensureInitializedFromStorage() => (super.noSuchMethod( Invocation.method( @@ -597,7 +568,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -606,7 +576,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -615,7 +584,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override void dispose() => super.noSuchMethod( Invocation.method( @@ -624,7 +592,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart index 9f2e5d8ed..fd78853db 100644 --- a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart @@ -80,7 +80,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> get( Uri? url, { @@ -110,7 +109,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> post( Uri? url, { @@ -154,7 +152,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> put( Uri? url, { @@ -198,7 +195,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -242,7 +238,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -286,7 +281,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future read( Uri? url, { @@ -301,7 +295,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); - @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -317,7 +310,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); - @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -342,7 +334,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -366,7 +357,6 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { returnValue: '', returnValueForMissingStub: '', ) as String); - @override _i3.Future> parseExams( _i2.Response? response, @@ -396,35 +386,30 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart index 49010fdd7..3fd2208a6 100644 --- a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart @@ -80,7 +80,6 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { returnValueForMissingStub: _i4.Future>.value(<_i5.Lecture>[]), ) as _i4.Future>); - @override _i2.Dates getDates() => (super.noSuchMethod( Invocation.method( @@ -102,7 +101,6 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { ), ), ) as _i2.Dates); - @override List getEndpoints(_i6.Session? session) => (super.noSuchMethod( Invocation.method( @@ -147,7 +145,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> get( Uri? url, { @@ -177,7 +174,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> post( Uri? url, { @@ -221,7 +217,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> put( Uri? url, { @@ -265,7 +260,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> patch( Uri? url, { @@ -309,7 +303,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> delete( Uri? url, { @@ -353,7 +346,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future read( Uri? url, { @@ -368,7 +360,6 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); - @override _i4.Future<_i9.Uint8List> readBytes( Uri? url, { @@ -384,7 +375,6 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValueForMissingStub: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), ) as _i4.Future<_i9.Uint8List>); - @override _i4.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => (super.noSuchMethod( @@ -409,7 +399,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -430,35 +419,30 @@ class MockResponse extends _i1.Mock implements _i3.Response { returnValue: _i9.Uint8List(0), returnValueForMissingStub: _i9.Uint8List(0), ) as _i9.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), From 1a2ec3903776efe87080b7a26ba732d7cd232f6b Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 18:27:04 +0000 Subject: [PATCH 04/13] Do not show stall profile picture in profile page --- uni/lib/view/profile/widgets/profile_overview.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/profile/widgets/profile_overview.dart b/uni/lib/view/profile/widgets/profile_overview.dart index 2ea9b1f30..fcc49a8fc 100644 --- a/uni/lib/view/profile/widgets/profile_overview.dart +++ b/uni/lib/view/profile/widgets/profile_overview.dart @@ -32,7 +32,9 @@ class ProfileOverview extends StatelessWidget { height: 150, decoration: BoxDecoration( shape: BoxShape.circle, - image: getProfileDecorationImage(profilePic.data), + image: profilePic.data != null + ? getProfileDecorationImage(profilePic.data) + : null, ), ), const Padding(padding: EdgeInsets.all(8)), From 6292d3e621b298a16ed9c6fde859538373f46a9b Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 18:30:46 +0000 Subject: [PATCH 05/13] Reduce library indicator radius --- uni/lib/view/library/widgets/library_occupation_card.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/library/widgets/library_occupation_card.dart b/uni/lib/view/library/widgets/library_occupation_card.dart index ee6a4548b..05a256194 100644 --- a/uni/lib/view/library/widgets/library_occupation_card.dart +++ b/uni/lib/view/library/widgets/library_occupation_card.dart @@ -66,7 +66,7 @@ class LibraryOccupationCard extends GenericCard { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: CircularPercentIndicator( - radius: 60, + radius: 40, lineWidth: 8, percent: occupation.percentage / 100, center: Text( From b6b8103bb307aa70df9af2d83a8c462db235bd5e Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 19:03:26 +0000 Subject: [PATCH 06/13] Tweak restaurants page and card --- .../view/home/widgets/restaurant_card.dart | 34 +++++++++++-- .../view/restaurant/restaurant_page_view.dart | 48 +++++++++++++------ 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/uni/lib/view/home/widgets/restaurant_card.dart b/uni/lib/view/home/widgets/restaurant_card.dart index 010264d9b..cc8274aa0 100644 --- a/uni/lib/view/home/widgets/restaurant_card.dart +++ b/uni/lib/view/home/widgets/restaurant_card.dart @@ -52,7 +52,7 @@ class RestaurantCard extends GenericCard { onNullContent: Column( children: [ Padding( - padding: const EdgeInsets.only(top: 15, bottom: 10), + padding: const EdgeInsets.only(top: 10, bottom: 10), child: Center( child: Text( S.of(context).no_favorite_restaurants, @@ -74,10 +74,32 @@ class RestaurantCard extends GenericCard { ); } - Widget generateRestaurants(List data, BuildContext context) { + Widget generateRestaurants( + List restaurants, + BuildContext context, + ) { final weekDay = DateTime.now().weekday; final offset = (weekDay - 1) % 7; - final restaurants = data; + + if (restaurants + .map((e) => e.meals[DayOfWeek.values[offset]]) + .every((element) => element?.isEmpty ?? true)) { + return Column( + children: [ + const SizedBox( + height: 15, + ), + Text( + S.of(context).no_menus, + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox( + height: 15, + ), + ], + ); + } + return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -108,12 +130,13 @@ class RestaurantCard extends GenericCard { Center( child: Container( alignment: Alignment.centerLeft, - padding: const EdgeInsets.fromLTRB(12, 20, 12, 5), + padding: const EdgeInsets.fromLTRB(10, 15, 5, 10), child: Text( restaurant.name, style: TextStyle( + fontSize: 16, color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w400, ), ), ), @@ -143,6 +166,7 @@ class RestaurantCard extends GenericCard { ), ), ), + const SizedBox(height: 10), ], ); } diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index 80a1c96b5..a31fefe11 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -65,12 +65,21 @@ class _RestaurantPageViewState extends GeneralPageViewState ], ), const SizedBox(height: 10), - RequestDependentWidgetBuilder( - status: restaurantProvider.status, - builder: () => - createTabViewBuilder(restaurantProvider.restaurants, context), - hasContentPredicate: restaurantProvider.restaurants.isNotEmpty, - onNullContent: Center(child: Text(S.of(context).no_menus)), + Expanded( + child: RequestDependentWidgetBuilder( + status: restaurantProvider.status, + builder: () => createTabViewBuilder( + restaurantProvider.restaurants, + context, + ), + hasContentPredicate: restaurantProvider.restaurants.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_menus, + style: const TextStyle(fontSize: 18), + ), + ), + ), ), ], ); @@ -78,15 +87,24 @@ class _RestaurantPageViewState extends GeneralPageViewState ); } - Widget createTabViewBuilder(dynamic restaurants, BuildContext context) { - final List dayContents = DayOfWeek.values.map((dayOfWeek) { - var restaurantsWidgets = []; - if (restaurants is List) { - restaurantsWidgets = restaurants - .map( - (restaurant) => createRestaurant(context, restaurant, dayOfWeek), - ) - .toList(); + Widget createTabViewBuilder( + List restaurants, + BuildContext context, + ) { + final dayContents = DayOfWeek.values.map((dayOfWeek) { + final restaurantsWidgets = restaurants + .where((element) => element.meals[dayOfWeek]?.isNotEmpty ?? false) + .map( + (restaurant) => createRestaurant(context, restaurant, dayOfWeek), + ) + .toList(); + if (restaurantsWidgets.isEmpty) { + return Center( + child: Text( + S.of(context).no_menus, + style: const TextStyle(fontSize: 18), + ), + ); } return ListView(children: restaurantsWidgets); }).toList(); From 2927b583cc5727634a467516fe5f2304f7f10cbb Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 19:06:25 +0000 Subject: [PATCH 07/13] Hide references when not available --- uni/lib/view/common_widgets/generic_card.dart | 23 ------------- .../profile/widgets/account_info_card.dart | 32 +++++++++---------- .../view/profile/widgets/print_info_card.dart | 4 --- 3 files changed, 15 insertions(+), 44 deletions(-) diff --git a/uni/lib/view/common_widgets/generic_card.dart b/uni/lib/view/common_widgets/generic_card.dart index 9bd18de0c..5d56b915a 100644 --- a/uni/lib/view/common_widgets/generic_card.dart +++ b/uni/lib/view/common_widgets/generic_card.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/entities/time_utilities.dart'; /// App default card abstract class GenericCard extends StatefulWidget { @@ -51,27 +49,6 @@ abstract class GenericCard extends StatefulWidget { style: Theme.of(context).textTheme.titleLarge, ); } - - StatelessWidget showLastRefreshedTime(String? time, BuildContext context) { - if (time == null) { - return const Text('N/A'); - } - - final parsedTime = DateTime.tryParse(time); - if (parsedTime == null) { - return const Text('N/A'); - } - - return Container( - alignment: Alignment.center, - child: Text( - S.of(context).last_refresh_time( - parsedTime.toTimeHourMinString(), - ), - style: Theme.of(context).textTheme.bodySmall, - ), - ); - } } class GenericCardState extends State { diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index 31fe34b0f..3e5eca04d 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -96,25 +96,23 @@ class AccountInfoCard extends GenericCard { ), ], ), - Container( - padding: const EdgeInsets.all(10), - child: Row( - children: [ - Text( - S.of(context).pendent_references, - style: Theme.of(context).textTheme.titleLarge?.apply( - color: Theme.of(context).colorScheme.secondary, - ), - ), - ], + if (references.isNotEmpty) + Container( + padding: const EdgeInsets.all(10), + child: Row( + children: [ + Text( + S.of(context).pendent_references, + style: Theme.of(context).textTheme.titleLarge?.apply( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ), ), - ), - ReferenceList(references: references), + if (references.isNotEmpty) + ReferenceList(references: references), const SizedBox(height: 10), - showLastRefreshedTime( - profileStateProvider.lastUpdateTime?.toIso8601String(), - context, - ), ], ); }, diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index cebb17675..b2acae5a8 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -66,10 +66,6 @@ class PrintInfoCard extends GenericCard { ), ], ), - showLastRefreshedTime( - profileStateProvider.lastUpdateTime?.toIso8601String(), - context, - ), ], ); }, From 19f64d8875e56f18533dc06e45baefe1db1da83f Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:38:02 +0000 Subject: [PATCH 08/13] Remove top right button override on bug report page --- uni/lib/view/bug_report/bug_report.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/bug_report/bug_report.dart b/uni/lib/view/bug_report/bug_report.dart index b04005058..26b9ae931 100644 --- a/uni/lib/view/bug_report/bug_report.dart +++ b/uni/lib/view/bug_report/bug_report.dart @@ -22,9 +22,4 @@ class BugReportPageViewState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} - - @override - Container getTopRightButton(BuildContext context) { - return Container(); - } } From 4d979ea950e8ce8141a6ca075d854a8f57e53109 Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:39:23 +0000 Subject: [PATCH 09/13] Remove top right button override on about page --- uni/lib/view/about/about.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/about/about.dart b/uni/lib/view/about/about.dart index 513ff4a98..a0c17a67c 100644 --- a/uni/lib/view/about/about.dart +++ b/uni/lib/view/about/about.dart @@ -37,9 +37,4 @@ class AboutPageViewState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} - - @override - Widget getTopRightButton(BuildContext context) { - return Container(); - } } From 4c641bf822f0518e3c6956e0d2d24f9473468d0c Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:42:30 +0000 Subject: [PATCH 10/13] Remove top right button on settings page --- uni/lib/view/settings/settings.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart index 8360b80d2..c1f0210a9 100644 --- a/uni/lib/view/settings/settings.dart +++ b/uni/lib/view/settings/settings.dart @@ -95,9 +95,4 @@ class SettingsPageState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} - - @override - Widget getTopRightButton(BuildContext context) { - return Container(); - } } From 801b481c83670eff7da8c71a58eb649a88b52610 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 11 Dec 2023 12:25:24 +0000 Subject: [PATCH 11/13] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 32cc5a199..9c84debe6 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.21+211 \ No newline at end of file +1.7.22+212 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 998bf377e..045b77b6e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.21+211 +version: 1.7.22+212 environment: sdk: ">=3.0.0 <4.0.0" From fbe2129f1a6ad5e200dddbe0f641d3644c1be88b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:27:35 +0000 Subject: [PATCH 12/13] Bump build_runner from 2.4.6 to 2.4.7 in /uni Bumps [build_runner](https://github.com/dart-lang/build) from 2.4.6 to 2.4.7. - [Release notes](https://github.com/dart-lang/build/releases) - [Commits](https://github.com/dart-lang/build/compare/build_runner-v2.4.6...build_runner-v2.4.7) --- updated-dependencies: - dependency-name: build_runner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index f7a45f92e..3e3464b01 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -109,10 +109,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" build_runner_core: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 045b77b6e..d21888921 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: workmanager: ^0.5.2 dev_dependencies: - build_runner: ^2.4.6 + build_runner: ^2.4.7 flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter From 5afe78494854dd7b51a6c3f6e23db2733eda43b5 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 13 Dec 2023 17:26:29 +0000 Subject: [PATCH 13/13] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 9c84debe6..69dbf75d4 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.22+212 \ No newline at end of file +1.7.23+213 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index d21888921..9b2e408d0 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.22+212 +version: 1.7.23+213 environment: sdk: ">=3.0.0 <4.0.0"