import 'dart:async'; import 'dart:html' as html; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'injection.dart' as di; import 'presentation/blocs/auth/auth_bloc.dart'; import 'presentation/blocs/auth/auth_event.dart'; import 'presentation/blocs/auth/auth_state.dart'; import 'presentation/blocs/server/server_bloc.dart'; import 'presentation/blocs/server/server_event.dart'; import 'presentation/blocs/action_mapping/action_mapping_bloc.dart'; import 'presentation/blocs/action_mapping/action_mapping_event.dart'; import 'presentation/blocs/geviserver/geviserver_bloc.dart'; import 'presentation/blocs/geviscope/geviscope_bloc.dart'; import 'presentation/screens/auth/login_screen.dart'; import 'presentation/screens/geviserver/geviserver_screen.dart'; import 'presentation/screens/geviscope/geviscope_screen.dart'; import 'presentation/screens/servers/server_list_screen.dart'; import 'presentation/screens/servers/servers_management_screen.dart'; import 'presentation/screens/servers/server_form_screen.dart'; import 'presentation/screens/action_mappings/action_mappings_list_screen.dart'; import 'presentation/screens/action_mappings/action_mapping_form_screen.dart'; import 'domain/entities/server.dart'; import 'domain/entities/action_mapping.dart'; import 'data/models/server_hive_model.dart'; import 'data/models/action_mapping_hive_model.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize Hive for local storage await Hive.initFlutter(); // Register Hive adapters Hive.registerAdapter(ServerHiveModelAdapter()); Hive.registerAdapter(ActionMappingHiveModelAdapter()); // Initialize dependency injection await di.init(); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { late final AuthBloc _authBloc; late final GoRouter _router; @override void initState() { super.initState(); _authBloc = di.sl()..add(const CheckAuthStatus()); _router = _createRouter(_authBloc); } @override void dispose() { _authBloc.close(); super.dispose(); } @override Widget build(BuildContext context) { return BlocProvider.value( value: _authBloc, child: MaterialApp.router( title: 'GeViAPI - Video Management System', debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: Colors.blue, brightness: Brightness.light, ), useMaterial3: true, appBarTheme: const AppBarTheme( centerTitle: false, elevation: 2, ), cardTheme: CardThemeData( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), ), routerConfig: _router, ), ); } } GoRouter _createRouter(AuthBloc authBloc) { return GoRouter( routes: [ GoRoute( path: '/login', builder: (context, state) => BlocProvider.value( value: context.read(), child: const LoginScreen(), ), ), GoRoute( path: '/', builder: (context, state) => BlocProvider.value( value: context.read(), child: const ServerListScreen(), ), ), ShellRoute( builder: (context, state, child) => BlocProvider( create: (_) => di.sl()..add(const LoadServers()), child: child, ), routes: [ GoRoute( path: '/servers', builder: (context, state) => const ServersManagementScreen(), ), GoRoute( path: '/servers/create', builder: (context, state) { final serverType = state.uri.queryParameters['type'] == 'geviscope' ? ServerType.geviscope : ServerType.gcore; return ServerFormScreen(serverType: serverType); }, ), GoRoute( path: '/servers/edit/:id', builder: (context, state) { final server = state.extra as Server; return ServerFormScreen(server: server, serverType: server.type); }, ), ], ), ShellRoute( builder: (context, state, child) => BlocProvider( create: (_) => di.sl()..add(const LoadActionMappings()), child: child, ), routes: [ GoRoute( path: '/action-mappings', builder: (context, state) => const ActionMappingsListScreen(), ), GoRoute( path: '/action-mappings/create', builder: (context, state) => const ActionMappingFormScreen(), ), GoRoute( path: '/action-mappings/edit/:id', builder: (context, state) { final mapping = state.extra as ActionMapping; return ActionMappingFormScreen(mapping: mapping); }, ), ], ), GoRoute( path: '/geviserver', builder: (context, state) => BlocProvider( create: (_) => di.sl(), child: const GeViServerScreen(), ), ), GoRoute( path: '/geviscope', builder: (context, state) => BlocProvider( create: (_) => di.sl(), child: const GeViScopeScreen(), ), ), ], redirect: (context, state) { final authState = authBloc.state; final isLoginRoute = state.matchedLocation == '/login'; if (authState is Authenticated) { // Check for post-import redirect flag final postImportRedirect = html.window.localStorage['post_import_redirect']; if (postImportRedirect != null && postImportRedirect.isNotEmpty) { // Clear the flag html.window.localStorage.remove('post_import_redirect'); // Redirect to the saved path print('[Router] Post-import redirect to: $postImportRedirect'); return postImportRedirect; } // If authenticated and trying to access login, redirect to home if (isLoginRoute) { return '/'; } } else { // If not authenticated and not on login page, redirect to login if (!isLoginRoute) { return '/login'; } } // No redirect needed return null; }, refreshListenable: GoRouterRefreshStream( authBloc.stream, ), ); } class GoRouterRefreshStream extends ChangeNotifier { GoRouterRefreshStream(Stream stream) { notifyListeners(); _subscription = stream.asBroadcastStream().listen( (dynamic _) => notifyListeners(), ); } late final StreamSubscription _subscription; @override void dispose() { _subscription.cancel(); super.dispose(); } }