import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import '../utils/app_update_util.dart'; import '../viewmodels/admin_pin_view_model.dart'; import '../viewmodels/pin_lock_view_model.dart'; import '../viewmodels/remote_sync_view_model.dart'; import '../views/admin_screen_view.dart'; import '../views/bar/bar_screen_view.dart'; import '../views/dev_menu_view.dart'; import '../views/error_screen_view.dart'; import '../views/history_screen_view.dart'; import '../views/import_names_view.dart'; import '../views/pin_lock_view.dart'; import '../views/product_form_view.dart'; import '../views/product_list_view.dart'; import '../views/registration_view.dart'; import '../views/settings_view.dart'; import '../views/update_progress_view.dart'; final RouteObserver routeObserver = RouteObserver(); String? _redirectApp({ required PinLockViewModel pinLockViewModel, required AdminPinViewModel adminPinViewModel, required RemoteSyncViewModel remoteSyncViewModel, required GoRouterState state, }) { if (!pinLockViewModel.hasLoaded || !adminPinViewModel.hasLoaded || !remoteSyncViewModel.hasLoaded) { return null; } final location = state.uri.path; final isConfigured = remoteSyncViewModel.isConfigured; if (!isConfigured && location != '/register') { return '/register'; } if (isConfigured && location == '/register') { return '/bar'; } if (!isConfigured) return null; final goingToLock = location == '/lock'; final goingToAdminLock = location == '/admin/lock'; final mustBeOnLock = _mustBeOnLock(pinLockViewModel); if (mustBeOnLock && !goingToLock) return '/lock'; if (!mustBeOnLock && goingToLock) return '/bar'; final isAdminArea = _isAdminArea(location); final isDebugShortcutArea = _isDebugShortcutArea(location); if (isAdminArea && !isDebugShortcutArea && !goingToAdminLock && !adminPinViewModel.isUnlocked) { return '/admin/lock'; } if (goingToAdminLock && adminPinViewModel.isUnlocked) return '/admin'; return null; } bool _mustBeOnLock(PinLockViewModel pinLockViewModel) { final needsPinSetup = !pinLockViewModel.isPinSet && pinLockViewModel.pinRequired; final isLocked = pinLockViewModel.isPinSet && !pinLockViewModel.isUnlocked && pinLockViewModel.pinRequired; return needsPinSetup || isLocked; } bool _isAdminArea(String location) { return location == '/admin' || location.startsWith('/admin/') || location == '/products' || location.startsWith('/products/') || location == '/history' || location.startsWith('/history/'); } bool _isDebugShortcutArea(String location) { if (!kDebugMode) return false; return location == '/products' || location.startsWith('/products/') || location == '/history' || location.startsWith('/history/'); } GoRouter createAppRouter({ required PinLockViewModel pinLockViewModel, required AdminPinViewModel adminPinViewModel, required RemoteSyncViewModel remoteSyncViewModel, }) { return GoRouter( initialLocation: '/bar', observers: [routeObserver, SentryNavigatorObserver()], refreshListenable: Listenable.merge([ pinLockViewModel, adminPinViewModel, remoteSyncViewModel, ]), redirect: (_, state) => _redirectApp( pinLockViewModel: pinLockViewModel, adminPinViewModel: adminPinViewModel, remoteSyncViewModel: remoteSyncViewModel, state: state, ), routes: [ GoRoute(path: '/', redirect: (context, state) => '/bar'), GoRoute( path: '/register', builder: (context, state) => const RegistrationView(), ), GoRoute( path: '/lock', builder: (context, state) => PinEntryView( mode: pinLockViewModel.isPinSet ? PinEntryMode.unlock : PinEntryMode.create, onSuccess: () { context.go('/bar'); }, ), ), GoRoute(path: '/bar', builder: (context, state) => const BarScreenView()), GoRoute( path: '/admin/lock', builder: (context, state) => PinEntryView( mode: adminPinViewModel.isPinSet ? PinEntryMode.unlock : PinEntryMode.create, isAdmin: true, onVerify: adminPinViewModel.verify, onSet: adminPinViewModel.setPin, error: () => adminPinViewModel.error, onSuccess: () => context.go('/admin'), ), ), GoRoute( path: '/admin', builder: (context, state) => const AdminScreenView(), routes: [ GoRoute( path: 'import', builder: (context, state) => const ImportNamesView(), ), ], ), GoRoute( path: '/products', builder: (context, state) => const ProductListView(), routes: [ GoRoute( path: 'new', builder: (context, state) => const ProductFormView(), ), GoRoute( path: ':id/edit', builder: (context, state) { final productId = state.pathParameters['id']!; return ProductFormView(productId: productId); }, ), ], ), GoRoute( path: '/history', builder: (context, state) => const HistoryScreenView(), ), GoRoute( path: '/settings', builder: (context, state) => const SettingsScreenView(), ), GoRoute( path: '/update-progress', builder: (context, state) { final update = state.extra as UpdateInfo; final simulate = state.uri.queryParameters['simulate'] == 'true'; final simulateError = state.uri.queryParameters['simulateError'] == 'true'; return UpdateProgressView( update: update, simulate: simulate, simulateError: simulateError, ); }, ), GoRoute(path: '/dev', builder: (context, state) => const DevMenuView()), GoRoute( path: '/error', builder: (context, state) => const ErrorScreenView(), ), ], ); }