From 4aa8159500e7ec4418c3eeddf690168796737504 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sun, 25 Jan 2026 00:44:22 -0500 Subject: [PATCH 01/28] Ajout du package et des fichiers concernant la page login --- lib/domain/constants/router_paths.dart | 1 + lib/router.dart | 6 + lib/ui/login/view_model/login_viewmodel.dart | 12 ++ lib/ui/login/widgets/login_view.dart | 105 ++++++++++++++++++ .../startup/view_model/startup_viewmodel.dart | 31 +++--- 5 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 lib/ui/login/view_model/login_viewmodel.dart create mode 100644 lib/ui/login/widgets/login_view.dart diff --git a/lib/domain/constants/router_paths.dart b/lib/domain/constants/router_paths.dart index c50e3d292..c1f37fc62 100644 --- a/lib/domain/constants/router_paths.dart +++ b/lib/domain/constants/router_paths.dart @@ -2,6 +2,7 @@ class RouterPaths { static const String startup = "/startup"; static const String faq = "/faq"; + static const String login = "/login"; static const String dashboard = "/dashboard"; static const String schedule = "/schedule"; static const String defaultSchedule = "/schedule/default"; diff --git a/lib/router.dart b/lib/router.dart index 98549dc9c..bf116cdcf 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -26,6 +26,7 @@ import 'package:notredame/ui/startup/widgets/startup_view.dart'; import 'package:notredame/ui/student/grades/grade_details/widgets/grade_details_view.dart'; import 'package:notredame/ui/student/session_schedule/widgets/session_schedule_view.dart'; import 'package:notredame/ui/student/widgets/student_view.dart'; +import 'package:notredame/ui/login/widgets/login_view.dart'; Route generateRoute(RouteSettings routeSettings) { switch (routeSettings.name) { @@ -129,6 +130,11 @@ Route generateRoute(RouteSettings routeSettings) { settings: RouteSettings(name: routeSettings.name), builder: (_) => ChooseLanguageView(), ); + case RouterPaths.login: + return MaterialPageRoute( + settings: RouteSettings(name: routeSettings.name), + builder: (_) => LoginView(), + ); default: return PageRouteBuilder( settings: RouteSettings(name: routeSettings.name), diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart new file mode 100644 index 000000000..64236b648 --- /dev/null +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -0,0 +1,12 @@ +// Package imports: +import 'package:stacked/stacked.dart'; + +// Project imports: +import 'package:notredame/l10n/app_localizations.dart'; + +class LoginViewModel extends BaseViewModel { + /// Localization class of the application. + final AppIntl _appIntl; + + LoginViewModel({required AppIntl intl}) : _appIntl = intl; +} diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart new file mode 100644 index 000000000..9ed168d02 --- /dev/null +++ b/lib/ui/login/widgets/login_view.dart @@ -0,0 +1,105 @@ +// Flutter imports: +import 'package:flutter/material.dart'; + +// Package imports: +import 'package:stacked/stacked.dart'; + +// Project imports: +import 'package:notredame/l10n/app_localizations.dart'; +import 'package:notredame/ui/login/view_model/login_viewmodel.dart'; +import 'package:notredame/ui/core/themes/app_palette.dart'; +import 'package:notredame/ui/core/themes/app_theme.dart'; + +class LoginView extends StatefulWidget { + const LoginView({super.key}); + + @override + State createState() => _LoginViewState(); +} + +class _LoginViewState extends State { + final _usernameController = TextEditingController(); + final _passwordController = TextEditingController(); + + @override + void dispose() { + _usernameController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => LoginViewModel(intl: AppIntl.of(context)!), + builder: (context, model, child) => Scaffold( + backgroundColor: context.theme.appColors.backgroundVibrant, + body: Center( + child: Padding( + padding: const EdgeInsets.all(30.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'AppETS', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 26, + color: AppPalette.grey.white, + ), + ), + Text( + 'Veuillez vous authentifier pour continuer', + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 18, + color: AppPalette.grey.white, + ), + ), + TextField( + controller: _usernameController, + decoration: InputDecoration( + labelText: 'Nom d\'utilisateur', + border: OutlineInputBorder(), + ), + ), + TextField( + controller: _passwordController, + decoration: InputDecoration( + labelText: 'Mot de passe', + border: OutlineInputBorder(), + ), + obscureText: true, + ), + SizedBox( + width: double.infinity, + height: 49, + child: ElevatedButton( + onPressed: () { + // fait rient pour l'instant + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF3B62FF), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Text( + 'Se connecter', + style: TextStyle( + color: AppPalette.grey.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/ui/startup/view_model/startup_viewmodel.dart b/lib/ui/startup/view_model/startup_viewmodel.dart index 3faef0817..8d646baca 100644 --- a/lib/ui/startup/view_model/startup_viewmodel.dart +++ b/lib/ui/startup/view_model/startup_viewmodel.dart @@ -51,22 +51,23 @@ class StartUpViewModel extends BaseViewModel { _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); _navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard); } else { - AuthenticationResult? token; - int attempts = 0; - const maxAttempts = 3; + _navigationService.pushNamedAndRemoveUntil(RouterPaths.login); + // AuthenticationResult? token; + // int attempts = 0; + // const maxAttempts = 3; + // + // while (token == null && attempts < maxAttempts) { + // attempts++; + // token = (await _authService.acquireToken()).$1; + // if (token == null && attempts >= maxAttempts) { + // Fluttertoast.showToast(msg: intl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); + // await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); + // return; + // } + //} - while (token == null && attempts < maxAttempts) { - attempts++; - token = (await _authService.acquireToken()).$1; - if (token == null && attempts >= maxAttempts) { - Fluttertoast.showToast(msg: intl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); - return; - } - } - - _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); - _navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard); + //_settingsManager.setBool(PreferencesFlag.isLoggedIn, true); + _navigationService.pushNamedAndRemoveUntil(RouterPaths.login); } } From 47be59efb35e8637f07ce64f8c2aff39cd3f00a4 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sun, 25 Jan 2026 00:48:45 -0500 Subject: [PATCH 02/28] Ajout des variables dans router pour login --- lib/router.dart | 2 +- lib/ui/login/widgets/login_view.dart | 34 ++++++---------------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/lib/router.dart b/lib/router.dart index bf116cdcf..d3ed0c070 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -13,6 +13,7 @@ import 'package:notredame/ui/ets/events/news_details/widgets/news_details_view.d import 'package:notredame/ui/ets/quick_links/security_info/widgets/security_view.dart'; import 'package:notredame/ui/ets/quick_links/widgets/quick_links_view.dart'; import 'package:notredame/ui/ets/widgets/ets_view.dart'; +import 'package:notredame/ui/login/widgets/login_view.dart'; import 'package:notredame/ui/more/about/widgets/about_view.dart'; import 'package:notredame/ui/more/contributors/widgets/contributors_view.dart'; import 'package:notredame/ui/more/faq/widgets/faq_view.dart'; @@ -26,7 +27,6 @@ import 'package:notredame/ui/startup/widgets/startup_view.dart'; import 'package:notredame/ui/student/grades/grade_details/widgets/grade_details_view.dart'; import 'package:notredame/ui/student/session_schedule/widgets/session_schedule_view.dart'; import 'package:notredame/ui/student/widgets/student_view.dart'; -import 'package:notredame/ui/login/widgets/login_view.dart'; Route generateRoute(RouteSettings routeSettings) { switch (routeSettings.name) { diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 9ed168d02..5224f40b1 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -6,9 +6,9 @@ import 'package:stacked/stacked.dart'; // Project imports: import 'package:notredame/l10n/app_localizations.dart'; -import 'package:notredame/ui/login/view_model/login_viewmodel.dart'; import 'package:notredame/ui/core/themes/app_palette.dart'; import 'package:notredame/ui/core/themes/app_theme.dart'; +import 'package:notredame/ui/login/view_model/login_viewmodel.dart'; class LoginView extends StatefulWidget { const LoginView({super.key}); @@ -43,33 +43,19 @@ class _LoginViewState extends State { children: [ Text( 'AppETS', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 26, - color: AppPalette.grey.white, - ), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26, color: AppPalette.grey.white), ), Text( 'Veuillez vous authentifier pour continuer', - style: TextStyle( - fontWeight: FontWeight.normal, - fontSize: 18, - color: AppPalette.grey.white, - ), + style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), ), TextField( controller: _usernameController, - decoration: InputDecoration( - labelText: 'Nom d\'utilisateur', - border: OutlineInputBorder(), - ), + decoration: InputDecoration(labelText: 'Nom d\'utilisateur', border: OutlineInputBorder()), ), TextField( controller: _passwordController, - decoration: InputDecoration( - labelText: 'Mot de passe', - border: OutlineInputBorder(), - ), + decoration: InputDecoration(labelText: 'Mot de passe', border: OutlineInputBorder()), obscureText: true, ), SizedBox( @@ -81,17 +67,11 @@ class _LoginViewState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF3B62FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: Text( 'Se connecter', - style: TextStyle( - color: AppPalette.grey.white, - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), ), ), ), From 63275e86b7b48352ceb99b12fc3cb99b69a51255 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:36:07 -0500 Subject: [PATCH 03/28] premier visuel complete pour login --- lib/ui/login/widgets/login_view.dart | 52 ++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 5224f40b1..79a76cf6c 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -41,22 +41,46 @@ class _LoginViewState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - 'AppETS', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26, color: AppPalette.grey.white), + Padding( + padding: const EdgeInsets.only(bottom: 20), + child: Text( + 'AppETS', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26, color: AppPalette.grey.white), + ), ), - Text( - 'Veuillez vous authentifier pour continuer', - style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), + Padding( + padding: const EdgeInsets.only(bottom: 30), + child: Text( + 'Veuillez vous authentifier pour continuer', + style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), + ), ), - TextField( - controller: _usernameController, - decoration: InputDecoration(labelText: 'Nom d\'utilisateur', border: OutlineInputBorder()), + Padding( + padding: const EdgeInsets.only(bottom: 30), + child: TextField( + controller: _usernameController, + decoration: InputDecoration( + labelText: 'Nom d\'utilisateur', + border: OutlineInputBorder(), + labelStyle: TextStyle(color: AppPalette.grey.white), + filled: true, + fillColor: AppPalette.grey.darkGrey, + ), + ), ), - TextField( - controller: _passwordController, - decoration: InputDecoration(labelText: 'Mot de passe', border: OutlineInputBorder()), - obscureText: true, + Padding( + padding: const EdgeInsets.only(bottom: 30), + child: TextField( + controller: _passwordController, + decoration: InputDecoration( + labelText: 'Mot de passe', + border: OutlineInputBorder(), + labelStyle: TextStyle(color: AppPalette.grey.white), + filled: true, + fillColor: AppPalette.grey.darkGrey, + ), + obscureText: true, + ), ), SizedBox( width: double.infinity, @@ -66,7 +90,7 @@ class _LoginViewState extends State { // fait rient pour l'instant }, style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF3B62FF), + backgroundColor: AppPalette.grey.black, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: Text( From 50ee34f468de3856f9cebc65d581af9ecc40ac4b Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sat, 7 Feb 2026 00:35:07 -0500 Subject: [PATCH 04/28] meilleur respect de l'issue, vue refaite --- lib/ui/login/view_model/login_viewmodel.dart | 36 ++++++++++++++++ lib/ui/login/widgets/login_view.dart | 42 +++++++------------ .../startup/view_model/startup_viewmodel.dart | 7 ++-- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index 64236b648..66c726692 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -1,12 +1,48 @@ // Package imports: +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:msal_auth/msal_auth.dart'; import 'package:stacked/stacked.dart'; // Project imports: +import 'package:notredame/data/repositories/settings_repository.dart'; +import 'package:notredame/data/services/analytics_service.dart'; +import 'package:notredame/data/services/auth_service.dart'; +import 'package:notredame/data/services/navigation_service.dart'; +import 'package:notredame/domain/constants/preferences_flags.dart'; +import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; +import 'package:notredame/locator.dart'; class LoginViewModel extends BaseViewModel { /// Localization class of the application. final AppIntl _appIntl; LoginViewModel({required AppIntl intl}) : _appIntl = intl; + + Future authenticate() async { + final SettingsRepository _settingsManager = locator(); + final AuthService _authService = locator(); + final AnalyticsService _analyticsService = locator(); + + AuthenticationResult? token; + int attempts = 0; + const maxAttempts = 3; + + while (token == null && attempts < maxAttempts) { + attempts++; + token = (await _authService.acquireToken()).$1; + if (token == null && attempts >= maxAttempts) { + Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); + await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); + return; + } + } + + _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); + } + + void navigateToFAQ() { + final NavigationService _navigationService = locator(); + _navigationService.pushNamedAndRemoveUntil(RouterPaths.faq); + } } diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 79a76cf6c..9862c2240 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -56,45 +56,33 @@ class _LoginViewState extends State { ), ), Padding( - padding: const EdgeInsets.only(bottom: 30), - child: TextField( - controller: _usernameController, - decoration: InputDecoration( - labelText: 'Nom d\'utilisateur', - border: OutlineInputBorder(), - labelStyle: TextStyle(color: AppPalette.grey.white), - filled: true, - fillColor: AppPalette.grey.darkGrey, + padding: const EdgeInsets.only(bottom: 20), + child: ElevatedButton( + onPressed: () { + model.authenticate(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppPalette.grey.black, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 30), - child: TextField( - controller: _passwordController, - decoration: InputDecoration( - labelText: 'Mot de passe', - border: OutlineInputBorder(), - labelStyle: TextStyle(color: AppPalette.grey.white), - filled: true, - fillColor: AppPalette.grey.darkGrey, + child: Text( + 'Se connecter', + style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), ), - obscureText: true, ), ), - SizedBox( - width: double.infinity, - height: 49, + Padding( + padding: const EdgeInsets.only(bottom: 20), child: ElevatedButton( onPressed: () { - // fait rient pour l'instant + model.navigateToFAQ(); }, style: ElevatedButton.styleFrom( backgroundColor: AppPalette.grey.black, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: Text( - 'Se connecter', + 'FAQ', style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), ), ), diff --git a/lib/ui/startup/view_model/startup_viewmodel.dart b/lib/ui/startup/view_model/startup_viewmodel.dart index 8d646baca..2d79fdf0d 100644 --- a/lib/ui/startup/view_model/startup_viewmodel.dart +++ b/lib/ui/startup/view_model/startup_viewmodel.dart @@ -64,10 +64,9 @@ class StartUpViewModel extends BaseViewModel { // await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); // return; // } - //} - - //_settingsManager.setBool(PreferencesFlag.isLoggedIn, true); - _navigationService.pushNamedAndRemoveUntil(RouterPaths.login); + // } + // + // _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); } } From e6b875dae245df2d4fb87f7e281a626b618b702a Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sun, 15 Feb 2026 23:58:21 -0500 Subject: [PATCH 05/28] Page login complete --- lib/ui/login/view_model/login_viewmodel.dart | 26 +++++++++---------- lib/ui/login/widgets/login_view.dart | 12 ++++++++- .../startup/view_model/startup_viewmodel.dart | 16 ------------ 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index 66c726692..0976792e7 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -1,6 +1,7 @@ -// Package imports: +// import 'package:fluttertoast/fluttertoast.dart'; import 'package:msal_auth/msal_auth.dart'; +import 'package:notredame/domain/constants/router_paths.dart'; import 'package:stacked/stacked.dart'; // Project imports: @@ -9,7 +10,6 @@ import 'package:notredame/data/services/analytics_service.dart'; import 'package:notredame/data/services/auth_service.dart'; import 'package:notredame/data/services/navigation_service.dart'; import 'package:notredame/domain/constants/preferences_flags.dart'; -import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; import 'package:notredame/locator.dart'; @@ -17,13 +17,14 @@ class LoginViewModel extends BaseViewModel { /// Localization class of the application. final AppIntl _appIntl; - LoginViewModel({required AppIntl intl}) : _appIntl = intl; + final NavigationService navigationService = locator(); + final SettingsRepository _settingsManager = locator(); + final AuthService _authService = locator(); + final AnalyticsService _analyticsService = locator(); - Future authenticate() async { - final SettingsRepository _settingsManager = locator(); - final AuthService _authService = locator(); - final AnalyticsService _analyticsService = locator(); + LoginViewModel({required AppIntl intl}) : _appIntl = intl; + void authenticate() async { AuthenticationResult? token; int attempts = 0; const maxAttempts = 3; @@ -32,17 +33,14 @@ class LoginViewModel extends BaseViewModel { attempts++; token = (await _authService.acquireToken()).$1; if (token == null && attempts >= maxAttempts) { - Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); - return; + Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); + await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); + return; } } _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); - } - void navigateToFAQ() { - final NavigationService _navigationService = locator(); - _navigationService.pushNamedAndRemoveUntil(RouterPaths.faq); + navigationService.pushNamedAndRemoveUntil(RouterPaths.startup); } } diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 9862c2240..b3aaa818c 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; // Project imports: +import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; import 'package:notredame/ui/core/themes/app_palette.dart'; import 'package:notredame/ui/core/themes/app_theme.dart'; @@ -21,6 +22,8 @@ class _LoginViewState extends State { final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); + bool _isLoading = false; + @override void dispose() { _usernameController.dispose(); @@ -59,6 +62,9 @@ class _LoginViewState extends State { padding: const EdgeInsets.only(bottom: 20), child: ElevatedButton( onPressed: () { + setState(() { + _isLoading = true; + }); model.authenticate(); }, style: ElevatedButton.styleFrom( @@ -75,7 +81,7 @@ class _LoginViewState extends State { padding: const EdgeInsets.only(bottom: 20), child: ElevatedButton( onPressed: () { - model.navigateToFAQ(); + model.navigationService.pushNamed(RouterPaths.faq); }, style: ElevatedButton.styleFrom( backgroundColor: AppPalette.grey.black, @@ -87,6 +93,10 @@ class _LoginViewState extends State { ), ), ), + if (_isLoading) ... [ + const SizedBox(height: 15), + CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), + ] ], ), ), diff --git a/lib/ui/startup/view_model/startup_viewmodel.dart b/lib/ui/startup/view_model/startup_viewmodel.dart index 2d79fdf0d..ec1404b56 100644 --- a/lib/ui/startup/view_model/startup_viewmodel.dart +++ b/lib/ui/startup/view_model/startup_viewmodel.dart @@ -1,5 +1,4 @@ // Package imports: -import 'package:fluttertoast/fluttertoast.dart'; import 'package:msal_auth/msal_auth.dart'; import 'package:stacked/stacked.dart'; @@ -52,21 +51,6 @@ class StartUpViewModel extends BaseViewModel { _navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard); } else { _navigationService.pushNamedAndRemoveUntil(RouterPaths.login); - // AuthenticationResult? token; - // int attempts = 0; - // const maxAttempts = 3; - // - // while (token == null && attempts < maxAttempts) { - // attempts++; - // token = (await _authService.acquireToken()).$1; - // if (token == null && attempts >= maxAttempts) { - // Fluttertoast.showToast(msg: intl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - // await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); - // return; - // } - // } - // - // _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); } } From 868ea8bc1b4d2b31f8c31898929c60bc4467cee5 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Mon, 16 Feb 2026 00:00:12 -0500 Subject: [PATCH 06/28] Page login complete --- lib/ui/login/view_model/login_viewmodel.dart | 10 ++++++---- lib/ui/login/widgets/login_view.dart | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index 0976792e7..bbe920548 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -1,7 +1,8 @@ // + +// Package imports: import 'package:fluttertoast/fluttertoast.dart'; import 'package:msal_auth/msal_auth.dart'; -import 'package:notredame/domain/constants/router_paths.dart'; import 'package:stacked/stacked.dart'; // Project imports: @@ -10,6 +11,7 @@ import 'package:notredame/data/services/analytics_service.dart'; import 'package:notredame/data/services/auth_service.dart'; import 'package:notredame/data/services/navigation_service.dart'; import 'package:notredame/domain/constants/preferences_flags.dart'; +import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; import 'package:notredame/locator.dart'; @@ -33,9 +35,9 @@ class LoginViewModel extends BaseViewModel { attempts++; token = (await _authService.acquireToken()).$1; if (token == null && attempts >= maxAttempts) { - Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); - return; + Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); + await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); + return; } } diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index b3aaa818c..a8bcd340c 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -93,10 +93,10 @@ class _LoginViewState extends State { ), ), ), - if (_isLoading) ... [ + if (_isLoading) ...[ const SizedBox(height: 15), CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), - ] + ], ], ), ), From 9f4f796d834ef7c4f3611ff3896929406b15cba9 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:56:51 -0500 Subject: [PATCH 07/28] version anglais/francais --- lib/ui/login/widgets/login_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index a8bcd340c..349de1ff8 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -54,7 +54,7 @@ class _LoginViewState extends State { Padding( padding: const EdgeInsets.only(bottom: 30), child: Text( - 'Veuillez vous authentifier pour continuer', + AppIntl.of(context)!.startup_title, style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), ), ), @@ -72,7 +72,7 @@ class _LoginViewState extends State { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: Text( - 'Se connecter', + AppIntl.of(context)!.login_action_sign_in, style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), ), ), From a36967dad8c6fb0be6df4cf441d394acf06350be Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Thu, 26 Feb 2026 20:03:59 -0500 Subject: [PATCH 08/28] ajout dans localization en et fr --- lib/l10n/intl_en.arb | 1 + lib/l10n/intl_fr.arb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 76a956356..bbdbe3178 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -57,6 +57,7 @@ "title_ets_mobile": "ÉTSMobile", "login_title": "Login", + "startup_title": "Please login to continue", "login_prompt_universal_code": "Universal Code", "login_prompt_password": "Password", "login_action_sign_in": "Sign in", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 1509e45c5..900cf9406 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -56,6 +56,7 @@ "title_ets_mobile": "ÉTSMobile", "login_title": "Connexion", + "startup_title": "Veuillez vous authentifier pour continuer", "login_prompt_universal_code": "Code universel", "login_prompt_password": "Mot de passe", "login_action_sign_in": "Se connecter", From b5ad9ae6e4703acaedc616f7910386932d77d869 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Fri, 27 Feb 2026 07:00:19 -0500 Subject: [PATCH 09/28] changement du texte de la version anglaise --- lib/l10n/intl_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 6e31c970d..f648d68dc 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -57,7 +57,7 @@ "title_ets_mobile": "ÉTSMobile", "login_title": "Login", - "startup_title": "Please login to continue", + "startup_title": "Please sign in to continue", "login_prompt_universal_code": "Universal Code", "login_prompt_password": "Password", "login_action_sign_in": "Sign in", From 88268c7786f0106e74be65b60717a49f317d07f6 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Fri, 27 Feb 2026 07:32:07 -0500 Subject: [PATCH 10/28] tests + changement nom variable + enlevement code inutile --- lib/l10n/intl_en.arb | 2 +- lib/l10n/intl_fr.arb | 2 +- lib/ui/login/widgets/login_view.dart | 12 +----------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index f648d68dc..c952b7b66 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -57,7 +57,7 @@ "title_ets_mobile": "ÉTSMobile", "login_title": "Login", - "startup_title": "Please sign in to continue", + "login_startup_title": "Please sign in to continue", "login_prompt_universal_code": "Universal Code", "login_prompt_password": "Password", "login_action_sign_in": "Sign in", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 35a3db71b..ce741eb3e 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -56,7 +56,7 @@ "title_ets_mobile": "ÉTSMobile", "login_title": "Connexion", - "startup_title": "Veuillez vous authentifier pour continuer", + "login_startup_title": "Veuillez vous authentifier pour continuer", "login_prompt_universal_code": "Code universel", "login_prompt_password": "Mot de passe", "login_action_sign_in": "Se connecter", diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 349de1ff8..a0876654d 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -19,18 +19,8 @@ class LoginView extends StatefulWidget { } class _LoginViewState extends State { - final _usernameController = TextEditingController(); - final _passwordController = TextEditingController(); - bool _isLoading = false; - @override - void dispose() { - _usernameController.dispose(); - _passwordController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( @@ -54,7 +44,7 @@ class _LoginViewState extends State { Padding( padding: const EdgeInsets.only(bottom: 30), child: Text( - AppIntl.of(context)!.startup_title, + AppIntl.of(context)!.login_startup_title, style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), ), ), From f15d4fb3b6ad89fb261ce2d90eacf8c113aed00a Mon Sep 17 00:00:00 2001 From: Shafaatul-Islam <114493805+Shafaatul-Islam@users.noreply.github.com> Date: Fri, 27 Feb 2026 12:42:24 +0000 Subject: [PATCH 11/28] [BOT] Applying version. --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 2e75f0303..e76b5194e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: The 4th generation of ÉTSMobile, the main gateway between the Éco # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.63.2 +version: 4.64.0 environment: sdk: '>=3.9.0 <4.0.0' From 6aac8325a4cba8ca7bbdc5e1873f1bdc464b4239 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Fri, 6 Mar 2026 19:48:36 -0500 Subject: [PATCH 12/28] pull 5.9.2 --- linux/flutter/generated_plugin_registrant.cc | 19 ++++++++++ linux/flutter/generated_plugin_registrant.h | 15 ++++++++ linux/flutter/generated_plugins.cmake | 25 +++++++++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 36 +++++++++++++++++++ .../ephemeral/Flutter-Generated.xcconfig | 11 ++++++ .../ephemeral/flutter_export_environment.sh | 12 +++++++ 6 files changed, 118 insertions(+) create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 macos/Flutter/ephemeral/Flutter-Generated.xcconfig create mode 100644 macos/Flutter/ephemeral/flutter_export_environment.sh diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..38dd0bc6c --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..65240e99c --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,25 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..f31fa2b8b --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,36 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import connectivity_plus +import firebase_analytics +import firebase_core +import firebase_crashlytics +import firebase_remote_config +import flutter_secure_storage_darwin +import in_app_review +import msal_auth +import package_info_plus +import share_plus +import shared_preferences_foundation +import sqflite_darwin +import url_launcher_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) + FirebaseRemoteConfigPlugin.register(with: registry.registrar(forPlugin: "FirebaseRemoteConfigPlugin")) + FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin")) + InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) + MsalAuthPlugin.register(with: registry.registrar(forPlugin: "MsalAuthPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) +} diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 000000000..0cb1b286f --- /dev/null +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,11 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\Users\sgoku\flutter +FLUTTER_APPLICATION_PATH=C:\Projects\Notre-Dame +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=4.64.1 +FLUTTER_BUILD_NUMBER=4.64.1 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100644 index 000000000..aceff951e --- /dev/null +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\Users\sgoku\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Projects\Notre-Dame" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=4.64.1" +export "FLUTTER_BUILD_NUMBER=4.64.1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" From 1dc31945898cbeab817749eecda56f009f9e5331 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:58:53 -0500 Subject: [PATCH 13/28] changement du logo dans la page de login --- lib/ui/login/widgets/login_view.dart | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index a0876654d..b61975641 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; // Package imports: +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:stacked/stacked.dart'; // Project imports: @@ -36,9 +38,15 @@ class _LoginViewState extends State { children: [ Padding( padding: const EdgeInsets.only(bottom: 20), - child: Text( - 'AppETS', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 26, color: AppPalette.grey.white), + child: Hero( + tag: 'ets_logo', + child: SvgPicture.asset( + "assets/images/ets_white_logo.svg", + excludeFromSemantics: true, + width: 90, + height: 90, + colorFilter: ColorFilter.mode(context.theme.appColors.loginAccent, BlendMode.srcIn), + ), ), ), Padding( @@ -69,18 +77,12 @@ class _LoginViewState extends State { ), Padding( padding: const EdgeInsets.only(bottom: 20), - child: ElevatedButton( + child: IconButton( + tooltip: "FAQ", onPressed: () { model.navigationService.pushNamed(RouterPaths.faq); }, - style: ElevatedButton.styleFrom( - backgroundColor: AppPalette.grey.black, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - ), - child: Text( - 'FAQ', - style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), - ), + icon: const FaIcon(FontAwesomeIcons.instagram, color: Colors.white), ), ), if (_isLoading) ...[ From 848b331a4b3f272f2e28456ef8e8e1b44c85d360 Mon Sep 17 00:00:00 2001 From: Shafaatul-Islam <114493805+Shafaatul-Islam@users.noreply.github.com> Date: Sat, 7 Mar 2026 03:00:28 +0000 Subject: [PATCH 14/28] [BOT] Applying format. --- lib/ui/login/widgets/login_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index b61975641..88e510304 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; // Package imports: -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:stacked/stacked.dart'; // Project imports: From 3cbcc59b637212874cd7963a0e050b3557ff65f8 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:44:08 -0500 Subject: [PATCH 15/28] ajout des icones aux boutons --- lib/ui/login/widgets/login_view.dart | 58 ++++++++++++++++------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index b61975641..dac62dc7b 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; // Package imports: -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:stacked/stacked.dart'; // Project imports: @@ -58,37 +58,47 @@ class _LoginViewState extends State { ), Padding( padding: const EdgeInsets.only(bottom: 20), - child: ElevatedButton( - onPressed: () { - setState(() { - _isLoading = true; - }); - model.authenticate(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: AppPalette.grey.black, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - ), - child: Text( - AppIntl.of(context)!.login_action_sign_in, - style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), - ), - ), + child: Stack( + alignment: Alignment.centerRight, + children: [ + ElevatedButton.icon( + onPressed: () { + setState(() { + _isLoading = true; + }); + model.authenticate(); + }, + icon: const FaIcon(FontAwesomeIcons.lockOpen, color: Colors.white), + label: Text( + AppIntl.of(context)!.login_action_sign_in, + style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), + ), + style: ElevatedButton.styleFrom( + backgroundColor: AppPalette.grey.darkGrey, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + minimumSize: const Size(300, 50) + ), + ), + if (_isLoading) ...[ + CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), + ], + ] ), + ), Padding( padding: const EdgeInsets.only(bottom: 20), - child: IconButton( - tooltip: "FAQ", + child: ElevatedButton.icon( onPressed: () { model.navigationService.pushNamed(RouterPaths.faq); }, - icon: const FaIcon(FontAwesomeIcons.instagram, color: Colors.white), + icon: const FaIcon(FontAwesomeIcons.question, color: Colors.white), + label: Text("FAQ"), + style: ElevatedButton.styleFrom( + backgroundColor: AppPalette.grey.black, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), ), ), - if (_isLoading) ...[ - const SizedBox(height: 15), - CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), - ], ], ), ), From 7db1803c104d6131e8edea6f4ebe8e831b33c5e3 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:47:09 -0500 Subject: [PATCH 16/28] ajout des icones aux boutons --- lib/ui/login/widgets/login_view.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index dac62dc7b..464855572 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -64,9 +64,9 @@ class _LoginViewState extends State { ElevatedButton.icon( onPressed: () { setState(() { - _isLoading = true; + _isLoading = true; }); - model.authenticate(); + model.authenticate(); }, icon: const FaIcon(FontAwesomeIcons.lockOpen, color: Colors.white), label: Text( @@ -76,15 +76,15 @@ class _LoginViewState extends State { style: ElevatedButton.styleFrom( backgroundColor: AppPalette.grey.darkGrey, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - minimumSize: const Size(300, 50) + minimumSize: const Size(300, 50), ), ), if (_isLoading) ...[ CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), ], - ] + ], + ), ), - ), Padding( padding: const EdgeInsets.only(bottom: 20), child: ElevatedButton.icon( From 11f55f9355b84b32ec34995857a2f18fb1ffa6b6 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Tue, 31 Mar 2026 18:07:26 -0400 Subject: [PATCH 17/28] fix workflow + debut fix test --- lib/router.dart | 2 +- lib/ui/login/widgets/login_view.dart | 2 +- .../view_model/login_viewmodel_test.dart | 69 +++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 test/ui/login/view_model/login_viewmodel_test.dart diff --git a/lib/router.dart b/lib/router.dart index 8f3fab3f0..376bf496e 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -139,7 +139,7 @@ Route generateRoute(RouteSettings routeSettings) { case RouterPaths.login: return MaterialPageRoute( settings: RouteSettings(name: routeSettings.name), - builder: (_) => LoginView(), + builder: (_) => const LoginView(), ); default: return PageRouteBuilder( diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 464855572..16d35eda4 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -92,7 +92,7 @@ class _LoginViewState extends State { model.navigationService.pushNamed(RouterPaths.faq); }, icon: const FaIcon(FontAwesomeIcons.question, color: Colors.white), - label: Text("FAQ"), + label: const Text("FAQ"), style: ElevatedButton.styleFrom( backgroundColor: AppPalette.grey.black, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), diff --git a/test/ui/login/view_model/login_viewmodel_test.dart b/test/ui/login/view_model/login_viewmodel_test.dart new file mode 100644 index 000000000..0c2013850 --- /dev/null +++ b/test/ui/login/view_model/login_viewmodel_test.dart @@ -0,0 +1,69 @@ +// Package imports: +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +// Project imports: +import 'package:notredame/data/repositories/settings_repository.dart'; +import 'package:notredame/data/repositories/user_repository.dart'; +import 'package:notredame/data/services/analytics_service.dart'; +import 'package:notredame/data/services/auth_service.dart'; +import 'package:notredame/data/services/navigation_service.dart'; +import 'package:notredame/data/services/networking_service.dart'; +import 'package:notredame/domain/constants/preferences_flags.dart'; +import 'package:notredame/domain/constants/router_paths.dart'; +import 'package:notredame/l10n/app_localizations.dart'; +import 'package:notredame/ui/login/view_model/login_viewmodel.dart'; +import '../../../data/mocks/repositories/settings_repository_mock.dart'; +import '../../../data/mocks/services/auth_service_mock.dart'; +import '../../../data/mocks/services/navigation_service_mock.dart'; +import '../../../data/mocks/services/networking_service_mock.dart'; +import '../../../helpers.dart'; + +void main() { + late NavigationServiceMock navigationServiceMock; + late SettingsRepositoryMock settingsRepositoryMock; + late NetworkingServiceMock networkingServiceMock; + late AuthServiceMock authServiceMock; + + late LoginViewModel viewModel; + late AppIntl appIntl; + + group('StartupViewModel - ', () { + setUp(() async { + setupAnalyticsServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); + settingsRepositoryMock = setupSettingsRepositoryMock(); + networkingServiceMock = setupNetworkingServiceMock(); + authServiceMock = setupAuthServiceMock(); + + appIntl = await setupAppIntl(); + viewModel = LoginViewModel(intl: appIntl); + }); + + tearDown(() { + unregister(); + unregister(); + unregister(); + unregister(); + unregister(); + unregister(); + }); + + group('handleStartUp - ', () { + test('silent sign in failed redirect to login', () async { + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); + SettingsRepositoryMock.stubGetBool(settingsRepositoryMock, PreferencesFlag.languageChoice, toReturn: true); + AuthServiceMock.stubCreatePublicClientApplication(authServiceMock); + AuthServiceMock.stubAcquireTokenSilent(authServiceMock, success: false); + AuthServiceMock.stubAcquireToken(authServiceMock, success: true); + + viewModel.authenticate(); + + verify(authServiceMock.acquireTokenSilent()).called(1); + verify(authServiceMock.acquireToken()).called(1); + verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.dashboard)); + verify(settingsRepositoryMock.setBool(PreferencesFlag.isLoggedIn, true)).called(1); + }); + }); + }); +} From fad1eedfc1ae7ac063c1709911ee34d93e1578d1 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:32:26 -0400 Subject: [PATCH 18/28] mouvement des tests de startup a login --- lib/ui/login/view_model/login_viewmodel.dart | 4 +-- .../view_model/login_viewmodel_test.dart | 25 ++++++++----------- .../view_model/startup_viewmodel_test.dart | 6 ++--- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index bbe920548..d99347997 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -19,14 +19,14 @@ class LoginViewModel extends BaseViewModel { /// Localization class of the application. final AppIntl _appIntl; - final NavigationService navigationService = locator(); final SettingsRepository _settingsManager = locator(); final AuthService _authService = locator(); + final NavigationService navigationService = locator(); final AnalyticsService _analyticsService = locator(); LoginViewModel({required AppIntl intl}) : _appIntl = intl; - void authenticate() async { + Future authenticate() async { AuthenticationResult? token; int attempts = 0; const maxAttempts = 3; diff --git a/test/ui/login/view_model/login_viewmodel_test.dart b/test/ui/login/view_model/login_viewmodel_test.dart index 0c2013850..a632d3aae 100644 --- a/test/ui/login/view_model/login_viewmodel_test.dart +++ b/test/ui/login/view_model/login_viewmodel_test.dart @@ -28,7 +28,7 @@ void main() { late LoginViewModel viewModel; late AppIntl appIntl; - group('StartupViewModel - ', () { + group('LoginViewModel - ', () { setUp(() async { setupAnalyticsServiceMock(); navigationServiceMock = setupNavigationServiceMock(); @@ -49,21 +49,18 @@ void main() { unregister(); }); - group('handleStartUp - ', () { - test('silent sign in failed redirect to login', () async { - NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); - SettingsRepositoryMock.stubGetBool(settingsRepositoryMock, PreferencesFlag.languageChoice, toReturn: true); - AuthServiceMock.stubCreatePublicClientApplication(authServiceMock); - AuthServiceMock.stubAcquireTokenSilent(authServiceMock, success: false); - AuthServiceMock.stubAcquireToken(authServiceMock, success: true); + test('silent sign in failed redirect to startup', () async { + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); + SettingsRepositoryMock.stubGetBool(settingsRepositoryMock, PreferencesFlag.languageChoice, toReturn: true); + AuthServiceMock.stubCreatePublicClientApplication(authServiceMock); + AuthServiceMock.stubAcquireTokenSilent(authServiceMock, success: false); + AuthServiceMock.stubAcquireToken(authServiceMock, success: true); - viewModel.authenticate(); + await viewModel.authenticate(); - verify(authServiceMock.acquireTokenSilent()).called(1); - verify(authServiceMock.acquireToken()).called(1); - verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.dashboard)); - verify(settingsRepositoryMock.setBool(PreferencesFlag.isLoggedIn, true)).called(1); - }); + verify(authServiceMock.acquireToken()).called(1); + verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.startup)); + verify(settingsRepositoryMock.setBool(PreferencesFlag.isLoggedIn, true)).called(1); }); }); } diff --git a/test/ui/startup/view_model/startup_viewmodel_test.dart b/test/ui/startup/view_model/startup_viewmodel_test.dart index 0d4a5118d..38f52687a 100644 --- a/test/ui/startup/view_model/startup_viewmodel_test.dart +++ b/test/ui/startup/view_model/startup_viewmodel_test.dart @@ -73,9 +73,7 @@ void main() { await viewModel.handleStartUp(); verify(authServiceMock.acquireTokenSilent()).called(1); - verify(authServiceMock.acquireToken()).called(1); - verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.dashboard)); - verify(settingsRepositoryMock.setBool(PreferencesFlag.isLoggedIn, true)).called(1); + verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.login)); }); test('navigates to chooseLanguage if language not chosen', () async { @@ -96,4 +94,4 @@ void main() { }); }); }); -} +} \ No newline at end of file From 525f24988e3a5d34e4fcc526a7ff97efbaafc007 Mon Sep 17 00:00:00 2001 From: Shafaatul-Islam <114493805+Shafaatul-Islam@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:35:29 +0000 Subject: [PATCH 19/28] [BOT] Applying format. --- test/ui/startup/view_model/startup_viewmodel_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ui/startup/view_model/startup_viewmodel_test.dart b/test/ui/startup/view_model/startup_viewmodel_test.dart index 38f52687a..d55b3c081 100644 --- a/test/ui/startup/view_model/startup_viewmodel_test.dart +++ b/test/ui/startup/view_model/startup_viewmodel_test.dart @@ -94,4 +94,4 @@ void main() { }); }); }); -} \ No newline at end of file +} From a8513526da8d7ad7a02427035cc21ec75ec811ad Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:30:01 -0400 Subject: [PATCH 20/28] ajout du gradient et espacement entre les boutons --- lib/ui/login/widgets/login_view.dart | 145 +++++++++++++++------------ 1 file changed, 81 insertions(+), 64 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 16d35eda4..eddc0463d 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -29,80 +29,97 @@ class _LoginViewState extends State { viewModelBuilder: () => LoginViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => Scaffold( backgroundColor: context.theme.appColors.backgroundVibrant, - body: Center( - child: Padding( - padding: const EdgeInsets.all(30.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: Hero( - tag: 'ets_logo', - child: SvgPicture.asset( - "assets/images/ets_white_logo.svg", - excludeFromSemantics: true, - width: 90, - height: 90, - colorFilter: ColorFilter.mode(context.theme.appColors.loginAccent, BlendMode.srcIn), + body: Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + context.theme.appColors.backgroundVibrant, + AppPalette.etsDarkRed, + ], + ), + ), + child:Center( + child: Padding( + padding: const EdgeInsets.all(30.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 20), + child: Hero( + tag: 'ets_logo', + child: SvgPicture.asset( + "assets/images/ets_white_logo.svg", + excludeFromSemantics: true, + width: 90, + height: 90, + colorFilter: ColorFilter.mode(context.theme.appColors.loginAccent, BlendMode.srcIn), + ), ), ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 30), - child: Text( - AppIntl.of(context)!.login_startup_title, - style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), + Padding( + padding: const EdgeInsets.only(bottom: 30), + child: Text( + AppIntl.of(context)!.login_startup_title, + style: TextStyle(fontWeight: FontWeight.normal, fontSize: 18, color: AppPalette.grey.white), + ), ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: Stack( - alignment: Alignment.centerRight, - children: [ - ElevatedButton.icon( - onPressed: () { - setState(() { - _isLoading = true; - }); - model.authenticate(); - }, - icon: const FaIcon(FontAwesomeIcons.lockOpen, color: Colors.white), - label: Text( - AppIntl.of(context)!.login_action_sign_in, - style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), - ), - style: ElevatedButton.styleFrom( - backgroundColor: AppPalette.grey.darkGrey, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - minimumSize: const Size(300, 50), + Padding( + padding: const EdgeInsets.only(bottom: 40), + child: Stack( + alignment: Alignment.centerRight, + children: [ + ElevatedButton.icon( + onPressed: () { + setState(() { + _isLoading = true; + }); + model.authenticate(); + }, + icon: const FaIcon(FontAwesomeIcons.lockOpen, color: Colors.white), + label: Text( + AppIntl.of(context)!.login_action_sign_in, + style: TextStyle(color: AppPalette.grey.white, fontSize: 16, fontWeight: FontWeight.bold), + ), + style: ElevatedButton.styleFrom( + backgroundColor: AppPalette.grey.black, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + minimumSize: const Size(300, 50), + ), ), - ), - if (_isLoading) ...[ - CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), + if (_isLoading) ...[ + CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppPalette.grey.white)), + ], ], - ], + ), ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 20), - child: ElevatedButton.icon( - onPressed: () { - model.navigationService.pushNamed(RouterPaths.faq); - }, - icon: const FaIcon(FontAwesomeIcons.question, color: Colors.white), - label: const Text("FAQ"), - style: ElevatedButton.styleFrom( - backgroundColor: AppPalette.grey.black, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + Padding( + padding: const EdgeInsets.only(bottom: 20), + child: ElevatedButton.icon( + onPressed: () { + model.navigationService.pushNamed(RouterPaths.faq); + }, + icon: const FaIcon(FontAwesomeIcons.question, color: Colors.white), + label: Text( + "FAQ", + style: TextStyle(color: AppPalette.grey.white, fontSize: 13, fontWeight: FontWeight.bold), + ), + style: ElevatedButton.styleFrom( + backgroundColor: AppPalette.grey.darkGrey, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), ), ), - ), - ], + ], + ), ), ), - ), + ) ), ); } From d38495d28a510a6b53c20023e4b960216249a179 Mon Sep 17 00:00:00 2001 From: Shafaatul-Islam <114493805+Shafaatul-Islam@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:38:12 +0000 Subject: [PATCH 21/28] [BOT] Applying format. --- lib/ui/login/widgets/login_view.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index eddc0463d..2894db0c0 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -36,13 +36,10 @@ class _LoginViewState extends State { gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [ - context.theme.appColors.backgroundVibrant, - AppPalette.etsDarkRed, - ], + colors: [context.theme.appColors.backgroundVibrant, AppPalette.etsDarkRed], ), ), - child:Center( + child: Center( child: Padding( padding: const EdgeInsets.all(30.0), child: Column( @@ -119,7 +116,7 @@ class _LoginViewState extends State { ), ), ), - ) + ), ), ); } From 70e5b87884c44075c27d7084085b89027672181b Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:47:45 -0400 Subject: [PATCH 22/28] deletion of the linux and macos folders+changes after mergin with V5+new tests for login_view --- lib/ui/login/view_model/login_viewmodel.dart | 20 ++++------- lib/ui/login/widgets/login_view.dart | 8 +++-- .../startup/view_model/startup_viewmodel.dart | 16 --------- linux/flutter/generated_plugin_registrant.cc | 19 ---------- linux/flutter/generated_plugin_registrant.h | 15 -------- linux/flutter/generated_plugins.cmake | 25 ------------- macos/Flutter/GeneratedPluginRegistrant.swift | 36 ------------------- .../ephemeral/Flutter-Generated.xcconfig | 11 ------ .../ephemeral/flutter_export_environment.sh | 12 ------- .../view_model/login_viewmodel_test.dart | 7 ++-- .../view_model/startup_viewmodel_test.dart | 3 -- 11 files changed, 16 insertions(+), 156 deletions(-) delete mode 100644 linux/flutter/generated_plugin_registrant.cc delete mode 100644 linux/flutter/generated_plugin_registrant.h delete mode 100644 linux/flutter/generated_plugins.cmake delete mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 macos/Flutter/ephemeral/Flutter-Generated.xcconfig delete mode 100644 macos/Flutter/ephemeral/flutter_export_environment.sh diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index d99347997..c0a078857 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -10,7 +10,6 @@ import 'package:notredame/data/repositories/settings_repository.dart'; import 'package:notredame/data/services/analytics_service.dart'; import 'package:notredame/data/services/auth_service.dart'; import 'package:notredame/data/services/navigation_service.dart'; -import 'package:notredame/domain/constants/preferences_flags.dart'; import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; import 'package:notredame/locator.dart'; @@ -28,20 +27,15 @@ class LoginViewModel extends BaseViewModel { Future authenticate() async { AuthenticationResult? token; - int attempts = 0; - const maxAttempts = 3; - - while (token == null && attempts < maxAttempts) { - attempts++; - token = (await _authService.acquireToken()).$1; - if (token == null && attempts >= maxAttempts) { - Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); - return; - } + + token = (await _authService.acquireToken()).$1; + if (token == null) { + Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); + await _analyticsService.logError('LoginViewmodel', 'Failed to acquire token'); + return false; } - _settingsManager.setBool(PreferencesFlag.isLoggedIn, true); + _settingsManager.isLoggedIn = true; navigationService.pushNamedAndRemoveUntil(RouterPaths.startup); } diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index 2894db0c0..dab22973d 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -72,11 +72,15 @@ class _LoginViewState extends State { alignment: Alignment.centerRight, children: [ ElevatedButton.icon( - onPressed: () { + onPressed: () async { setState(() { _isLoading = true; }); - model.authenticate(); + if (await model.authenticate() as bool == false) { + setState(() { + _isLoading = false; + }); + } }, icon: const FaIcon(FontAwesomeIcons.lockOpen, color: Colors.white), label: Text( diff --git a/lib/ui/startup/view_model/startup_viewmodel.dart b/lib/ui/startup/view_model/startup_viewmodel.dart index 5e016a3e8..07406e327 100644 --- a/lib/ui/startup/view_model/startup_viewmodel.dart +++ b/lib/ui/startup/view_model/startup_viewmodel.dart @@ -50,22 +50,6 @@ class StartUpViewModel extends BaseViewModel { _navigationService.pushNamedAndRemoveUntil(RouterPaths.root); } else { _navigationService.pushNamedAndRemoveUntil(RouterPaths.login); - AuthenticationResult? token; - int attempts = 0; - const maxAttempts = 3; - - while (token == null && attempts < maxAttempts) { - attempts++; - token = (await _authService.acquireToken()).$1; - if (token == null && attempts >= maxAttempts) { - Fluttertoast.showToast(msg: intl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - await _analyticsService.logError('StartupViewmodel', 'Failed to acquire token after $maxAttempts attempts'); - return; - } - } - - _settingsManager.isLoggedIn = true; - _navigationService.pushNamedAndRemoveUntil(RouterPaths.root); } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 38dd0bc6c..000000000 --- a/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); - flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); -} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc..000000000 --- a/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 65240e99c..000000000 --- a/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - flutter_secure_storage_linux - url_launcher_linux -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index f31fa2b8b..000000000 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import connectivity_plus -import firebase_analytics -import firebase_core -import firebase_crashlytics -import firebase_remote_config -import flutter_secure_storage_darwin -import in_app_review -import msal_auth -import package_info_plus -import share_plus -import shared_preferences_foundation -import sqflite_darwin -import url_launcher_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) - FirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FirebaseAnalyticsPlugin")) - FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) - FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) - FirebaseRemoteConfigPlugin.register(with: registry.registrar(forPlugin: "FirebaseRemoteConfigPlugin")) - FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin")) - InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) - MsalAuthPlugin.register(with: registry.registrar(forPlugin: "MsalAuthPlugin")) - FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) -} diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig deleted file mode 100644 index 0cb1b286f..000000000 --- a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig +++ /dev/null @@ -1,11 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=C:\Users\sgoku\flutter -FLUTTER_APPLICATION_PATH=C:\Projects\Notre-Dame -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=4.64.1 -FLUTTER_BUILD_NUMBER=4.64.1 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=true -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh deleted file mode 100644 index aceff951e..000000000 --- a/macos/Flutter/ephemeral/flutter_export_environment.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\Users\sgoku\flutter" -export "FLUTTER_APPLICATION_PATH=C:\Projects\Notre-Dame" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=4.64.1" -export "FLUTTER_BUILD_NUMBER=4.64.1" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/test/ui/login/view_model/login_viewmodel_test.dart b/test/ui/login/view_model/login_viewmodel_test.dart index a632d3aae..710da9afa 100644 --- a/test/ui/login/view_model/login_viewmodel_test.dart +++ b/test/ui/login/view_model/login_viewmodel_test.dart @@ -9,7 +9,6 @@ import 'package:notredame/data/services/analytics_service.dart'; import 'package:notredame/data/services/auth_service.dart'; import 'package:notredame/data/services/navigation_service.dart'; import 'package:notredame/data/services/networking_service.dart'; -import 'package:notredame/domain/constants/preferences_flags.dart'; import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; import 'package:notredame/ui/login/view_model/login_viewmodel.dart'; @@ -51,7 +50,7 @@ void main() { test('silent sign in failed redirect to startup', () async { NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); - SettingsRepositoryMock.stubGetBool(settingsRepositoryMock, PreferencesFlag.languageChoice, toReturn: true); + SettingsRepositoryMock.stubIsLocaleDefined(settingsRepositoryMock, toReturn: true); AuthServiceMock.stubCreatePublicClientApplication(authServiceMock); AuthServiceMock.stubAcquireTokenSilent(authServiceMock, success: false); AuthServiceMock.stubAcquireToken(authServiceMock, success: true); @@ -60,7 +59,7 @@ void main() { verify(authServiceMock.acquireToken()).called(1); verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.startup)); - verify(settingsRepositoryMock.setBool(PreferencesFlag.isLoggedIn, true)).called(1); + verify(settingsRepositoryMock.isLoggedIn = true).called(1); }); }); -} +} \ No newline at end of file diff --git a/test/ui/startup/view_model/startup_viewmodel_test.dart b/test/ui/startup/view_model/startup_viewmodel_test.dart index 44141bd82..380fcc12d 100644 --- a/test/ui/startup/view_model/startup_viewmodel_test.dart +++ b/test/ui/startup/view_model/startup_viewmodel_test.dart @@ -73,9 +73,6 @@ void main() { verify(authServiceMock.acquireTokenSilent()).called(1); verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.login)); - verify(authServiceMock.acquireToken()).called(1); - verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.root)); - verify(settingsRepositoryMock.isLoggedIn = true).called(1); }); test('navigates to chooseLanguage if language not chosen', () async { From 2a84bf37ca7bd5c010cd1bd065440a9465d0fe84 Mon Sep 17 00:00:00 2001 From: Shafaatul-Islam <114493805+Shafaatul-Islam@users.noreply.github.com> Date: Mon, 27 Apr 2026 03:50:37 +0000 Subject: [PATCH 23/28] [BOT] Applying format. --- lib/ui/login/view_model/login_viewmodel.dart | 2 +- test/ui/login/view_model/login_viewmodel_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index c0a078857..05705f04a 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -29,7 +29,7 @@ class LoginViewModel extends BaseViewModel { AuthenticationResult? token; token = (await _authService.acquireToken()).$1; - if (token == null) { + if (token == null) { Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); await _analyticsService.logError('LoginViewmodel', 'Failed to acquire token'); return false; diff --git a/test/ui/login/view_model/login_viewmodel_test.dart b/test/ui/login/view_model/login_viewmodel_test.dart index 710da9afa..481d3db95 100644 --- a/test/ui/login/view_model/login_viewmodel_test.dart +++ b/test/ui/login/view_model/login_viewmodel_test.dart @@ -62,4 +62,4 @@ void main() { verify(settingsRepositoryMock.isLoggedIn = true).called(1); }); }); -} \ No newline at end of file +} From 9e81d159251202f70596cdecfd6f5488a9c6ff1b Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:56:42 -0400 Subject: [PATCH 24/28] removing useless imports in router.dart --- lib/router.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/router.dart b/lib/router.dart index 5dbb9268e..ab18c3dd3 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -11,8 +11,6 @@ import 'package:notredame/ui/ets/events/author/widgets/author_view.dart'; import 'package:notredame/ui/ets/events/news/widgets/news_view.dart'; import 'package:notredame/ui/ets/events/news_details/widgets/news_details_view.dart'; import 'package:notredame/ui/ets/quick_links/security_info/widgets/security_view.dart'; -import 'package:notredame/ui/ets/quick_links/widgets/quick_links_view.dart'; -import 'package:notredame/ui/ets/widgets/ets_view.dart'; import 'package:notredame/ui/login/widgets/login_view.dart'; import 'package:notredame/ui/more/about/widgets/about_view.dart'; import 'package:notredame/ui/more/contributors/widgets/contributors_view.dart'; From 245a309de532891a61311483ffb1174f9dcacad3 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Mon, 27 Apr 2026 00:00:44 -0400 Subject: [PATCH 25/28] indentation in login_viewmodel and login_viewmodel_test --- lib/ui/login/view_model/login_viewmodel.dart | 2 +- test/ui/login/view_model/login_viewmodel_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index c0a078857..05705f04a 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -29,7 +29,7 @@ class LoginViewModel extends BaseViewModel { AuthenticationResult? token; token = (await _authService.acquireToken()).$1; - if (token == null) { + if (token == null) { Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); await _analyticsService.logError('LoginViewmodel', 'Failed to acquire token'); return false; diff --git a/test/ui/login/view_model/login_viewmodel_test.dart b/test/ui/login/view_model/login_viewmodel_test.dart index 710da9afa..481d3db95 100644 --- a/test/ui/login/view_model/login_viewmodel_test.dart +++ b/test/ui/login/view_model/login_viewmodel_test.dart @@ -62,4 +62,4 @@ void main() { verify(settingsRepositoryMock.isLoggedIn = true).called(1); }); }); -} \ No newline at end of file +} From 3f0f79d3333d6035bc4e4221d313e6310f1fc3f2 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:35:19 -0400 Subject: [PATCH 26/28] deletion of redundant constants in router_paths + cleaner loading management in login_view + use of the exception pattern in login_viewmodel --- lib/domain/constants/router_paths.dart | 2 - lib/l10n/intl_en.arb | 1 + lib/l10n/intl_fr.arb | 1 + lib/ui/login/view_model/login_viewmodel.dart | 14 +-- lib/ui/login/widgets/login_view.dart | 11 +-- test/ui/login/widgets/login_view_test.dart | 93 ++++++++++++++++++++ 6 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 test/ui/login/widgets/login_view_test.dart diff --git a/lib/domain/constants/router_paths.dart b/lib/domain/constants/router_paths.dart index 202c56463..0c5b5d72f 100644 --- a/lib/domain/constants/router_paths.dart +++ b/lib/domain/constants/router_paths.dart @@ -3,8 +3,6 @@ class RouterPaths { static const String startup = "/startup"; static const String faq = "/faq"; static const String login = "/login"; - static const String dashboard = "/dashboard"; - static const String schedule = "/schedule"; static const String root = "/root"; static const String defaultSchedule = "/schedule/default"; static const String gradeDetails = "/student/grade/details"; diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ad70be1d6..3ccffa973 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -47,6 +47,7 @@ "close_button_text": "Close", "startup_viewmodel_acquire_token_fail": "Connection failed after multiple attempts. Please try again.", + "login_viewmodel_acquire_token_fail": "Connection failed due to cancellation. Please try again.", "title_schedule": "Schedule", "title_student": "Student", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index c48537d46..9d290fa84 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -46,6 +46,7 @@ "close_button_text": "Fermer", "startup_viewmodel_acquire_token_fail": "Échec de la connexion après plusieurs tentatives. Veuillez réessayer.", + "login_viewmodel_acquire_token_fail": "Échec de la connexion à cause de l'annulation. Veuillez réessayer.", "title_schedule": "Horaire", "title_student": "Étudiant", diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index 05705f04a..7ab943362 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -27,16 +27,18 @@ class LoginViewModel extends BaseViewModel { Future authenticate() async { AuthenticationResult? token; - token = (await _authService.acquireToken()).$1; - if (token == null) { - Fluttertoast.showToast(msg: _appIntl.startup_viewmodel_acquire_token_fail, toastLength: Toast.LENGTH_LONG); - await _analyticsService.logError('LoginViewmodel', 'Failed to acquire token'); - return false; + + try { + if (token == null) { + throw const MsalUserCancelException(message: "", correlationId: null); + } + } catch (e) { + Fluttertoast.showToast(msg: _appIntl.login_viewmodel_acquire_token_fail); + await _analyticsService.logError('LoginViewmodel', 'Failed to acquire token due to cancellation'); } _settingsManager.isLoggedIn = true; - navigationService.pushNamedAndRemoveUntil(RouterPaths.startup); } } diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index dab22973d..d58dfde54 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -73,14 +73,9 @@ class _LoginViewState extends State { children: [ ElevatedButton.icon( onPressed: () async { - setState(() { - _isLoading = true; - }); - if (await model.authenticate() as bool == false) { - setState(() { - _isLoading = false; - }); - } + setState(() => _isLoading = true); + await model.authenticate(); + setState(() => _isLoading = false); }, icon: const FaIcon(FontAwesomeIcons.lockOpen, color: Colors.white), label: Text( diff --git a/test/ui/login/widgets/login_view_test.dart b/test/ui/login/widgets/login_view_test.dart new file mode 100644 index 000000000..9728cf14d --- /dev/null +++ b/test/ui/login/widgets/login_view_test.dart @@ -0,0 +1,93 @@ +// Flutter imports: +import 'package:flutter/material.dart'; + +// Package imports: +import 'package:flutter_test/flutter_test.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:mockito/mockito.dart'; + +// Project imports: +import 'package:notredame/data/repositories/settings_repository.dart'; +import 'package:notredame/data/repositories/user_repository.dart'; +import 'package:notredame/data/services/analytics_service.dart'; +import 'package:notredame/data/services/auth_service.dart'; +import 'package:notredame/data/services/navigation_service.dart'; +import 'package:notredame/data/services/networking_service.dart'; +import 'package:notredame/domain/constants/router_paths.dart'; +import 'package:notredame/l10n/app_localizations.dart'; +import 'package:notredame/ui/login/widgets/login_view.dart'; +import '../../../data/mocks/repositories/settings_repository_mock.dart'; +import '../../../data/mocks/services/auth_service_mock.dart'; +import '../../../data/mocks/services/navigation_service_mock.dart'; +import '../../../data/mocks/services/networking_service_mock.dart'; +import '../../../helpers.dart'; + +void main() { + late NavigationServiceMock navigationServiceMock; + late SettingsRepositoryMock settingsRepositoryMock; + late NetworkingServiceMock networkingServiceMock; + late AuthServiceMock authServiceMock; + + late AppIntl appIntl; + + group('LoginView', () { + setUp(() async { + setupAnalyticsServiceMock(); + navigationServiceMock = setupNavigationServiceMock(); + settingsRepositoryMock = setupSettingsRepositoryMock(); + networkingServiceMock = setupNetworkingServiceMock(); + authServiceMock = setupAuthServiceMock(); + + appIntl = await setupAppIntl(); + }); + + tearDown(() { + unregister(); + unregister(); + unregister(); + unregister(); + unregister(); + unregister(); + }); + + group('UI', () { + testWidgets('has 2 buttons with an icon, title and school logo', (WidgetTester tester) async { + await tester.pumpWidget(localizedWidget(child: const LoginView())); + await tester.pumpAndSettle(); + + final heroFinder = find.byType(Hero); + expect(heroFinder, findsOneWidget); + final Hero heroWidget = tester.widget(heroFinder); + expect(heroWidget.tag, 'ets_logo'); + expect(find.text(appIntl.login_startup_title), findsOneWidget); + expect(find.byIcon(FontAwesomeIcons.lockOpen), findsOneWidget); + expect(find.text(appIntl.login_action_sign_in), findsOneWidget); + expect(find.byIcon(FontAwesomeIcons.question), findsOneWidget); + expect(find.text("FAQ"), findsOneWidget); + }); + + testWidgets('sign in button triggers authentication', (WidgetTester tester) async { + NetworkingServiceMock.stubHasConnectivity(networkingServiceMock); + SettingsRepositoryMock.stubIsLocaleDefined(settingsRepositoryMock, toReturn: true); + + await tester.pumpWidget(localizedWidget(child: const LoginView())); + await tester.pumpAndSettle(); + + final signInButton = find.byIcon(FontAwesomeIcons.lockOpen); + await tester.tap(signInButton); + await tester.pump(); + verify(authServiceMock.acquireToken()).called(1); + }); + + testWidgets('FAQ button navigates to the FAQ page', (WidgetTester tester) async { + await tester.pumpWidget(localizedWidget(child: const LoginView())); + await tester.pumpAndSettle(); + + final faqButton = find.text("FAQ"); + await tester.tap(faqButton); + await tester.pumpAndSettle(); + verify(navigationServiceMock.pushNamed(RouterPaths.faq)).called(1); + }); + }); + }); +} From 8fda8c2cb13a025b041ff64bf5979e4d4a5a1eb5 Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Tue, 5 May 2026 16:10:40 -0400 Subject: [PATCH 27/28] correct exception pattern that does not catch locally in login_viewmodel --- lib/ui/login/view_model/login_viewmodel.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/ui/login/view_model/login_viewmodel.dart b/lib/ui/login/view_model/login_viewmodel.dart index 7ab943362..4e6becd21 100644 --- a/lib/ui/login/view_model/login_viewmodel.dart +++ b/lib/ui/login/view_model/login_viewmodel.dart @@ -29,13 +29,10 @@ class LoginViewModel extends BaseViewModel { AuthenticationResult? token; token = (await _authService.acquireToken()).$1; - try { - if (token == null) { - throw const MsalUserCancelException(message: "", correlationId: null); - } - } catch (e) { + if (token == null) { Fluttertoast.showToast(msg: _appIntl.login_viewmodel_acquire_token_fail); await _analyticsService.logError('LoginViewmodel', 'Failed to acquire token due to cancellation'); + return; } _settingsManager.isLoggedIn = true; From c8361fd61b75e2383d95e4417e0ae62c35cbaeea Mon Sep 17 00:00:00 2001 From: DogithuboM <114493805+DogithuboM@users.noreply.github.com> Date: Tue, 12 May 2026 22:39:28 -0400 Subject: [PATCH 28/28] change in login to better suit dark mode + name change of variable loginAccent to spashScreenLogo --- .../choose_language/widgets/choose_language_view.dart | 2 +- lib/ui/core/themes/app_colors_extension.dart | 10 +++++----- lib/ui/core/themes/app_theme.dart | 4 ++-- lib/ui/login/widgets/login_view.dart | 8 +++----- lib/ui/startup/widgets/startup_view.dart | 2 +- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/ui/choose_language/widgets/choose_language_view.dart b/lib/ui/choose_language/widgets/choose_language_view.dart index 7868d6ec2..63a36e81a 100644 --- a/lib/ui/choose_language/widgets/choose_language_view.dart +++ b/lib/ui/choose_language/widgets/choose_language_view.dart @@ -53,7 +53,7 @@ class _ChooseLanguageViewState extends State { child: ListView( shrinkWrap: true, children: [ - Icon(Icons.language, size: 80, color: context.theme.appColors.loginAccent), + Icon(Icons.language, size: 80, color: context.theme.appColors.splashScreenLogo), Padding( padding: const EdgeInsets.only(left: 20, top: 60), child: Align( diff --git a/lib/ui/core/themes/app_colors_extension.dart b/lib/ui/core/themes/app_colors_extension.dart index 20e922eaa..8e7190f51 100644 --- a/lib/ui/core/themes/app_colors_extension.dart +++ b/lib/ui/core/themes/app_colors_extension.dart @@ -30,7 +30,7 @@ class AppColorsExtension extends ThemeExtension { required this.dayIndicatorWeekView, required this.dayIndicatorDayView, required this.loginMain, - required this.loginAccent, + required this.splashScreenLogo, required this.inputError, required this.mapLegend, }); @@ -62,7 +62,7 @@ class AppColorsExtension extends ThemeExtension { final Color dayIndicatorWeekView; final Color dayIndicatorDayView; final Color loginMain; - final Color loginAccent; + final Color splashScreenLogo; final Color inputError; final Color mapLegend; @@ -95,7 +95,7 @@ class AppColorsExtension extends ThemeExtension { Color? dayIndicatorWeekView, Color? dayIndicatorDayView, Color? loginMain, - Color? loginAccent, + Color? splashScreenLogo, Color? inputError, Color? mapLegend, }) { @@ -127,7 +127,7 @@ class AppColorsExtension extends ThemeExtension { dayIndicatorWeekView: dayIndicatorWeekView ?? this.dayIndicatorWeekView, dayIndicatorDayView: dayIndicatorDayView ?? this.dayIndicatorDayView, loginMain: loginMain ?? this.loginMain, - loginAccent: loginAccent ?? this.loginAccent, + splashScreenLogo: splashScreenLogo ?? this.splashScreenLogo, inputError: inputError ?? this.inputError, mapLegend: mapLegend ?? this.mapLegend, ); @@ -169,7 +169,7 @@ class AppColorsExtension extends ThemeExtension { dayIndicatorWeekView: _lerp(dayIndicatorWeekView, other.dayIndicatorWeekView, t), dayIndicatorDayView: _lerp(dayIndicatorDayView, other.dayIndicatorDayView, t), loginMain: _lerp(loginMain, other.loginMain, t), - loginAccent: _lerp(loginAccent, other.loginAccent, t), + splashScreenLogo: _lerp(splashScreenLogo, other.splashScreenLogo, t), inputError: _lerp(inputError, other.inputError, t), mapLegend: _lerp(mapLegend, other.mapLegend, t), ); diff --git a/lib/ui/core/themes/app_theme.dart b/lib/ui/core/themes/app_theme.dart index beaaae055..d0c1c0e1b 100644 --- a/lib/ui/core/themes/app_theme.dart +++ b/lib/ui/core/themes/app_theme.dart @@ -114,7 +114,7 @@ class AppTheme with ChangeNotifier { dayIndicatorWeekView: const Color(0x83ef3e45), dayIndicatorDayView: AppPalette.grey.lightGrey, loginMain: AppPalette.etsLightRed, - loginAccent: AppPalette.grey.white, + splashScreenLogo: AppPalette.grey.white, inputError: Colors.amberAccent, mapLegend: const Color(0xfffffdfd), ); @@ -208,7 +208,7 @@ class AppTheme with ChangeNotifier { dayIndicatorWeekView: const Color(0x96ef3e45), dayIndicatorDayView: AppPalette.grey.darkGrey, loginMain: AppPalette.grey.white, - loginAccent: AppPalette.etsLightRed, + splashScreenLogo: AppPalette.etsLightRed, inputError: Colors.redAccent, mapLegend: const Color(0xff121212), ); diff --git a/lib/ui/login/widgets/login_view.dart b/lib/ui/login/widgets/login_view.dart index d58dfde54..5519914a3 100644 --- a/lib/ui/login/widgets/login_view.dart +++ b/lib/ui/login/widgets/login_view.dart @@ -10,7 +10,6 @@ import 'package:stacked/stacked.dart'; import 'package:notredame/domain/constants/router_paths.dart'; import 'package:notredame/l10n/app_localizations.dart'; import 'package:notredame/ui/core/themes/app_palette.dart'; -import 'package:notredame/ui/core/themes/app_theme.dart'; import 'package:notredame/ui/login/view_model/login_viewmodel.dart'; class LoginView extends StatefulWidget { @@ -28,15 +27,14 @@ class _LoginViewState extends State { return ViewModelBuilder.reactive( viewModelBuilder: () => LoginViewModel(intl: AppIntl.of(context)!), builder: (context, model, child) => Scaffold( - backgroundColor: context.theme.appColors.backgroundVibrant, body: Container( width: double.infinity, height: double.infinity, - decoration: BoxDecoration( + decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [context.theme.appColors.backgroundVibrant, AppPalette.etsDarkRed], + colors: [AppPalette.etsLightRed, AppPalette.etsDarkRed], ), ), child: Center( @@ -55,7 +53,7 @@ class _LoginViewState extends State { excludeFromSemantics: true, width: 90, height: 90, - colorFilter: ColorFilter.mode(context.theme.appColors.loginAccent, BlendMode.srcIn), + colorFilter: ColorFilter.mode(AppPalette.grey.white, BlendMode.srcIn), ), ), ), diff --git a/lib/ui/startup/widgets/startup_view.dart b/lib/ui/startup/widgets/startup_view.dart index bd18cd065..7ef327306 100644 --- a/lib/ui/startup/widgets/startup_view.dart +++ b/lib/ui/startup/widgets/startup_view.dart @@ -41,7 +41,7 @@ class StartUpView extends StatelessWidget { excludeFromSemantics: true, width: 90, height: 90, - colorFilter: ColorFilter.mode(context.theme.appColors.loginAccent, BlendMode.srcIn), + colorFilter: ColorFilter.mode(context.theme.appColors.splashScreenLogo, BlendMode.srcIn), ), ), const SizedBox(height: 15),