Files
geo/app/lib/app.dart

291 lines
11 KiB
Dart
Executable File

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:geosector_app/core/services/theme_service.dart';
import 'package:go_router/go_router.dart';
import 'package:geosector_app/core/services/current_user_service.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/repositories/operation_repository.dart';
import 'package:geosector_app/core/repositories/passage_repository.dart';
import 'package:geosector_app/core/repositories/sector_repository.dart';
import 'package:geosector_app/core/repositories/membre_repository.dart';
import 'package:geosector_app/core/repositories/amicale_repository.dart';
import 'package:geosector_app/core/services/sync_service.dart';
import 'package:geosector_app/core/services/connectivity_service.dart';
import 'package:geosector_app/core/services/chat_manager.dart';
import 'package:geosector_app/presentation/auth/splash_page.dart';
import 'package:geosector_app/presentation/auth/login_page.dart';
import 'package:geosector_app/presentation/auth/register_page.dart';
import 'package:geosector_app/presentation/admin/admin_dashboard_page.dart';
import 'package:geosector_app/presentation/user/user_dashboard_page.dart';
// Instances globales des repositories (plus besoin d'injecter ApiService)
final operationRepository = OperationRepository();
final passageRepository = PassageRepository();
final userRepository = UserRepository();
final sectorRepository = SectorRepository();
final membreRepository = MembreRepository();
final amicaleRepository = AmicaleRepository();
final syncService = SyncService(userRepository: userRepository);
final connectivityService = ConnectivityService();
final themeService = ThemeService.instance;
class GeosectorApp extends StatefulWidget {
const GeosectorApp({super.key});
@override
State<GeosectorApp> createState() => _GeosectorAppState();
}
class _GeosectorAppState extends State<GeosectorApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
// Arrêter le chat quand l'app se ferme
ChatManager.instance.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
// App revenue au premier plan - relancer les syncs
debugPrint('📱 App au premier plan - Reprise des syncs chat');
ChatManager.instance.resumeSyncs();
break;
case AppLifecycleState.paused:
// App mise en arrière-plan - arrêter les syncs pour économiser la batterie
debugPrint('⏸️ App en arrière-plan - Pause des syncs chat');
ChatManager.instance.pauseSyncs();
break;
case AppLifecycleState.inactive:
// État transitoire (ex: appel entrant) - ne rien faire
debugPrint('💤 App inactive temporairement');
break;
case AppLifecycleState.detached:
// App vraiment fermée (rare sur mobile) - nettoyer complètement
debugPrint('🛑 App fermée complètement - Arrêt total du chat');
ChatManager.instance.dispose();
break;
case AppLifecycleState.hidden:
// État masqué (Flutter 3.13+) - traiter comme paused
debugPrint('👻 App masquée');
ChatManager.instance.pauseSyncs();
break;
}
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: themeService,
builder: (context, child) {
return MaterialApp.router(
title: 'GeoSector',
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
themeMode: themeService.themeMode,
routerConfig: _createRouter(),
debugShowCheckedModeBanner: false,
// Configuration des localisations pour le français
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('fr', 'FR'), // Français comme langue principale
Locale('en', 'US'), // Anglais en fallback
],
locale: const Locale('fr', 'FR'), // Forcer le français par défaut
);
},
);
}
/// Création du routeur avec configuration pour URLs propres
GoRouter _createRouter() {
return GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
name: 'splash',
builder: (context, state) {
// Récupérer les paramètres de query pour redirection automatique
final action = state.uri.queryParameters['action'];
final type = state.uri.queryParameters['type'];
debugPrint('GoRoute: Affichage de SplashPage avec action=$action, type=$type');
return SplashPage(action: action, type: type);
},
),
GoRoute(
path: '/login',
name: 'login',
builder: (context, state) {
// Récupérer le type depuis les query parameters ou extra data
final type = state.uri.queryParameters['type'] ?? (state.extra as Map<String, dynamic>?)?['type'] as String?;
debugPrint('GoRoute: Affichage de LoginPage avec type: $type');
return LoginPage(loginType: type);
},
),
// Routes spécifiques pour chaque type de login
GoRoute(
path: '/login/user',
name: 'login-user',
builder: (context, state) {
debugPrint('GoRoute: Affichage de LoginPage pour utilisateur');
return const LoginPage(loginType: 'user');
},
),
GoRoute(
path: '/login/admin',
name: 'login-admin',
builder: (context, state) {
debugPrint('GoRoute: Affichage de LoginPage pour admin');
return const LoginPage(loginType: 'admin');
},
),
GoRoute(
path: '/register',
name: 'register',
builder: (context, state) {
debugPrint('GoRoute: Affichage de RegisterPage');
return const RegisterPage();
},
),
GoRoute(
path: '/user',
name: 'user',
builder: (context, state) {
debugPrint('GoRoute: Affichage de UserDashboardPage');
return const UserDashboardPage();
},
),
GoRoute(
path: '/admin',
name: 'admin',
builder: (context, state) {
debugPrint('GoRoute: Affichage de AdminDashboardPage');
return const AdminDashboardPage();
},
),
],
redirect: (context, state) {
final currentPath = state.uri.path;
debugPrint('GoRouter.redirect: currentPath = $currentPath');
// Pour la page racine, toujours autoriser l'affichage de la splash page
if (currentPath == '/') {
debugPrint('GoRouter.redirect: Autorisation splash page');
return null;
}
// Pages publiques qui ne nécessitent pas d'authentification
final publicPaths = ['/login', '/login/user', '/login/admin', '/register'];
if (publicPaths.any((path) => currentPath.startsWith(path))) {
debugPrint('GoRouter.redirect: Page publique autorisée: $currentPath');
return null;
}
// Vérifier l'authentification pour les pages protégées
try {
// Utiliser le nouveau CurrentUserService
final userService = CurrentUserService.instance;
final isAuthenticated = userService.isLoggedIn;
final currentUser = userService.currentUser;
debugPrint('GoRouter.redirect: isAuthenticated = $isAuthenticated');
debugPrint('GoRouter.redirect: currentUser = ${currentUser?.email}');
// Si pas authentifié, rediriger vers la splash page
if (!isAuthenticated) {
debugPrint('GoRouter.redirect: Non authentifié, redirection vers /');
return '/';
}
// Vérifier les permissions pour les pages admin
if (currentPath.startsWith('/admin')) {
final userRole = userService.userRole;
final isAdmin = userService.canAccessAdmin;
debugPrint('GoRouter.redirect: userRole = $userRole, canAccessAdmin = $isAdmin');
if (!isAdmin) {
debugPrint('GoRouter.redirect: Pas admin, redirection vers /user');
return '/user';
}
}
// Si on arrive ici, l'utilisateur a les permissions nécessaires
debugPrint('GoRouter.redirect: Accès autorisé à $currentPath');
return null;
} catch (e) {
debugPrint('GoRouter.redirect: Erreur lors de la vérification auth: $e');
// En cas d'erreur, rediriger vers la splash page pour sécurité
return '/';
}
},
// Listener pour déboguer les changements de route
refreshListenable: CurrentUserService.instance, // Écouter les changements dans CurrentUserService
debugLogDiagnostics: true, // Activer les logs de débogage
errorBuilder: (context, state) {
debugPrint('GoRouter.errorBuilder: Erreur pour ${state.uri.path}');
return Scaffold(
appBar: AppBar(
title: const Text('Erreur de navigation'),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 64, color: Colors.red),
const SizedBox(height: 16),
Text(
'Page non trouvée',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
'Chemin: ${state.uri.path}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[600],
),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
debugPrint('GoRouter.errorBuilder: Retour vers /');
context.go('/');
},
icon: const Icon(Icons.home),
label: const Text('Retour à l\'accueil'),
),
],
),
),
),
);
},
);
}
}