Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/app/router/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:webtrit_phone/blocs/app/app_bloc.dart';
import 'package:webtrit_phone/data/data.dart';
import 'package:webtrit_phone/features/features.dart';
import 'package:webtrit_phone/models/models.dart';
import 'package:webtrit_phone/repositories/repositories.dart';
import 'package:webtrit_phone/resolvers/resolvers.dart';

import 'deeplinks.dart';
Expand All @@ -27,6 +28,7 @@ class AppRouter extends RootStackRouter {
AppRouter(
this._appBloc,
this._appPermissions,
this._systemInfoRepository,
EmbeddedData? launchEmbeddedData,
BottomMenuConfig bottomMenuFeature,
InitialTabResolver initialTabResolver,
Expand All @@ -40,6 +42,7 @@ class AppRouter extends RootStackRouter {

final AppBloc _appBloc;
final AppPermissions _appPermissions;
final SystemInfoRepository _systemInfoRepository;

late EmbeddedData? _launchEmbeddedData;
late BottomMenuConfig _bottomMenuFeature;
Expand Down Expand Up @@ -339,6 +342,25 @@ class AppRouter extends RootStackRouter {
return;
}

// Ensure system info is in cache before MainShell builds.
// Several provider create: callbacks call getLocalSystemInfo() synchronously;
// without this guard the app crashes if system info was cleared (e.g. during
// session cleanup after an FGS failure) or was never fetched in this session.
try {
final systemInfo = await _systemInfoRepository.getSystemInfo(fetchPolicy: FetchPolicy.cacheFirst);
if (systemInfo == null) {
_logger.warning('onMainShellRouteGuardNavigation: system info unavailable, redirecting to login');
resolver.next(false);
router.replaceAll([LoginRouterPageRoute(launchEmbeddedData: _launchEmbeddedData)]);
return;
}
} catch (e, s) {
_logger.severe('onMainShellRouteGuardNavigation: failed to load system info', e, s);
resolver.next(false);
router.replaceAll([LoginRouterPageRoute(launchEmbeddedData: _launchEmbeddedData)]);
Comment on lines +352 to +360

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redirecting to LoginRouterPageRoute here can cause an infinite redirect loop: the login route is guarded by onLoginScreenPageRouteGuardNavigation, which blocks navigation when session.isLoggedIn is true. In this failure path the app is still in authenticated state (and the session token/coreUrl are still present), so the login guard will immediately redirect back to MainShellRoute, re-trigger this system-info fetch, and loop. Instead of routing to login directly, trigger a real logout/cleanup (e.g., dispatch AppLogoutRequested with an appropriate reason and route to the teardown screen), or clear the session before navigating to the login flow so the login guard allows it.

Suggested change
_logger.warning('onMainShellRouteGuardNavigation: system info unavailable, redirecting to login');
resolver.next(false);
router.replaceAll([LoginRouterPageRoute(launchEmbeddedData: _launchEmbeddedData)]);
return;
}
} catch (e, s) {
_logger.severe('onMainShellRouteGuardNavigation: failed to load system info', e, s);
resolver.next(false);
router.replaceAll([LoginRouterPageRoute(launchEmbeddedData: _launchEmbeddedData)]);
_logger.warning(
'onMainShellRouteGuardNavigation: system info unavailable, blocking navigation until session cleanup completes',
);
resolver.next(false);
return;
}
} catch (e, s) {
_logger.severe('onMainShellRouteGuardNavigation: failed to load system info', e, s);
resolver.next(false);

Copilot uses AI. Check for mistakes.
return;
}

// Enforce mandatory legal agreements and system permissions.
final contactsSourceTypes = _bottomMenuFeature.getTabEnabled<ContactsBottomMenuTab>()?.contactSourceTypes;
final isLocalContactsEnabled = contactsSourceTypes?.contains(ContactSourceType.local) ?? false;
Expand Down
1 change: 1 addition & 0 deletions lib/app/view/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class _AppState extends State<App> {
appRouter = AppRouter(
appBloc,
context.read<AppPermissions>(),
context.read<SystemInfoRepository>(),
featureAccess.loginConfig.launchLoginPage,
featureAccess.bottomMenuConfig,
initialTabResolver,
Expand Down
Loading