From c98a593c4035eeef9b5b7958b66ddc738413b834 Mon Sep 17 00:00:00 2001 From: mcquenji Date: Fri, 14 Mar 2025 09:13:03 +0100 Subject: [PATCH 1/3] fix: resolve not running on non web Fixed some race conditions resulting in infinite redirects. Idk why this was working on web but not on desktop tho... --- lib/src/app/app.dart | 9 ++- .../auth/presentation/guards/auth_guard.dart | 12 +++- .../presentation/guards/capability_guard.dart | 7 +- .../repositories/auth_repository.dart | 14 +++- .../repositories/user_repository.dart | 6 +- .../presentation/screens/login_screen.dart | 20 +++--- .../auth/presentation/widgets/login_form.dart | 15 ++-- .../calendar_plan_repository.dart | 18 ++--- .../repositories/invites_repository.dart | 4 +- .../guards/has_courses_guard.dart | 4 +- .../moodle_courses_repository.dart | 2 + .../slot_master_slots_repository.dart | 4 +- .../repositories/slots_repository.dart | 4 +- pubspec.lock | 72 +++++++++---------- pubspec.yaml | 2 + 15 files changed, 109 insertions(+), 84 deletions(-) diff --git a/lib/src/app/app.dart b/lib/src/app/app.dart index 8a530125..b53e7ba4 100644 --- a/lib/src/app/app.dart +++ b/lib/src/app/app.dart @@ -66,6 +66,9 @@ class AppModule extends Module { customTransition: defaultTransition, guards: [ CapabilityGuard({UserCapability.student}, redirectTo: '/slots/'), + // apparently having a guard in a child route will override the parent's guard so we need it here too + // Important to have this guard last as the order of guards is reversed + AuthGuard(redirectTo: '/auth/'), ], ), ModuleRoute( @@ -74,8 +77,9 @@ class AppModule extends Module { transition: TransitionType.custom, customTransition: defaultTransition, guards: [ - CapabilityGuard({UserCapability.student}, redirectTo: '/slots/'), FeatureGuard([kCalendarPlanFeatureID], redirectTo: '/settings/'), + CapabilityGuard({UserCapability.student}, redirectTo: '/slots/'), + AuthGuard(redirectTo: '/auth/'), ], ), ModuleRoute( @@ -97,14 +101,15 @@ class AppModule extends Module { customTransition: defaultTransition, guards: [ CapabilityGuard({UserCapability.student}, redirectTo: '/slots/'), + AuthGuard(redirectTo: '/auth/'), ], ), ], customTransition: defaultTransition, transition: TransitionType.custom, guards: [ - AuthGuard(redirectTo: '/auth/'), HasCoursesGuard(), + AuthGuard(redirectTo: '/auth/'), ], ) ..module( diff --git a/lib/src/auth/presentation/guards/auth_guard.dart b/lib/src/auth/presentation/guards/auth_guard.dart index 936ca48a..ae191c69 100644 --- a/lib/src/auth/presentation/guards/auth_guard.dart +++ b/lib/src/auth/presentation/guards/auth_guard.dart @@ -1,10 +1,11 @@ import 'package:eduplanner/src/auth/auth.dart'; import 'package:flutter_modular/flutter_modular.dart'; +import 'package:mcquenji_core/mcquenji_core.dart'; /// Guard that checks if the user is authenticated. /// /// You must import [AuthModule] in the module using this guard. -class AuthGuard extends RouteGuard { +class AuthGuard extends RouteGuard with MiddlewareLogger { /// Guard that checks if the user is authenticated. /// /// You must import [AuthModule] in the module using this guard. @@ -14,10 +15,15 @@ class AuthGuard extends RouteGuard { Future canActivate(String path, ModularRoute route) async { final auth = Modular.tryGet(); - if (auth == null) return false; + if (auth == null) { + log('AuthRepository not found'); + return false; + } await auth.ready; - return Modular.get().isAuthenticated; + log('Authenticated: ${auth.isAuthenticated}'); + + return auth.isAuthenticated; } } diff --git a/lib/src/auth/presentation/guards/capability_guard.dart b/lib/src/auth/presentation/guards/capability_guard.dart index f55785eb..9360ff3b 100644 --- a/lib/src/auth/presentation/guards/capability_guard.dart +++ b/lib/src/auth/presentation/guards/capability_guard.dart @@ -1,8 +1,11 @@ +import 'dart:async'; + import 'package:eduplanner/src/auth/auth.dart'; import 'package:flutter_modular/flutter_modular.dart'; +import 'package:mcquenji_core/mcquenji_core.dart'; /// Guard that checks if the user has the required capabilities. -class CapabilityGuard extends RouteGuard { +class CapabilityGuard extends RouteGuard with MiddlewareLogger { /// The required capabilities. final Set capabilities; @@ -19,6 +22,8 @@ class CapabilityGuard extends RouteGuard { await user.ready; + log('User capabilities: ${user.state.data?.capabilities} ; Required: $capabilities'); + if (!user.state.hasData) { return false; } diff --git a/lib/src/auth/presentation/repositories/auth_repository.dart b/lib/src/auth/presentation/repositories/auth_repository.dart index 188c1d8e..f85c0e3c 100644 --- a/lib/src/auth/presentation/repositories/auth_repository.dart +++ b/lib/src/auth/presentation/repositories/auth_repository.dart @@ -70,7 +70,7 @@ class AuthRepository extends Repository>> with Tracable { } /// Sign in with [username] and [password]. - Future authenticate({ + Future authenticate({ required String username, required String password, }) async { @@ -94,14 +94,17 @@ class AuthRepository extends Repository>> with Tracable { }, ); - if (!state.hasData) return; + if (!state.hasData) return false; log('Authentication successful'); await _localStorage.write(state.requireData); + + return true; } catch (e) { transaction.internalError(e); - rethrow; + + return false; } finally { await transaction.commit(); } @@ -128,6 +131,11 @@ class AuthRepository extends Repository>> with Tracable { /// `true` if the user is authenticated. bool get isAuthenticated => state.hasData && state.requireData.isNotEmpty; + /// Throws a [WaitForDataException] if the user is not authenticated. + void requireAuth() { + if (!isAuthenticated) throw WaitForDataException(AuthRepository); + } + @override void emit(AsyncValue> state) { super.emit(state); diff --git a/lib/src/auth/presentation/repositories/user_repository.dart b/lib/src/auth/presentation/repositories/user_repository.dart index 50ad20d0..ab107927 100644 --- a/lib/src/auth/presentation/repositories/user_repository.dart +++ b/lib/src/auth/presentation/repositories/user_repository.dart @@ -28,13 +28,11 @@ class UserRepository extends Repository> with Tracable { FutureOr build(BuildTrigger trigger) async { final transaction = startTransaction('loadUsers'); - await _auth.ready; - final tokens = waitForData(_auth); _isHandlingAuthChange = true; - if (tokens.isEmpty) { + if (!_auth.isAuthenticated) { log('User is unauthenticated'); error( @@ -67,7 +65,7 @@ class UserRepository extends Repository> with Tracable { distinctId: hash, properties: { 'capabilities': user.capabilities.map((c) => c.name).toList(), - 'vintage': user.vintage, + 'vintage': user.vintage?.humanReadable, 'theme': user.themeName, 'optional_tasks_enabled': user.optionalTasksEnabled, 'display_task_count': user.displayTaskCount, diff --git a/lib/src/auth/presentation/screens/login_screen.dart b/lib/src/auth/presentation/screens/login_screen.dart index 602c9e93..da48ebef 100644 --- a/lib/src/auth/presentation/screens/login_screen.dart +++ b/lib/src/auth/presentation/screens/login_screen.dart @@ -10,16 +10,8 @@ class LoginScreen extends StatelessWidget with AdaptiveWidget { /// Presents an authentication form to the user. const LoginScreen({super.key}); - @override - Widget build(BuildContext context) { - final user = context.watch(); - final auth = context.watch(); - - if (auth.isAuthenticated && user.state.hasData) { - Modular.to.navigate('/dashboard/'); - } - - return super.build(context); + static void _onLogin() { + Modular.to.navigate('/dashboard'); } @override @@ -42,7 +34,9 @@ class LoginScreen extends StatelessWidget with AdaptiveWidget { padding: EdgeInsets.only(right: 150), child: SizedBox( width: 350, - child: LoginForm(), + child: LoginForm( + onLogin: _onLogin, + ), ), ), ), @@ -61,7 +55,9 @@ class LoginScreen extends StatelessWidget with AdaptiveWidget { spacing: Spacing.mediumSpacing, children: [ const Spacer(), - const LoginForm(), + const LoginForm( + onLogin: _onLogin, + ), const Spacer(), Text( context.t.auth_version(kInstalledRelease.toString()), diff --git a/lib/src/auth/presentation/widgets/login_form.dart b/lib/src/auth/presentation/widgets/login_form.dart index c564fb76..6868c375 100644 --- a/lib/src/auth/presentation/widgets/login_form.dart +++ b/lib/src/auth/presentation/widgets/login_form.dart @@ -13,7 +13,10 @@ import 'package:url_launcher/url_launcher.dart'; /// A form prompting the user to input their credentials. class LoginForm extends StatefulWidget { /// A form prompting the user to input their credentials. - const LoginForm({super.key}); + const LoginForm({super.key, this.onLogin}); + + /// Callback to be called when the user logs in successfully. + final void Function()? onLogin; @override State createState() => _LoginFormState(); @@ -79,17 +82,17 @@ class _LoginFormState extends State with WidgetsBindingObserver { final auth = Modular.get(); - await auth.authenticate(username: username, password: password); + final success = await auth.authenticate(username: username, password: password); setState(() { loggingIn = false; }); - if (auth.state.hasError) { - return; + if (success) { + widget.onLogin?.call(); + } else { + usernameFocusNode.requestFocus(); } - - usernameFocusNode.requestFocus(); } void togglePasswordVisibility() { diff --git a/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart b/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart index ba1acadd..1ae67f73 100644 --- a/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart +++ b/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart @@ -96,7 +96,7 @@ class CalendarPlanRepository extends Repository> with T await captureEvent('plan_cleared'); - await build(this); + await refresh(this); } catch (e, st) { transaction.internalError(e); log('Failed to clear plan.', e, st); @@ -140,8 +140,8 @@ class CalendarPlanRepository extends Repository> with T await captureEvent('deadline_set', properties: {'id': taskId, 'start': start, 'end': end}); - await _tasks.build(this); - await build(this); + await _tasks.refresh(this); + await refresh(this); } catch (e, st) { transaction.internalError(e); @@ -177,8 +177,8 @@ class CalendarPlanRepository extends Repository> with T await captureEvent('deadline_removed', properties: {'id': id}); - await _tasks.build(this); - await build(this); + await _tasks.refresh(this); + await refresh(this); } catch (e, st) { transaction.internalError(e); @@ -205,7 +205,7 @@ class CalendarPlanRepository extends Repository> with T await captureEvent('plan_left'); - await build(this); + await refresh(this); } catch (e, st) { log('Failed to leave plan.', e, st); @@ -233,7 +233,7 @@ class CalendarPlanRepository extends Repository> with T await captureEvent('member_kicked'); - await build(this); + await refresh(this); } catch (e, st) { log('Failed to remove member.', e, st); @@ -262,7 +262,7 @@ class CalendarPlanRepository extends Repository> with T await captureEvent('member_access_changed', properties: {'access_type': accessType}); - await build(this); + await refresh(this); } catch (e, st) { log('Failed to modify member.', e, st); @@ -288,7 +288,7 @@ class CalendarPlanRepository extends Repository> with T state.requireData.copyWith(name: name), ); - await build(this); + await refresh(this); } catch (e, st) { log('Failed to change name.', e, st); diff --git a/lib/src/calendar/presentation/repositories/invites_repository.dart b/lib/src/calendar/presentation/repositories/invites_repository.dart index c9279e27..46de81dc 100644 --- a/lib/src/calendar/presentation/repositories/invites_repository.dart +++ b/lib/src/calendar/presentation/repositories/invites_repository.dart @@ -57,7 +57,7 @@ class InvitesRepository extends Repository>> with Tr await captureEvent('user_invited'); - await build(this); + await refresh(this); } catch (e, st) { log('Failed to invite user.', e, st); transaction.internalError(e); @@ -131,7 +131,7 @@ class InvitesRepository extends Repository>> with Tr await captureEvent('invite_accepted'); - await _plan.build(this); + await _plan.refresh(this); } catch (e, st) { log('Failed to accept invite.', e, st); transaction.internalError(e); diff --git a/lib/src/moodle/presentation/guards/has_courses_guard.dart b/lib/src/moodle/presentation/guards/has_courses_guard.dart index b22401f2..fc5b188b 100644 --- a/lib/src/moodle/presentation/guards/has_courses_guard.dart +++ b/lib/src/moodle/presentation/guards/has_courses_guard.dart @@ -4,7 +4,7 @@ import 'package:mcquenji_core/mcquenji_core.dart'; /// Guard that checks if the user has any courses enabled. /// Redirects to the course selection screen per default if no courses are enabled. -class HasCoursesGuard extends RouteGuard implements BuildTrigger { +class HasCoursesGuard extends RouteGuard with MiddlewareLogger implements Trigger { /// Guard that checks if the user has any courses enabled. /// Redirects to the course selection screen per default if no courses are enabled. HasCoursesGuard({super.redirectTo = '/moodle/select-courses/'}); @@ -23,7 +23,7 @@ class HasCoursesGuard extends RouteGuard implements BuildTrigger { if (!user.state.requireData.capabilities.hasStudent) return true; - await courses.build(this); + await courses.refresh(this); return courses.filter(enabled: true).isNotEmpty; } diff --git a/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart b/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart index 587162db..44d119f3 100644 --- a/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart +++ b/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart @@ -19,6 +19,8 @@ class MoodleCoursesRepository extends Repository>> @override Future build(BuildTrigger trigger) async { + _auth.requireAuth(); + final transaction = startTransaction('loadCourses'); final tokens = waitForData(_auth); diff --git a/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart b/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart index fe70756b..31d31270 100644 --- a/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart +++ b/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart @@ -79,7 +79,7 @@ class SlotMasterSlotsRepository extends Repository>> with ); } - await build(this); + await refresh(this); } catch (e, s) { log('Failed to create slot', e, s); transaction.internalError(e); @@ -188,7 +188,7 @@ class SlotMasterSlotsRepository extends Repository>> with ); } - await build(this); + await refresh(this); } catch (e, s) { log('Failed to update slot', e, s); transaction.internalError(e); diff --git a/lib/src/slots/presentation/repositories/slots_repository.dart b/lib/src/slots/presentation/repositories/slots_repository.dart index a444ed9b..e47d4866 100644 --- a/lib/src/slots/presentation/repositories/slots_repository.dart +++ b/lib/src/slots/presentation/repositories/slots_repository.dart @@ -96,7 +96,7 @@ class SlotsRepository extends Repository>> with Tracable { log('Reserved slot ${slot.id} for current user'); - await build(this); + await refresh(this); } catch (e) { log('Failed to reserve slot: $e'); transaction.internalError(e); @@ -163,7 +163,7 @@ class SlotsRepository extends Repository>> with Tracable { log('Unreserved slot ${slot.id} for current user'); - await build(this); + await refresh(this); } catch (e) { log('Failed to unreserve slot: $e'); transaction.internalError(e); diff --git a/pubspec.lock b/pubspec.lock index ed2c00e6..5058b8fe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: args - sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.7.0" async: dependency: transitive description: @@ -74,18 +74,18 @@ packages: dependency: transitive description: name: auto_injector - sha256: d2e204bc46d7349795364884d07ba79fe6a0f3a84a651b70dcbb68d82dcebab0 + sha256: ad7a95d7c381363d48b54e00cb680f024fd97009067244454e9b4850337608e8 url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.0" awesome_extensions: dependency: "direct main" description: name: awesome_extensions - sha256: "91dc128e8cf01fbd3d3567b8f1dd1e3183cbf9fd6b1850e8b0fafce9a7eee0da" + sha256: "9b1693e986e4045141add298fa2d7f9aa6cdd3c125b951e2cde739a5058ed879" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.0.21" bloc: dependency: "direct main" description: @@ -170,10 +170,10 @@ packages: dependency: transitive description: name: built_value - sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" + sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 url: "https://pub.dev" source: hosted - version: "8.9.3" + version: "8.9.5" carousel_slider: dependency: "direct main" description: @@ -346,10 +346,10 @@ packages: dependency: transitive description: name: dio_web_adapter - sha256: e485c7a39ff2b384fa1d7e09b4e25f755804de8384358049124830b04fc4f93a + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" dotenv: dependency: transitive description: @@ -821,10 +821,10 @@ packages: description: path: "." ref: HEAD - resolved-ref: "71b5ce4df64f1831b9486fddc174f6ec21bbeb33" + resolved-ref: af42b1e41e7746c5800b99dbff218841c665f84b url: "https://github.com/mcquenji/mcquenji_core.git" source: git - version: "3.0.0" + version: "4.0.0" mcquenji_local_storage: dependency: "direct main" description: @@ -904,26 +904,26 @@ packages: dependency: transitive description: name: package_config - sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.0" package_info_plus: dependency: transitive description: name: package_info_plus - sha256: "67eae327b1b0faf761964a1d2e5d323c797f3799db0e85aa232db8d9e922bc35" + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" url: "https://pub.dev" source: hosted - version: "8.2.1" + version: "8.3.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76" + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.2.0" path: dependency: "direct main" description: @@ -952,10 +952,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12" url: "https://pub.dev" source: hosted - version: "2.2.15" + version: "2.2.16" path_provider_foundation: dependency: transitive description: @@ -1074,10 +1074,10 @@ packages: dependency: transitive description: name: pub_semver - sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" pubspec_parse: dependency: transitive description: @@ -1146,34 +1146,34 @@ packages: dependency: transitive description: name: sentry - sha256: "41a3c8e61d5194f5e12adf469fc430d7fa76a373fde6b11280f7df05c24e19e2" + sha256: "3a64dd001bff768ce5ab6fc3608deef4dde22acd4b5d947763557b20db9e2a32" url: "https://pub.dev" source: hosted - version: "8.13.0" + version: "8.14.0" sentry_dart_plugin: dependency: "direct dev" description: name: sentry_dart_plugin - sha256: fb893ff20f51954e2cefd6b280c27c75d689bc65fc740b5bf9b7627453d46b7f + sha256: "84436958fa9231e2e716be117a3b31695e54458b9f27039f76d14515e24248a6" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" sentry_dio: dependency: "direct main" description: name: sentry_dio - sha256: "6e0b43cdd1cae4db1eac1c56181544a80df7215ffd7b6ca347042cc423b09d94" + sha256: f810a71b36e0e0a3405baf7f3eeaa8481ca55bb65a5822f0befc5bdda0049264 url: "https://pub.dev" source: hosted - version: "8.13.0" + version: "8.14.0" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: "86ba98250bfc55cf3a9908afa46a851a46706e8338413d062daa5705ce016d05" + sha256: "3d361f2d5f805783e2e4ed1bd475ef126b36cf525b359dc3627a765a3fb7424d" url: "https://pub.dev" source: hosted - version: "8.13.0" + version: "8.14.0" shared_preferences: dependency: transitive description: @@ -1471,10 +1471,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" + sha256: "1d0eae19bd7606ef60fe69ef3b312a437a16549476c42321d5dc1506c9ca3bf4" url: "https://pub.dev" source: hosted - version: "6.3.14" + version: "6.3.15" url_launcher_ios: dependency: transitive description: @@ -1591,10 +1591,10 @@ packages: dependency: transitive description: name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web_socket: dependency: transitive description: @@ -1623,10 +1623,10 @@ packages: dependency: transitive description: name: win32 - sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e + sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f url: "https://pub.dev" source: hosted - version: "5.10.1" + version: "5.12.0" window_manager: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9aaf7168..936399b1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -88,6 +88,8 @@ dev_dependencies: test: ^1.25.2 dependency_overrides: + # mcquenji_core: + # path: ../mcquenji_core figma_squircle: git: url: https://github.com/mark8044/figma_squircle.git From 34985271458b10b00c267fc5d890152d70816502 Mon Sep 17 00:00:00 2001 From: mcquenji Date: Fri, 14 Mar 2025 09:18:45 +0100 Subject: [PATCH 2/3] style: fix lint errors --- lib/src/app/utils/sentry_utils.dart | 2 +- lib/src/auth/presentation/repositories/auth_repository.dart | 2 +- .../auth/presentation/repositories/echidna_user_repository.dart | 2 +- lib/src/auth/presentation/repositories/user_repository.dart | 2 +- .../presentation/repositories/calendar_plan_repository.dart | 2 +- .../calendar/presentation/repositories/invites_repository.dart | 2 +- .../presentation/repositories/moodle_courses_repository.dart | 2 +- .../presentation/repositories/moodle_tasks_repository.dart | 2 +- lib/src/moodle/presentation/repositories/users_repository.dart | 2 +- .../presentation/repositories/notifications_repository.dart | 2 +- .../repositories/slot_master_courses_repository.dart | 2 +- .../presentation/repositories/slot_master_slots_repository.dart | 2 +- lib/src/slots/presentation/repositories/slots_repository.dart | 2 +- .../presentation/repositories/supervisor_slots_repository.dart | 2 +- .../presentation/repositories/course_stats_repository.dart | 2 +- .../presentation/repositories/global_stats_repository.dart | 2 +- lib/src/theming/presentation/repositories/theme_repository.dart | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/src/app/utils/sentry_utils.dart b/lib/src/app/utils/sentry_utils.dart index 3f1e0a96..e9ed246f 100644 --- a/lib/src/app/utils/sentry_utils.dart +++ b/lib/src/app/utils/sentry_utils.dart @@ -21,7 +21,7 @@ extension TransactionX on ISentrySpan { } /// A mixin that adds transaction tracing to a class. -mixin Tracable on ILoggable { +mixin Tracable on Loggable { ISentrySpan? _transaction; /// The parent tracable to attach transactions to. diff --git a/lib/src/auth/presentation/repositories/auth_repository.dart b/lib/src/auth/presentation/repositories/auth_repository.dart index f85c0e3c..3db4898b 100644 --- a/lib/src/auth/presentation/repositories/auth_repository.dart +++ b/lib/src/auth/presentation/repositories/auth_repository.dart @@ -21,7 +21,7 @@ class AuthRepository extends Repository>> with Tracable { } @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { if (trigger is! InitialBuildTrigger) return; if (_state != null && isAuthenticated) { diff --git a/lib/src/auth/presentation/repositories/echidna_user_repository.dart b/lib/src/auth/presentation/repositories/echidna_user_repository.dart index c63b1683..e82c9005 100644 --- a/lib/src/auth/presentation/repositories/echidna_user_repository.dart +++ b/lib/src/auth/presentation/repositories/echidna_user_repository.dart @@ -15,7 +15,7 @@ class EchidnaUserRepository extends UserIdRepository { } @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { if (trigger is! UserRepository) return; final user = waitForData(_user); diff --git a/lib/src/auth/presentation/repositories/user_repository.dart b/lib/src/auth/presentation/repositories/user_repository.dart index ab107927..04ffc3ec 100644 --- a/lib/src/auth/presentation/repositories/user_repository.dart +++ b/lib/src/auth/presentation/repositories/user_repository.dart @@ -25,7 +25,7 @@ class UserRepository extends Repository> with Tracable { } @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { final transaction = startTransaction('loadUsers'); final tokens = waitForData(_auth); diff --git a/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart b/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart index 1ae67f73..28de5cf8 100644 --- a/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart +++ b/lib/src/calendar/presentation/repositories/calendar_plan_repository.dart @@ -39,7 +39,7 @@ class CalendarPlanRepository extends Repository> with T bool get refreshOptimization => true; @override - Future build(BuildTrigger trigger) async { + Future build(Trigger trigger) async { // We don't need to refresh the plan as it's only loosely connected to the tasks. if (trigger is MoodleTasksRepository) { log('Skipping refresh triggered by $trigger'); diff --git a/lib/src/calendar/presentation/repositories/invites_repository.dart b/lib/src/calendar/presentation/repositories/invites_repository.dart index 46de81dc..0ffebbbe 100644 --- a/lib/src/calendar/presentation/repositories/invites_repository.dart +++ b/lib/src/calendar/presentation/repositories/invites_repository.dart @@ -20,7 +20,7 @@ class InvitesRepository extends Repository>> with Tr Duration get updateInterval => kRefreshIntervalDuration; @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { final transaction = startTransaction('loadInvites'); final token = waitForData(_auth); diff --git a/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart b/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart index 44d119f3..98aaf0d8 100644 --- a/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart +++ b/lib/src/moodle/presentation/repositories/moodle_courses_repository.dart @@ -18,7 +18,7 @@ class MoodleCoursesRepository extends Repository>> Duration get updateInterval => kRefreshIntervalDuration; @override - Future build(BuildTrigger trigger) async { + Future build(Trigger trigger) async { _auth.requireAuth(); final transaction = startTransaction('loadCourses'); diff --git a/lib/src/moodle/presentation/repositories/moodle_tasks_repository.dart b/lib/src/moodle/presentation/repositories/moodle_tasks_repository.dart index ca76886c..90ad7a2b 100644 --- a/lib/src/moodle/presentation/repositories/moodle_tasks_repository.dart +++ b/lib/src/moodle/presentation/repositories/moodle_tasks_repository.dart @@ -24,7 +24,7 @@ class MoodleTasksRepository extends Repository>> wit Duration get updateInterval => kRefreshIntervalDuration; @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { final transaction = startTransaction('loadTasks'); final tokens = waitForData(_auth); diff --git a/lib/src/moodle/presentation/repositories/users_repository.dart b/lib/src/moodle/presentation/repositories/users_repository.dart index 8b0cb589..a8310881 100644 --- a/lib/src/moodle/presentation/repositories/users_repository.dart +++ b/lib/src/moodle/presentation/repositories/users_repository.dart @@ -20,7 +20,7 @@ class UsersRepository extends Repository>> with Tracable { Duration get updateInterval => kLessImportantRefreshIntervalDuration; @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { final transaction = startTransaction('loadUsers'); await guard( () async => _datasource.getUsers( diff --git a/lib/src/notifications/presentation/repositories/notifications_repository.dart b/lib/src/notifications/presentation/repositories/notifications_repository.dart index 4fb8c5e2..bd1535e7 100644 --- a/lib/src/notifications/presentation/repositories/notifications_repository.dart +++ b/lib/src/notifications/presentation/repositories/notifications_repository.dart @@ -25,7 +25,7 @@ class NotificationsRepository extends Repository>> bool get hasUnreadNotifications => state.data?.any((element) => !element.read) ?? false; @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { waitForData(_auth); if (_auth.state.requireData.isEmpty) return; diff --git a/lib/src/slots/presentation/repositories/slot_master_courses_repository.dart b/lib/src/slots/presentation/repositories/slot_master_courses_repository.dart index 8cf828ce..c7bc367a 100644 --- a/lib/src/slots/presentation/repositories/slot_master_courses_repository.dart +++ b/lib/src/slots/presentation/repositories/slot_master_courses_repository.dart @@ -21,7 +21,7 @@ class SlotMasterCoursesRepository extends Repository kRefreshIntervalDuration; @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { final transaction = startTransaction('loadSlotsMasterCourses'); final tokens = waitForData(_auth); diff --git a/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart b/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart index 31d31270..29d98b56 100644 --- a/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart +++ b/lib/src/slots/presentation/repositories/slot_master_slots_repository.dart @@ -24,7 +24,7 @@ class SlotMasterSlotsRepository extends Repository>> with Duration get updateInterval => kRefreshIntervalDuration; @override - Future build(BuildTrigger trigger) async { + Future build(Trigger trigger) async { final transaction = startTransaction('loadSlotMasterSlots'); final token = waitForData(_auth); diff --git a/lib/src/slots/presentation/repositories/slots_repository.dart b/lib/src/slots/presentation/repositories/slots_repository.dart index e47d4866..55762167 100644 --- a/lib/src/slots/presentation/repositories/slots_repository.dart +++ b/lib/src/slots/presentation/repositories/slots_repository.dart @@ -27,7 +27,7 @@ class SlotsRepository extends Repository>> with Tracable { bool get refreshOptimization => true; @override - Future build(BuildTrigger trigger) async { + Future build(Trigger trigger) async { final transaction = startTransaction('loadSlots'); waitForData(_auth); diff --git a/lib/src/slots/presentation/repositories/supervisor_slots_repository.dart b/lib/src/slots/presentation/repositories/supervisor_slots_repository.dart index 2ec902b9..4a02cca7 100644 --- a/lib/src/slots/presentation/repositories/supervisor_slots_repository.dart +++ b/lib/src/slots/presentation/repositories/supervisor_slots_repository.dart @@ -21,7 +21,7 @@ class SupervisorSlotsRepository extends Repository>> with Duration get updateInterval => kImportantRefreshIntervalDuration; @override - Future build(BuildTrigger trigger) async { + Future build(Trigger trigger) async { final transaction = startTransaction('loadSupervisorSlots'); waitForData(_auth); diff --git a/lib/src/statistics/presentation/repositories/course_stats_repository.dart b/lib/src/statistics/presentation/repositories/course_stats_repository.dart index 9d55ed61..88d9fa1f 100644 --- a/lib/src/statistics/presentation/repositories/course_stats_repository.dart +++ b/lib/src/statistics/presentation/repositories/course_stats_repository.dart @@ -21,7 +21,7 @@ class CourseStatsRepository extends Repository build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { if (!_courses.state.hasData || !_tasks.state.hasData) return; final transaction = startTransaction('loadCourseStats'); diff --git a/lib/src/statistics/presentation/repositories/global_stats_repository.dart b/lib/src/statistics/presentation/repositories/global_stats_repository.dart index 7547ac1f..defed7f2 100644 --- a/lib/src/statistics/presentation/repositories/global_stats_repository.dart +++ b/lib/src/statistics/presentation/repositories/global_stats_repository.dart @@ -17,7 +17,7 @@ class GlobalStatsRepository extends Repository> with T } @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { if (!_tasks.state.hasData) return; final transaction = startTransaction('loadGlobalStats'); diff --git a/lib/src/theming/presentation/repositories/theme_repository.dart b/lib/src/theming/presentation/repositories/theme_repository.dart index 3813ab5f..f158e751 100644 --- a/lib/src/theming/presentation/repositories/theme_repository.dart +++ b/lib/src/theming/presentation/repositories/theme_repository.dart @@ -25,7 +25,7 @@ class ThemeRepository extends Repository with Tracable { } @override - FutureOr build(BuildTrigger trigger) async { + FutureOr build(Trigger trigger) async { if (!_user.state.hasData) return; final transaction = startTransaction('loadThemes'); From ab60f9477c49925a6f97b5dfb12f04494da200ad Mon Sep 17 00:00:00 2001 From: mcquenji Date: Sat, 15 Mar 2025 02:19:20 +0100 Subject: [PATCH 3/3] fix: redirect to dashboard after login --- .../screens/not_found_screen.dart | 34 ++++++++++--------- .../presentation/widgets/image_message.dart | 22 ++++++------ .../presentation/screens/login_screen.dart | 2 +- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lib/src/app/presentation/screens/not_found_screen.dart b/lib/src/app/presentation/screens/not_found_screen.dart index d3cade42..dfdd8690 100644 --- a/lib/src/app/presentation/screens/not_found_screen.dart +++ b/lib/src/app/presentation/screens/not_found_screen.dart @@ -12,22 +12,24 @@ class NotFoundScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - body: Padding( - padding: PaddingAll(), - child: Column( - children: [ - ImageMessage( - message: context.t.notFound, - image: Assets.a404, - ).expanded(), - Spacing.mediumVertical(), - ElevatedButton( - onPressed: () { - Modular.to.navigate('/dashboard/'); - }, - child: Text(context.t.notFound_returnHome), - ), - ], + body: Center( + child: Padding( + padding: PaddingAll(), + child: Column( + children: [ + ImageMessage( + message: context.t.notFound, + image: Assets.a404, + ).expanded(), + Spacing.mediumVertical(), + ElevatedButton( + onPressed: () { + Modular.to.navigate('/dashboard/'); + }, + child: Text(context.t.notFound_returnHome), + ), + ], + ), ), ), ); diff --git a/lib/src/app/presentation/widgets/image_message.dart b/lib/src/app/presentation/widgets/image_message.dart index 4a6ae5ff..a6f7661c 100644 --- a/lib/src/app/presentation/widgets/image_message.dart +++ b/lib/src/app/presentation/widgets/image_message.dart @@ -16,16 +16,18 @@ class ImageMessage extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - children: [ - image.themed(context).expanded(), - Spacing.mediumVertical(), - Text( - message, - style: context.theme.textTheme.titleMedium, - textAlign: TextAlign.center, - ), - ], + return Center( + child: Column( + children: [ + image.themed(context).expanded(), + Spacing.mediumVertical(), + Text( + message, + style: context.theme.textTheme.titleMedium, + textAlign: TextAlign.center, + ), + ], + ), ); } } diff --git a/lib/src/auth/presentation/screens/login_screen.dart b/lib/src/auth/presentation/screens/login_screen.dart index da48ebef..04764393 100644 --- a/lib/src/auth/presentation/screens/login_screen.dart +++ b/lib/src/auth/presentation/screens/login_screen.dart @@ -11,7 +11,7 @@ class LoginScreen extends StatelessWidget with AdaptiveWidget { const LoginScreen({super.key}); static void _onLogin() { - Modular.to.navigate('/dashboard'); + Modular.to.navigate('/dashboard/'); } @override