import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../features/tasks/application/task_list_navigation.dart'; import '../../features/extensions/presentation/pages/extensions_page.dart'; import '../../features/extensions/presentation/pages/extension_details_page.dart'; import '../../features/extensions/application/extensions_controller.dart'; import '../../features/auth/application/web_auth_controller.dart'; import '../../features/auth/presentation/pages/login_page.dart'; import '../../features/home/presentation/pages/home_page.dart'; import '../../features/settings/presentation/pages/settings_page.dart'; import '../../features/tasks/presentation/pages/create_task_window_page.dart'; import '../../features/tasks/domain/task_record.dart'; import '../../features/tasks/presentation/pages/task_details_page.dart'; import '../../features/tasks/presentation/pages/task_files_page.dart'; import 'shells/main_shell.dart'; import 'mobile_exit_guard.dart'; import 'runtime_page.dart'; class AppRouter { static final GlobalKey rootNavigatorKey = GlobalKey(); static void showDownloadingTasks() { final context = rootNavigatorKey.currentContext; if (context == null || !context.mounted) return; ProviderScope.containerOf(context, listen: false).read(taskListNavigationProvider.notifier).showDownloading(); GoRouter.of(context).go('/'); } static GoRouter build(WebAuthController webAuthController) { // Keep desktop and web sections as peers; native mobile retains home below them. final isMobile = !kIsWeb && (defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); final sectionRoutes = [ GoRoute( path: isMobile ? 'extensions' : '/extensions', builder: _runtimePage((context, state) => const ExtensionsPage()), routes: [ GoRoute( path: ':id', builder: _runtimePage( (context, state) => ExtensionDetailsPage( extensionId: state.pathParameters['id'] ?? '', initialItem: state.extra is ExtensionListItem ? state.extra as ExtensionListItem : null, ), ), ), ], ), GoRoute( path: isMobile ? 'settings' : '/settings', builder: _runtimePage((context, state) => const SettingsPage()), routes: [ GoRoute( path: ':section', builder: _runtimePage((context, state) => SettingsPage(sectionKey: state.pathParameters['section'])), ), ], ), ]; return GoRouter( navigatorKey: rootNavigatorKey, refreshListenable: webAuthController, redirect: (context, state) { final onLoginPage = state.matchedLocation == '/login'; if (webAuthController.loginRequired) { if (onLoginPage) return null; return Uri(path: '/login', queryParameters: {'from': state.uri.toString()}).toString(); } if (onLoginPage) { return _safeReturnLocation(state.uri.queryParameters['from']); } return null; }, routes: [ GoRoute(path: '/login', parentNavigatorKey: rootNavigatorKey, builder: (context, state) => const LoginPage()), ShellRoute( builder: (context, state, child) => MobileExitGuard(child: MainShell(child: child)), routes: [ GoRoute( path: '/', builder: _runtimePage((context, state) => const HomePage()), routes: [ GoRoute(path: 'create', builder: _runtimePage((context, state) => const CreateTaskWindowPage())), GoRoute( path: 'tasks/:id', builder: _runtimePage( (context, state) => TaskDetailsPage( taskId: state.pathParameters['id'] ?? '', initialTask: state.extra is TaskRecord ? state.extra as TaskRecord : null, ), ), routes: [ GoRoute( path: 'files', builder: _runtimePage( (context, state) => TaskFilesPage( taskId: state.pathParameters['id'] ?? '', initialTask: state.extra is TaskRecord ? state.extra as TaskRecord : null, ), ), ), ], ), if (isMobile) ...sectionRoutes, ], ), if (!isMobile) ...sectionRoutes, ], ), ], ); } static GoRouterWidgetBuilder _runtimePage(GoRouterWidgetBuilder builder) { return (context, state) => RuntimePage(builder: (context) => builder(context, state)); } static String _safeReturnLocation(String? location) { if (location == null || location.isEmpty) return '/'; final uri = Uri.tryParse(location); if (uri == null || uri.hasScheme || uri.host.isNotEmpty || !location.startsWith('/') || uri.path == '/login') { return '/'; } return location; } }