diff --git a/packages/uni_app/assets/fonts/Poppins/OFL.txt b/packages/uni_app/assets/fonts/Poppins/OFL.txt new file mode 100644 index 000000000..3e92931fa --- /dev/null +++ b/packages/uni_app/assets/fonts/Poppins/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Black.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Black.ttf new file mode 100644 index 000000000..71c0f995e Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Black.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-BlackItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-BlackItalic.ttf new file mode 100644 index 000000000..7aeb58bd1 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-BlackItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Bold.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Bold.ttf new file mode 100644 index 000000000..00559eeb2 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Bold.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-BoldItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-BoldItalic.ttf new file mode 100644 index 000000000..e61e8e88b Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-BoldItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraBold.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraBold.ttf new file mode 100644 index 000000000..df7093608 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraBold.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraBoldItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraBoldItalic.ttf new file mode 100644 index 000000000..14d2b375d Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraBoldItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraLight.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraLight.ttf new file mode 100644 index 000000000..e76ec69a6 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraLight.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraLightItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraLightItalic.ttf new file mode 100644 index 000000000..89513d946 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-ExtraLightItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Italic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Italic.ttf new file mode 100644 index 000000000..12b7b3c40 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Italic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Light.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Light.ttf new file mode 100644 index 000000000..bc36bcc24 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Light.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-LightItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-LightItalic.ttf new file mode 100644 index 000000000..9e70be6a9 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-LightItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Medium.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Medium.ttf new file mode 100644 index 000000000..6bcdcc27f Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Medium.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-MediumItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-MediumItalic.ttf new file mode 100644 index 000000000..be67410fd Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-MediumItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Regular.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Regular.ttf new file mode 100644 index 000000000..9f0c71b70 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Regular.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-SemiBold.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-SemiBold.ttf new file mode 100644 index 000000000..74c726e32 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-SemiBold.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-SemiBoldItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-SemiBoldItalic.ttf new file mode 100644 index 000000000..3e6c94223 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-SemiBoldItalic.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-Thin.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-Thin.ttf new file mode 100644 index 000000000..03e736613 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-Thin.ttf differ diff --git a/packages/uni_app/assets/fonts/Poppins/Poppins-ThinItalic.ttf b/packages/uni_app/assets/fonts/Poppins/Poppins-ThinItalic.ttf new file mode 100644 index 000000000..e26db5dd3 Binary files /dev/null and b/packages/uni_app/assets/fonts/Poppins/Poppins-ThinItalic.ttf differ diff --git a/packages/uni_app/assets/images/exams_intro.png b/packages/uni_app/assets/images/exams_intro.png new file mode 100644 index 000000000..dfe25c2f7 Binary files /dev/null and b/packages/uni_app/assets/images/exams_intro.png differ diff --git a/packages/uni_app/assets/images/faculty_intro.png b/packages/uni_app/assets/images/faculty_intro.png new file mode 100644 index 000000000..c42790c19 Binary files /dev/null and b/packages/uni_app/assets/images/faculty_intro.png differ diff --git a/packages/uni_app/assets/images/intro1.png b/packages/uni_app/assets/images/intro1.png new file mode 100644 index 000000000..fac584aa7 Binary files /dev/null and b/packages/uni_app/assets/images/intro1.png differ diff --git a/packages/uni_app/assets/images/intro2.png b/packages/uni_app/assets/images/intro2.png new file mode 100644 index 000000000..da931d980 Binary files /dev/null and b/packages/uni_app/assets/images/intro2.png differ diff --git a/packages/uni_app/assets/images/map_intro.png b/packages/uni_app/assets/images/map_intro.png new file mode 100644 index 000000000..d001817a2 Binary files /dev/null and b/packages/uni_app/assets/images/map_intro.png differ diff --git a/packages/uni_app/assets/images/restaurants_intro.png b/packages/uni_app/assets/images/restaurants_intro.png new file mode 100644 index 000000000..cf6cbe1bf Binary files /dev/null and b/packages/uni_app/assets/images/restaurants_intro.png differ diff --git a/packages/uni_app/assets/images/schedule_intro.png b/packages/uni_app/assets/images/schedule_intro.png new file mode 100644 index 000000000..530fb719a Binary files /dev/null and b/packages/uni_app/assets/images/schedule_intro.png differ diff --git a/packages/uni_app/lib/controller/background_workers/notifications.dart b/packages/uni_app/lib/controller/background_workers/notifications.dart index 106a8ac00..2622352df 100644 --- a/packages/uni_app/lib/controller/background_workers/notifications.dart +++ b/packages/uni_app/lib/controller/background_workers/notifications.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:logger/logger.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uni/controller/background_workers/notifications/tuition_notification.dart'; import 'package:uni/controller/local_storage/notification_timeout_storage.dart'; @@ -75,6 +76,50 @@ class NotificationManager { await _buildNotificationWorker(); } + Future hasNotificationPermission() async { + if (Platform.isAndroid) { + try { + final status = await Permission.notification.status; + if (status.isGranted) { + return true; + } + + if (status.isDenied || + status.isPermanentlyDenied || + status.isRestricted) { + return false; + } + + final androidPlugin = _localNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin + >(); + final bool? enabled = await androidPlugin?.areNotificationsEnabled(); + return enabled ?? true; + } catch (_) { + final androidPlugin = _localNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin + >(); + try { + final bool? enabled = await androidPlugin?.areNotificationsEnabled(); + return enabled ?? true; + } catch (_) { + return true; + } + } + } else if (Platform.isIOS) { + try { + final status = await Permission.notification.status; + return status.isGranted; + } catch (_) { + return false; + } + } + + return false; + } + static Future _initFlutterNotificationsPlugin() async { const initializationSettingsAndroid = AndroidInitializationSettings( '@mipmap/launcher_icon', diff --git a/packages/uni_app/lib/generated/intl/messages_en.dart b/packages/uni_app/lib/generated/intl/messages_en.dart index 015247fa8..49150f64b 100644 --- a/packages/uni_app/lib/generated/intl/messages_en.dart +++ b/packages/uni_app/lib/generated/intl/messages_en.dart @@ -185,6 +185,9 @@ class MessageLookup extends MessageLookupByLibrary { "exams_filter": MessageLookupByLibrary.simpleMessage( "Exams Filter Settings", ), + "exams_intro_message": MessageLookupByLibrary.simpleMessage( + "Stay always updated with your exams", + ), "exit_confirm": MessageLookupByLibrary.simpleMessage( "Do you really want to exit?", ), @@ -254,6 +257,10 @@ class MessageLookup extends MessageLookupByLibrary { ), "logout": MessageLookupByLibrary.simpleMessage("Log out"), "lunch": MessageLookupByLibrary.simpleMessage("Lunch"), + "map": MessageLookupByLibrary.simpleMessage("Map"), + "map_intro_message": MessageLookupByLibrary.simpleMessage( + "Explore the campus with our interactive map", + ), "menus": MessageLookupByLibrary.simpleMessage("Menus"), "min_value_reference": MessageLookupByLibrary.simpleMessage( "Minimum value: 1,00 €", @@ -374,6 +381,9 @@ class MessageLookup extends MessageLookupByLibrary { "No trips found at the moment", ), "notifications": MessageLookupByLibrary.simpleMessage("Notifications"), + "notifications_intro_message": MessageLookupByLibrary.simpleMessage( + "Would you like to receive notifications regarding important updates and alerts, including fee deadlines?", + ), "now": MessageLookupByLibrary.simpleMessage("Now"), "occurrence_type": MessageLookupByLibrary.simpleMessage( "Type of occurrence", @@ -428,9 +438,15 @@ class MessageLookup extends MessageLookupByLibrary { ), "restaurant_period": m4, "restaurants": MessageLookupByLibrary.simpleMessage("Restaurants"), + "restaurants_intro_message": MessageLookupByLibrary.simpleMessage( + "Discover campus dining options and menus", + ), "room": MessageLookupByLibrary.simpleMessage("Room"), "save": MessageLookupByLibrary.simpleMessage("Save"), "schedule": MessageLookupByLibrary.simpleMessage("Schedule"), + "schedule_intro_message": MessageLookupByLibrary.simpleMessage( + "Keep track of your daily classes and activities", + ), "school_calendar": MessageLookupByLibrary.simpleMessage("School Calendar"), "search": MessageLookupByLibrary.simpleMessage("Search"), "see_more": MessageLookupByLibrary.simpleMessage("See more"), @@ -441,6 +457,9 @@ class MessageLookup extends MessageLookupByLibrary { "An error occurred in sending", ), "services": MessageLookupByLibrary.simpleMessage("Services"), + "services_intro_message": MessageLookupByLibrary.simpleMessage( + "Access various campus services at your fingertips", + ), "settings": MessageLookupByLibrary.simpleMessage("Settings"), "snackbar": MessageLookupByLibrary.simpleMessage("Snackbar"), "some_error": MessageLookupByLibrary.simpleMessage("Some error!"), diff --git a/packages/uni_app/lib/generated/intl/messages_pt_PT.dart b/packages/uni_app/lib/generated/intl/messages_pt_PT.dart index bedaae31e..f9993f46c 100644 --- a/packages/uni_app/lib/generated/intl/messages_pt_PT.dart +++ b/packages/uni_app/lib/generated/intl/messages_pt_PT.dart @@ -191,6 +191,9 @@ class MessageLookup extends MessageLookupByLibrary { "exams_filter": MessageLookupByLibrary.simpleMessage( "Definições Filtro de Exames", ), + "exams_intro_message": MessageLookupByLibrary.simpleMessage( + "Mantém-te sempre atualizado com os teus exames", + ), "exit_confirm": MessageLookupByLibrary.simpleMessage( "Tem a certeza de que pretende sair?", ), @@ -266,6 +269,10 @@ class MessageLookup extends MessageLookupByLibrary { ), "logout": MessageLookupByLibrary.simpleMessage("Terminar sessão"), "lunch": MessageLookupByLibrary.simpleMessage("Almoço"), + "map": MessageLookupByLibrary.simpleMessage("Mapa"), + "map_intro_message": MessageLookupByLibrary.simpleMessage( + "Navega pelo campus com o nosso mapa interativo", + ), "menus": MessageLookupByLibrary.simpleMessage("Ementas"), "min_value_reference": MessageLookupByLibrary.simpleMessage( "Valor mínimo: 1,00 €", @@ -396,6 +403,9 @@ class MessageLookup extends MessageLookupByLibrary { "Não há viagens planeadas de momento", ), "notifications": MessageLookupByLibrary.simpleMessage("Notificações"), + "notifications_intro_message": MessageLookupByLibrary.simpleMessage( + "Queres receber alertas de eventos e informações importantes, incluindo o prazo limite de propinas?", + ), "now": MessageLookupByLibrary.simpleMessage("Agora"), "occurrence_type": MessageLookupByLibrary.simpleMessage( "Tipo de ocorrência", @@ -450,9 +460,15 @@ class MessageLookup extends MessageLookupByLibrary { ), "restaurant_period": m4, "restaurants": MessageLookupByLibrary.simpleMessage("Restaurantes"), + "restaurants_intro_message": MessageLookupByLibrary.simpleMessage( + "Descobre as opções de restauração do campus e os menus", + ), "room": MessageLookupByLibrary.simpleMessage("Sala"), "save": MessageLookupByLibrary.simpleMessage("Guardar"), "schedule": MessageLookupByLibrary.simpleMessage("Aulas"), + "schedule_intro_message": MessageLookupByLibrary.simpleMessage( + "Mantém o controlo das tuas aulas e atividades diárias", + ), "school_calendar": MessageLookupByLibrary.simpleMessage( "Calendário Escolar", ), @@ -465,6 +481,9 @@ class MessageLookup extends MessageLookupByLibrary { "Ocorreu um erro no envio", ), "services": MessageLookupByLibrary.simpleMessage("Serviços"), + "services_intro_message": MessageLookupByLibrary.simpleMessage( + "Explora os serviços académicos disponíveis para ti", + ), "settings": MessageLookupByLibrary.simpleMessage("Definições"), "snackbar": MessageLookupByLibrary.simpleMessage("Snackbar"), "some_error": MessageLookupByLibrary.simpleMessage("Algum erro!"), diff --git a/packages/uni_app/lib/generated/l10n.dart b/packages/uni_app/lib/generated/l10n.dart index 8928735f9..327127f6c 100644 --- a/packages/uni_app/lib/generated/l10n.dart +++ b/packages/uni_app/lib/generated/l10n.dart @@ -1950,6 +1950,71 @@ class S { ); } + /// `Map` + String get map { + return Intl.message('Map', name: 'map', desc: '', args: []); + } + + /// `Stay always updated with your exams` + String get exams_intro_message { + return Intl.message( + 'Stay always updated with your exams', + name: 'exams_intro_message', + desc: '', + args: [], + ); + } + + /// `Keep track of your daily classes and activities` + String get schedule_intro_message { + return Intl.message( + 'Keep track of your daily classes and activities', + name: 'schedule_intro_message', + desc: '', + args: [], + ); + } + + /// `Discover campus dining options and menus` + String get restaurants_intro_message { + return Intl.message( + 'Discover campus dining options and menus', + name: 'restaurants_intro_message', + desc: '', + args: [], + ); + } + + /// `Access various campus services at your fingertips` + String get services_intro_message { + return Intl.message( + 'Access various campus services at your fingertips', + name: 'services_intro_message', + desc: '', + args: [], + ); + } + + /// `Explore the campus with our interactive map` + String get map_intro_message { + return Intl.message( + 'Explore the campus with our interactive map', + name: 'map_intro_message', + desc: '', + args: [], + ); + } + + /// `Would you like to receive notifications regarding important updates and alerts, including fee deadlines?` + String get notifications_intro_message { + return Intl.message( + 'Would you like to receive notifications regarding important updates and alerts, including fee deadlines?', + name: 'notifications_intro_message', + desc: '', + args: [], + ); + } + /// `No classes have been assigned to this course unit yet` String get no_course_unit_classes { return Intl.message( diff --git a/packages/uni_app/lib/l10n/intl_en.arb b/packages/uni_app/lib/l10n/intl_en.arb index b6e687820..a99e300f2 100644 --- a/packages/uni_app/lib/l10n/intl_en.arb +++ b/packages/uni_app/lib/l10n/intl_en.arb @@ -466,6 +466,20 @@ "@services": {}, "goi": "Orientation and Integration Office", "@goi": {}, + "map": "Map", + "@map": {}, + "exams_intro_message": "Stay always updated with your exams", + "@exams_intro_message": {}, + "schedule_intro_message": "Keep track of your daily classes and activities", + "@schedule_intro_message": {}, + "restaurants_intro_message": "Discover campus dining options and menus", + "@restaurants_intro_message": {}, + "services_intro_message": "Access various campus services at your fingertips", + "@services_intro_message": {}, + "map_intro_message": "Explore the campus with our interactive map", + "@map_intro_message": {}, + "notifications_intro_message": "Would you like to receive notifications regarding important updates and alerts, including fee deadlines?", + "@notifications_intro_message": {}, "no_course_unit_classes": "No classes have been assigned to this course unit yet", "@no_course_unit_classes": {}, "no_course_unit_info": "This course unit has no information available yet", diff --git a/packages/uni_app/lib/l10n/intl_pt_PT.arb b/packages/uni_app/lib/l10n/intl_pt_PT.arb index d08c7f8a4..7a3426c39 100644 --- a/packages/uni_app/lib/l10n/intl_pt_PT.arb +++ b/packages/uni_app/lib/l10n/intl_pt_PT.arb @@ -466,6 +466,20 @@ "@services": {}, "goi": "Gabinete de Orientação e Integração", "@goi": {}, + "map": "Mapa", + "@map": {}, + "exams_intro_message": "Mantém-te sempre atualizado com os teus exames", + "@exams_intro_message": {}, + "schedule_intro_message": "Mantém o controlo das tuas aulas e atividades diárias", + "@schedule_intro_message": {}, + "restaurants_intro_message": "Descobre as opções de restauração do campus e os menus", + "@restaurants_intro_message": {}, + "services_intro_message": "Explora os serviços académicos disponíveis para ti", + "@services_intro_message": {}, + "map_intro_message": "Navega pelo campus com o nosso mapa interativo", + "@map_intro_message": {}, + "notifications_intro_message": "Queres receber alertas de eventos e informações importantes, incluindo o prazo limite de propinas?", + "@notifications_intro_message": {}, "no_course_unit_classes": "Não foram atribuídas turmas a esta unidade curricular", "@no_course_unit_classes": {}, "no_course_unit_info": "Esta unidade curricular ainda não tem informação disponível", diff --git a/packages/uni_app/lib/main.dart b/packages/uni_app/lib/main.dart index fbefafce8..885fe6ff6 100644 --- a/packages/uni_app/lib/main.dart +++ b/packages/uni_app/lib/main.dart @@ -30,6 +30,7 @@ import 'package:uni/view/course_unit_info/course_unit_info.dart'; import 'package:uni/view/faculty/faculty.dart'; import 'package:uni/view/home/edit_home.dart'; import 'package:uni/view/home/home.dart'; +import 'package:uni/view/introduction/introduction.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/login/login.dart'; import 'package:uni/view/map/map.dart'; @@ -51,7 +52,7 @@ Future firstRoute() async { final savedSession = await PreferencesController.getSavedSession(); if (savedSession != null) { - return '/${NavigationItem.navPersonalArea.route}'; + return '/${NavigationItem.navIntroduction.route}'; } await acceptTermsAndConditions(); @@ -250,6 +251,11 @@ class ApplicationState extends ConsumerState { page: CourseUnitDetailPageView(courseUnit!), settings: settings, ), + '/${NavigationItem.navIntroduction.route}': () => + PageTransition.introductionTransitionRoute( + page: const IntroductionScreenView(), + settings: settings, + ), }; final builder = transitionFunctions[settings.name]; diff --git a/packages/uni_app/lib/utils/navigation_items.dart b/packages/uni_app/lib/utils/navigation_items.dart index e70a8dd2b..44bc68247 100644 --- a/packages/uni_app/lib/utils/navigation_items.dart +++ b/packages/uni_app/lib/utils/navigation_items.dart @@ -16,7 +16,8 @@ enum NavigationItem { navLogin('login'), navBugreport('bug_report'), navSplash('splash'), - navAboutus('sobre_nos'); + navAboutus('sobre_nos'), + navIntroduction('introducao'); const NavigationItem(this.route, {this.faculties}); diff --git a/packages/uni_app/lib/view/introduction/introduction.dart b/packages/uni_app/lib/view/introduction/introduction.dart new file mode 100644 index 000000000..a1e562c1f --- /dev/null +++ b/packages/uni_app/lib/view/introduction/introduction.dart @@ -0,0 +1,240 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:uni/controller/background_workers/notifications.dart'; +import 'package:uni/utils/navigation_items.dart'; +import 'package:uni/view/introduction/widgets/exams_intro_page.dart'; +import 'package:uni/view/introduction/widgets/first_page.dart'; +import 'package:uni/view/introduction/widgets/map_intro_page.dart'; +import 'package:uni/view/introduction/widgets/notifications_intro_page.dart'; +import 'package:uni/view/introduction/widgets/restaurants_intro_page.dart'; +import 'package:uni/view/introduction/widgets/schedule_intro_page.dart'; +import 'package:uni/view/introduction/widgets/services_intro_page.dart'; + +class IntroductionScreenView extends StatefulWidget { + const IntroductionScreenView({super.key}); + + @override + State createState() => _IntroductionScreenViewState(); +} + +class _IntroductionScreenViewState extends State + with TickerProviderStateMixin { + late PageController _pageController; + int _currentPage = 0; + bool _notificationPermission = false; + final NotificationManager _notificationManager = NotificationManager(); + + @override + void initState() { + super.initState(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + _pageController = PageController(); + _pageController.addListener(() { + if (_pageController.page?.round() != _currentPage) { + setState(() { + _currentPage = _pageController.page?.round() ?? 0; + }); + } + }); + _checkNotificationPermission(); + } + + Future _checkNotificationPermission() async { + final granted = await _notificationManager.hasNotificationPermission(); + if (mounted) { + setState(() { + _notificationPermission = granted; + }); + } + } + + @override + void dispose() { + _pageController.dispose(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + super.dispose(); + } + + void _nextPage() { + if (_currentPage < 6) { + _pageController.nextPage( + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + } else { + _finishIntro(); + } + } + + void _finishIntro() { + Navigator.of( + context, + ).pushReplacementNamed('/${NavigationItem.navPersonalArea.route}'); + } + + @override + Widget build(BuildContext context) { + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarIconBrightness: Brightness.light, + ), + ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + return Scaffold( + extendBody: true, + extendBodyBehindAppBar: true, + backgroundColor: const Color(0xFF280709), + body: Stack( + children: [ + Container( + decoration: const BoxDecoration( + gradient: RadialGradient( + center: Alignment(-0.95, -1), + colors: [Color(0x705F171D), Color(0x02511515)], + stops: [0, 1], + ), + ), + ), + Container( + decoration: const BoxDecoration( + gradient: RadialGradient( + center: Alignment(0.1, 0.95), + radius: 0.3, + colors: [Color(0x705F171D), Color(0x02511515)], + stops: [0, 1], + ), + ), + ), + PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + children: [ + FirstPage(pageController: _pageController), + ExamsIntroPage(pageController: _pageController), + ScheduleIntroPage(pageController: _pageController), + RestaurantsIntroPage(pageController: _pageController), + ServicesIntroPage(pageController: _pageController), + MapIntroPage(pageController: _pageController), + NotificationsIntroPage( + pageController: _pageController, + notificationPermission: _notificationPermission, + onPermissionChanged: (granted) { + setState(() { + _notificationPermission = granted; + }); + }, + ), + ], + ), + Align(alignment: const Alignment(0, 0.95), child: _buildBottomArea()), + ], + ), + ); + } + + Widget _buildBottomArea() { + if (_currentPage == 6 && !_notificationPermission) { + return Row( + mainAxisSize: MainAxisSize.min, + spacing: 32, + children: [ + GestureDetector( + onTap: _finishIntro, + child: Container( + height: 60, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + child: const Center( + child: Text( + 'Skip', + style: TextStyle( + fontFamily: 'Poppins', + color: Color(0xFFFFF5F3), + fontSize: 16, + ), + ), + ), + ), + ), + GestureDetector( + onTap: () async { + try { + await _notificationManager.initializeNotifications(); + final granted = await _notificationManager + .hasNotificationPermission(); + if (mounted) { + setState(() { + _notificationPermission = granted; + }); + } + } catch (_) {} + }, + child: Container( + height: 48, + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + decoration: ShapeDecoration( + gradient: const LinearGradient( + begin: Alignment.bottomRight, + end: Alignment(-0.24, -0.31), + colors: [Color(0xFF280709), Color(0xFF461014)], + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + shadows: const [ + BoxShadow( + color: Color(0xBF996B6E), + blurRadius: 22, + offset: Offset(0, 7), + ), + ], + ), + child: const Center( + child: Text( + 'Allow', + style: TextStyle( + fontFamily: 'Poppins', + color: Color(0xFFFFF5F3), + fontSize: 16, + ), + ), + ), + ), + ), + ], + ); + } + + return GestureDetector( + onTap: _nextPage, + child: Container( + width: 60, + height: 60, + decoration: ShapeDecoration( + gradient: const LinearGradient( + begin: Alignment.bottomRight, + end: Alignment(-0.24, -0.31), + colors: [Color(0xFF280709), Color(0xFF461014)], + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + shadows: const [ + BoxShadow( + color: Color(0xBF996B6E), + blurRadius: 22, + offset: Offset(0, 7), + ), + ], + ), + child: const Center( + child: Icon( + Icons.arrow_forward_ios_rounded, + color: Color(0xFFFFF5F3), + size: 24, + ), + ), + ), + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/exams_intro_page.dart b/packages/uni_app/lib/view/introduction/widgets/exams_intro_page.dart new file mode 100644 index 000000000..5e41c209b --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/exams_intro_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; + +class ExamsIntroPage extends StatefulWidget { + const ExamsIntroPage({super.key, required this.pageController}); + + final PageController pageController; + + @override + State createState() => _ExamsIntroPageState(); +} + +class _ExamsIntroPageState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: Text( + S.of(context).exams.toUpperCase(), + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + ), + Align( + alignment: const Alignment(0, -0.7), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + S.of(context).exams_intro_message, + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontSize: 16, + ), + ), + ), + ), + Align( + alignment: const Alignment(0.035, 0.5), + child: Image.asset('assets/images/exams_intro.png', width: 260), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/first_page.dart b/packages/uni_app/lib/view/introduction/widgets/first_page.dart new file mode 100644 index 000000000..2014c2ff3 --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/first_page.dart @@ -0,0 +1,151 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class FirstPage extends StatefulWidget { + const FirstPage({super.key, required this.pageController}); + + final PageController pageController; + + @override + State createState() => _FirstPageState(); +} + +class _FirstPageState extends State with TickerProviderStateMixin { + late AnimationController _logoController; + late AnimationController _image1Controller; + late AnimationController _image2Controller; + + late Animation _logoFadeAnimation; + late Animation _logoSlideAnimation; + late Animation _image1FadeAnimation; + late Animation _image1SlideAnimation; + late Animation _image2FadeAnimation; + late Animation _image2SlideAnimation; + + @override + void initState() { + super.initState(); + _logoController = AnimationController( + duration: const Duration(milliseconds: 800), + vsync: this, + ); + _image1Controller = AnimationController( + duration: const Duration(milliseconds: 800), + vsync: this, + ); + _image2Controller = AnimationController( + duration: const Duration(milliseconds: 800), + vsync: this, + ); + + _logoFadeAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _logoController, curve: Curves.easeOut)); + + _logoSlideAnimation = Tween( + begin: const Offset(0, 0.1), + end: Offset.zero, + ).animate(CurvedAnimation(parent: _logoController, curve: Curves.easeOut)); + + _image1FadeAnimation = Tween(begin: 0, end: 1).animate( + CurvedAnimation(parent: _image1Controller, curve: Curves.easeOut), + ); + + _image1SlideAnimation = + Tween(begin: const Offset(0, 0.1), end: Offset.zero).animate( + CurvedAnimation(parent: _image1Controller, curve: Curves.easeOut), + ); + + _image2FadeAnimation = Tween(begin: 0, end: 1).animate( + CurvedAnimation(parent: _image2Controller, curve: Curves.easeOut), + ); + + _image2SlideAnimation = + Tween(begin: const Offset(0, 0.1), end: Offset.zero).animate( + CurvedAnimation(parent: _image2Controller, curve: Curves.easeOut), + ); + + _startAnimations(); + } + + Future _startAnimations() async { + unawaited(_logoController.forward()); + + unawaited(Future.delayed(const Duration(milliseconds: 300))); + unawaited(_image1Controller.forward()); + + unawaited(Future.delayed(const Duration(milliseconds: 200))); + unawaited(_image2Controller.forward()); + } + + @override + void dispose() { + _logoController.dispose(); + _image1Controller.dispose(); + _image2Controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: AnimatedBuilder( + animation: _logoController, + builder: (context, child) { + return FadeTransition( + opacity: _logoFadeAnimation, + child: SlideTransition( + position: _logoSlideAnimation, + child: SvgPicture.asset( + 'assets/images/logo_dark.svg', + width: 120, + colorFilter: const ColorFilter.mode( + Color(0xFFFFF5F3), + BlendMode.srcIn, + ), + ), + ), + ); + }, + ), + ), + Align( + alignment: const Alignment(1, -0.1), + child: AnimatedBuilder( + animation: _image2Controller, + builder: (context, child) { + return FadeTransition( + opacity: _image2FadeAnimation, + child: SlideTransition( + position: _image2SlideAnimation, + child: Image.asset('assets/images/intro2.png', width: 220), + ), + ); + }, + ), + ), + Align( + alignment: const Alignment(-1, 0.3), + child: AnimatedBuilder( + animation: _image1Controller, + builder: (context, child) { + return FadeTransition( + opacity: _image1FadeAnimation, + child: SlideTransition( + position: _image1SlideAnimation, + child: Image.asset('assets/images/intro1.png', width: 220), + ), + ); + }, + ), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/map_intro_page.dart b/packages/uni_app/lib/view/introduction/widgets/map_intro_page.dart new file mode 100644 index 000000000..99be3ac26 --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/map_intro_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; + +class MapIntroPage extends StatefulWidget { + const MapIntroPage({super.key, required this.pageController}); + + final PageController pageController; + + @override + State createState() => _MapIntroPageState(); +} + +class _MapIntroPageState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: Text( + S.of(context).map.toUpperCase(), + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + ), + Align( + alignment: const Alignment(0, -0.7), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + S.of(context).map_intro_message, + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontSize: 16, + ), + ), + ), + ), + Align( + alignment: const Alignment(0.035, 0.5), + child: Image.asset('assets/images/map_intro.png', width: 260), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/notifications_intro_page.dart b/packages/uni_app/lib/view/introduction/widgets/notifications_intro_page.dart new file mode 100644 index 000000000..9fc05c0ff --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/notifications_intro_page.dart @@ -0,0 +1,129 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:uni/controller/background_workers/notifications.dart'; +import 'package:uni/generated/l10n.dart'; + +class NotificationsIntroPage extends StatefulWidget { + const NotificationsIntroPage({ + super.key, + required this.pageController, + required this.onPermissionChanged, + required this.notificationPermission, + }); + + final PageController pageController; + final void Function(bool) onPermissionChanged; + final bool notificationPermission; + + @override + State createState() => _NotificationsIntroPageState(); +} + +class _NotificationsIntroPageState extends State { + final NotificationManager _notificationManager = NotificationManager(); + + @override + void initState() { + super.initState(); + _loadNotificationPermission(); + } + + Future _loadNotificationPermission() async { + final granted = await _notificationManager.hasNotificationPermission(); + if (!mounted) { + return; + } + widget.onPermissionChanged(granted); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 12, + children: [ + const Icon( + Icons.notifications_rounded, + size: 48, + color: Color(0xFFFFF5F3), + ), + Text( + S.of(context).notifications.toUpperCase(), + style: const TextStyle( + fontFamily: 'Poppins', + color: Color(0xFFFFF5F3), + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + S.of(context).notifications_intro_message, + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Color(0xFFFFF5F3), + fontSize: 16, + ), + ), + ), + ], + ), + ), + Align( + alignment: const Alignment(0, 0.3), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: ClipRRect( + borderRadius: BorderRadius.circular(25), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + decoration: ShapeDecoration( + color: const Color(0xFFFFF5F3).withValues(alpha: 0.1), + shape: RoundedSuperellipseBorder( + borderRadius: BorderRadius.circular(25), + ), + ), + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + spacing: 12, + children: [ + Icon( + widget.notificationPermission + ? Icons.check_circle_rounded + : Icons.error_rounded, + size: 32, + color: const Color(0xFFFFF5F3), + ), + Text( + widget.notificationPermission + ? 'uni has permission to send you notifications.' + : 'uni does not have permission to send you notifications yet.', + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Color(0xFFFFF5F3), + fontSize: 12, + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/restaurants_intro_page.dart b/packages/uni_app/lib/view/introduction/widgets/restaurants_intro_page.dart new file mode 100644 index 000000000..575dd6943 --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/restaurants_intro_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; + +class RestaurantsIntroPage extends StatefulWidget { + const RestaurantsIntroPage({super.key, required this.pageController}); + + final PageController pageController; + + @override + State createState() => _RestaurantsIntroPageState(); +} + +class _RestaurantsIntroPageState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: Text( + S.of(context).restaurants.toUpperCase(), + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + ), + Align( + alignment: const Alignment(0, -0.7), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + S.of(context).restaurants_intro_message, + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontSize: 16, + ), + ), + ), + ), + Align( + alignment: const Alignment(0.035, 0.5), + child: Image.asset('assets/images/restaurants_intro.png', width: 260), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/schedule_intro_page.dart b/packages/uni_app/lib/view/introduction/widgets/schedule_intro_page.dart new file mode 100644 index 000000000..24f51f026 --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/schedule_intro_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; + +class ScheduleIntroPage extends StatefulWidget { + const ScheduleIntroPage({super.key, required this.pageController}); + + final PageController pageController; + + @override + State createState() => _ScheduleIntroPageState(); +} + +class _ScheduleIntroPageState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: Text( + S.of(context).schedule.toUpperCase(), + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + ), + Align( + alignment: const Alignment(0, -0.7), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + S.of(context).schedule_intro_message, + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontSize: 16, + ), + ), + ), + ), + Align( + alignment: const Alignment(0.035, 0.5), + child: Image.asset('assets/images/schedule_intro.png', width: 260), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/introduction/widgets/services_intro_page.dart b/packages/uni_app/lib/view/introduction/widgets/services_intro_page.dart new file mode 100644 index 000000000..71f9ae0ee --- /dev/null +++ b/packages/uni_app/lib/view/introduction/widgets/services_intro_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; + +class ServicesIntroPage extends StatefulWidget { + const ServicesIntroPage({super.key, required this.pageController}); + + final PageController pageController; + + @override + State createState() => _ServicesIntroPageState(); +} + +class _ServicesIntroPageState extends State { + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Align( + alignment: const Alignment(0, -0.85), + child: Text( + S.of(context).services.toUpperCase(), + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + ), + Align( + alignment: const Alignment(0, -0.7), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Text( + S.of(context).services_intro_message, + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'Poppins', + color: Colors.white, + fontSize: 16, + ), + ), + ), + ), + Align( + alignment: const Alignment(0.035, 0.5), + child: Image.asset('assets/images/faculty_intro.png', width: 260), + ), + ], + ); + } +} diff --git a/packages/uni_app/lib/view/login/login.dart b/packages/uni_app/lib/view/login/login.dart index b06460df8..beca4465e 100644 --- a/packages/uni_app/lib/view/login/login.dart +++ b/packages/uni_app/lib/view/login/login.dart @@ -60,12 +60,14 @@ class LoginPageViewState extends ConsumerState @override void initState() { super.initState(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); super.dispose(); } @@ -96,7 +98,7 @@ class LoginPageViewState extends ConsumerState if (mounted) { await Navigator.pushReplacementNamed( context, - '/${NavigationItem.navPersonalArea.route}', + '/${NavigationItem.navIntroduction.route}', ); setState(() { _loggingIn = false; @@ -167,6 +169,11 @@ class LoginPageViewState extends ConsumerState _loggingIn = true; }); } + if (mounted) { + setState(() { + _loggingIn = true; + }); + } final appLinks = UniAppLinks(); @@ -188,6 +195,12 @@ class LoginPageViewState extends ConsumerState persistentSession: _keepSignedIn, ); + if (mounted) { + setState(() { + _intercepting = true; + _loggingIn = true; + }); + } if (mounted) { setState(() { _intercepting = true; @@ -198,7 +211,7 @@ class LoginPageViewState extends ConsumerState if (mounted) { await Navigator.pushReplacementNamed( context, - '/${NavigationItem.navPersonalArea.route}', + '/${NavigationItem.navIntroduction.route}', ); } @@ -208,6 +221,12 @@ class LoginPageViewState extends ConsumerState _intercepting = false; }); } + if (mounted) { + setState(() { + _loggingIn = true; + _intercepting = false; + }); + } } catch (err, st) { await Sentry.captureException(err, stackTrace: st); await closeInAppWebView(); @@ -215,6 +234,8 @@ class LoginPageViewState extends ConsumerState setState(() { _loggingIn = false; }); + } + if (mounted) { Logger().e(S.of(context).fail_to_authenticate); unawaited( ToastMessage.error(context, S.of(context).fail_to_authenticate), @@ -227,6 +248,13 @@ class LoginPageViewState extends ConsumerState @override Widget build(BuildContext context) { + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarIconBrightness: Brightness.light, + ), + ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); return Theme( data: Theme.of(context), child: Builder( diff --git a/packages/uni_app/lib/view/splash/splash.dart b/packages/uni_app/lib/view/splash/splash.dart index 8333679a8..053a712a9 100644 --- a/packages/uni_app/lib/view/splash/splash.dart +++ b/packages/uni_app/lib/view/splash/splash.dart @@ -14,6 +14,13 @@ class SplashScreenView extends StatefulWidget { class _SplashScreenViewState extends State { @override Widget build(BuildContext context) { + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarIconBrightness: Brightness.light, + ), + ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); Future.delayed(const Duration(milliseconds: 1500), () async { if (mounted) { final route = await firstRoute(); diff --git a/packages/uni_app/lib/view/widgets/page_transition.dart b/packages/uni_app/lib/view/widgets/page_transition.dart index 15914abe1..4cba46807 100644 --- a/packages/uni_app/lib/view/widgets/page_transition.dart +++ b/packages/uni_app/lib/view/widgets/page_transition.dart @@ -1,3 +1,4 @@ +import 'package:animations/animations.dart'; import 'package:flutter/material.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/view/pedagogical_surveys_dialog.dart'; @@ -57,6 +58,26 @@ class PageTransition { ); } + static Route introductionTransitionRoute({ + required Widget page, + required RouteSettings settings, + bool maintainState = true, + }) { + return PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => page, + settings: settings, + maintainState: maintainState, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return SharedAxisTransition( + animation: animation, + secondaryAnimation: secondaryAnimation, + transitionType: SharedAxisTransitionType.vertical, + child: child, + ); + }, + ); + } + static Future requestTermsAndConditionsAcceptanceIfNeeded( BuildContext context, ) async { diff --git a/packages/uni_app/pubspec.yaml b/packages/uni_app/pubspec.yaml index a853706fa..4cd07745f 100644 --- a/packages/uni_app/pubspec.yaml +++ b/packages/uni_app/pubspec.yaml @@ -16,6 +16,7 @@ environment: dependencies: add_2_calendar_new: ^1.0.4 + animations: ^2.0.11 app_links: ^7.0.0 app_settings: git: @@ -69,6 +70,7 @@ dependencies: path: ^1.9.1 path_provider: ^2.1.5 percent_indicator: ^4.2.5 + permission_handler: ^12.0.1 plausible_analytics: ^0.3.0 sentry_flutter: ^9.7.0 shared_preferences: ^2.5.3 @@ -128,6 +130,53 @@ flutter: weight: 600 - asset: assets/fonts/Raleway-Thin.ttf weight: 100 + - family: Poppins + fonts: + - asset: assets/fonts/Poppins/Poppins-Black.ttf + weight: 900 + - asset: assets/fonts/Poppins/Poppins-BlackItalic.ttf + weight: 900 + style: italic + - asset: assets/fonts/Poppins/Poppins-Bold.ttf + weight: 700 + - asset: assets/fonts/Poppins/Poppins-BoldItalic.ttf + weight: 700 + style: italic + - asset: assets/fonts/Poppins/Poppins-ExtraBold.ttf + weight: 800 + - asset: assets/fonts/Poppins/Poppins-ExtraBoldItalic.ttf + weight: 800 + style: italic + - asset: assets/fonts/Poppins/Poppins-ExtraLight.ttf + weight: 200 + - asset: assets/fonts/Poppins/Poppins-ExtraLightItalic.ttf + weight: 200 + style: italic + - asset: assets/fonts/Poppins/Poppins-Light.ttf + weight: 300 + - asset: assets/fonts/Poppins/Poppins-LightItalic.ttf + weight: 300 + style: italic + - asset: assets/fonts/Poppins/Poppins-Medium.ttf + weight: 500 + - asset: assets/fonts/Poppins/Poppins-MediumItalic.ttf + weight: 500 + style: italic + - asset: assets/fonts/Poppins/Poppins-Regular.ttf + weight: 400 + - asset: assets/fonts/Poppins/Poppins-Italic.ttf + weight: 400 + style: italic + - asset: assets/fonts/Poppins/Poppins-SemiBold.ttf + weight: 600 + - asset: assets/fonts/Poppins/Poppins-SemiBoldItalic.ttf + weight: 600 + style: italic + - asset: assets/fonts/Poppins/Poppins-Thin.ttf + weight: 100 + - asset: assets/fonts/Poppins/Poppins-ThinItalic.ttf + weight: 100 + style: italic flutter_intl: enabled: true diff --git a/packages/uni_app/windows/flutter/generated_plugins.cmake b/packages/uni_app/windows/flutter/generated_plugins.cmake index ef31d9dc3..d611b314e 100644 --- a/packages/uni_app/windows/flutter/generated_plugins.cmake +++ b/packages/uni_app/windows/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows flutter_secure_storage_windows objectbox_flutter_libs + permission_handler_windows sentry_flutter url_launcher_windows ) diff --git a/pubspec.lock b/pubspec.lock index ed88702b7..5b3b801ba 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.4" + animations: + dependency: transitive + description: + name: animations + sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb + url: "https://pub.dev" + source: hosted + version: "2.0.11" ansicolor: dependency: transitive description: @@ -1327,6 +1335,54 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.5" + permission_handler: + dependency: transitive + description: + name: permission_handler + sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 + url: "https://pub.dev" + source: hosted + version: "12.0.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + url: "https://pub.dev" + source: hosted + version: "13.0.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: