af5ecaeee1
- **Refonte du démarrage** : Mise en place d'un `AppInitializer` pour gérer l'initialisation asynchrone de Firebase et du cache en arrière-plan, réduisant le travail synchrone au lancement.
- **Sécurisation de l'authentification** :
- Création d'un `AppStartGate` pour gérer proprement la restauration de la session Firebase Auth et les erreurs potentielles sur le Web.
- Amélioration du `LocalUserProvider` avec un "bootstrap léger" permettant de rendre l'UID disponible immédiatement avant le chargement complet du profil.
- Ajout de protections contre les erreurs d'accès à `FirebaseAuth.instance` (notamment pour les problèmes d'interop JS sur le Web).
- **Optimisation de l'UI** :
- Remplacement du `AutoLoginWrapper` par une gestion plus robuste de la navigation post-authentification.
- Amélioration de l'`AuthGuard` pour permettre l'affichage de certains écrans (comme le calendrier) pendant le chargement des données utilisateur (`allowWhileLoading`).
- Ajout d'un écran de splash screen uniformisé (`StartupSplashScreen`).
- **Services & Cache** :
- Introduction de `CacheService` utilisant `shared_preferences` pour le stockage local léger.
- Refactoring des services (`AlertService`, `EmailService`, `FirebaseStorageManager`) pour accéder aux instances Firebase de manière plus flexible via des getters.
- Mise à jour des dépendances dans `pubspec.yaml` pour inclure `shared_preferences`.
- **Calendrier** : Ajout d'une logique de chargement initial différé des événements (`_scheduleInitialEventsLoad`) pour éviter les appels redondants au démarrage.
- **Maintenance** : Mise à jour de la version de l'application à `1.1.23` et nettoyage des fichiers de cache de déploiement.
245 lines
8.8 KiB
Dart
245 lines
8.8 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:em2rp/providers/users_provider.dart';
|
|
import 'package:em2rp/providers/event_provider.dart';
|
|
import 'package:em2rp/providers/equipment_provider.dart';
|
|
import 'package:em2rp/providers/container_provider.dart';
|
|
import 'package:em2rp/providers/maintenance_provider.dart';
|
|
import 'package:em2rp/providers/alert_provider.dart';
|
|
import 'package:em2rp/utils/auth_guard_widget.dart';
|
|
import 'package:em2rp/utils/performance_monitor.dart';
|
|
import 'package:em2rp/views/alerts_page.dart';
|
|
import 'package:em2rp/views/calendar_page.dart';
|
|
import 'package:em2rp/views/login_page.dart';
|
|
import 'package:em2rp/views/equipment_management_page.dart';
|
|
import 'package:em2rp/views/container_management_page.dart';
|
|
import 'package:em2rp/views/maintenance_management_page.dart';
|
|
import 'package:em2rp/views/container_form_page.dart';
|
|
import 'package:em2rp/views/container_detail_page.dart';
|
|
import 'package:em2rp/views/event_preparation_page.dart';
|
|
import 'package:em2rp/views/event_statistics_page.dart';
|
|
import 'package:em2rp/models/container_model.dart';
|
|
import 'package:em2rp/models/event_model.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:em2rp/services/app_initializer.dart';
|
|
import 'utils/colors.dart';
|
|
import 'views/my_account_page.dart';
|
|
import 'views/user_management_page.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'providers/local_user_provider.dart';
|
|
import 'views/reset_password_page.dart';
|
|
import 'config/env.dart';
|
|
import 'utils/app_start_gate.dart';
|
|
import 'views/widgets/common/startup_splash_screen.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
|
|
void main() {
|
|
// Ne pas effectuer d'initialisations asynchrones lourdes ici.
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
runApp(
|
|
MultiProvider(
|
|
providers: [
|
|
// Fournisseur d'initialisation de l'application (initialise Firebase et cache en tâche de fond)
|
|
ChangeNotifierProvider<AppInitializer>(
|
|
create: (_) => AppInitializer(),
|
|
),
|
|
// LocalUserProvider pour la gestion de l'authentification
|
|
ChangeNotifierProvider<LocalUserProvider>(
|
|
create: (context) => LocalUserProvider()),
|
|
|
|
// UsersProvider migré vers l'API
|
|
ChangeNotifierProvider<UsersProvider>(
|
|
create: (context) => UsersProvider(),
|
|
),
|
|
|
|
// EventProvider migré vers l'API
|
|
ChangeNotifierProvider<EventProvider>(
|
|
create: (context) => EventProvider(),
|
|
),
|
|
|
|
// EquipmentProvider migré vers l'API
|
|
ChangeNotifierProvider<EquipmentProvider>(
|
|
create: (context) => EquipmentProvider(),
|
|
),
|
|
|
|
// ContainerProvider migré vers l'API
|
|
ChangeNotifierProvider<ContainerProvider>(
|
|
create: (context) => ContainerProvider(),
|
|
),
|
|
|
|
// MaintenanceProvider migré vers l'API
|
|
ChangeNotifierProvider<MaintenanceProvider>(
|
|
create: (context) => MaintenanceProvider(),
|
|
),
|
|
ChangeNotifierProvider<AlertProvider>(
|
|
create: (context) => AlertProvider(),
|
|
),
|
|
],
|
|
child: const MyApp(),
|
|
),
|
|
);
|
|
}
|
|
|
|
class MyApp extends StatefulWidget {
|
|
const MyApp({super.key});
|
|
|
|
@override
|
|
State<MyApp> createState() => _MyAppState();
|
|
}
|
|
|
|
class _MyAppState extends State<MyApp> {
|
|
late final Future<void> _startupFuture;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_startupFuture = _bootstrapApp();
|
|
}
|
|
|
|
Future<void> _bootstrapApp() async {
|
|
final initializer = context.read<AppInitializer>();
|
|
final localAuthProvider = context.read<LocalUserProvider>();
|
|
|
|
await initializer.initialize();
|
|
|
|
// Attendre la première valeur d'authentification avant toute décision
|
|
// de navigation, afin d'éviter un flash de la page login.
|
|
await FirebaseAuth.instance.authStateChanges().first;
|
|
|
|
if (FirebaseAuth.instance.currentUser != null) {
|
|
unawaited(
|
|
localAuthProvider.loadUserData().catchError((e) {
|
|
print('User data bootstrap failed: $e');
|
|
}),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// En développement, on garde la connexion automatique existante.
|
|
if (Env.isDevelopment) {
|
|
await localAuthProvider.signInWithEmailAndPassword(
|
|
Env.devAdminEmail,
|
|
Env.devAdminPassword,
|
|
);
|
|
unawaited(
|
|
localAuthProvider.loadUserData().catchError((e) {
|
|
print('Dev user bootstrap failed: $e');
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return FutureBuilder<void>(
|
|
future: _startupFuture,
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState != ConnectionState.done) {
|
|
return const MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
home: StartupSplashScreen(),
|
|
);
|
|
}
|
|
|
|
return MaterialApp(
|
|
title: 'EM2 Hub',
|
|
theme: ThemeData(
|
|
primarySwatch: Colors.red,
|
|
primaryColor: AppColors.noir,
|
|
colorScheme:
|
|
ColorScheme.fromSwatch().copyWith(secondary: AppColors.rouge),
|
|
textTheme: const TextTheme(
|
|
bodyMedium: TextStyle(color: AppColors.noir),
|
|
),
|
|
inputDecorationTheme: const InputDecorationTheme(
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(color: AppColors.noir),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(color: AppColors.gris),
|
|
),
|
|
labelStyle: TextStyle(color: AppColors.noir),
|
|
hintStyle: TextStyle(color: AppColors.gris),
|
|
),
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
foregroundColor: AppColors.blanc,
|
|
backgroundColor: AppColors.noir,
|
|
),
|
|
),
|
|
),
|
|
locale: const Locale('fr', 'FR'),
|
|
supportedLocales: const [
|
|
Locale('fr', 'FR'),
|
|
],
|
|
localizationsDelegates: const [
|
|
GlobalMaterialLocalizations.delegate,
|
|
GlobalWidgetsLocalizations.delegate,
|
|
GlobalCupertinoLocalizations.delegate,
|
|
],
|
|
routes: {
|
|
'/login': (context) => const LoginPage(),
|
|
'/alerts': (context) => const AuthGuard(child: AlertsPage()),
|
|
'/calendar': (context) => const AuthGuard(
|
|
allowWhileLoading: true, child: CalendarPage()),
|
|
'/my_account': (context) => const AuthGuard(child: MyAccountPage()),
|
|
'/user_management': (context) => const AuthGuard(
|
|
requiredPermission: "view_all_users",
|
|
child: UserManagementPage()),
|
|
'/reset_password': (context) {
|
|
final args = ModalRoute.of(context)!.settings.arguments
|
|
as Map<String, dynamic>;
|
|
return ResetPasswordPage(
|
|
email: args['email'] as String,
|
|
actionCode: args['actionCode'] as String,
|
|
);
|
|
},
|
|
'/equipment_management': (context) => const AuthGuard(
|
|
requiredPermission: "view_equipment",
|
|
child: EquipmentManagementPage()),
|
|
'/container_management': (context) => const AuthGuard(
|
|
requiredPermission: "view_equipment",
|
|
child: ContainerManagementPage()),
|
|
'/maintenance_management': (context) => const AuthGuard(
|
|
requiredPermission: "manage_maintenances",
|
|
child: MaintenanceManagementPage()),
|
|
'/container_form': (context) {
|
|
final args = ModalRoute.of(context)?.settings.arguments;
|
|
return AuthGuard(
|
|
requiredPermission: "manage_equipment",
|
|
child: ContainerFormPage(
|
|
container: args as ContainerModel?,
|
|
),
|
|
);
|
|
},
|
|
'/container_detail': (context) {
|
|
final container = ModalRoute.of(context)!.settings.arguments
|
|
as ContainerModel;
|
|
return AuthGuard(
|
|
requiredPermission: "view_equipment",
|
|
child: ContainerDetailPage(container: container),
|
|
);
|
|
},
|
|
'/event_preparation': (context) {
|
|
final args = ModalRoute.of(context)!.settings.arguments
|
|
as Map<String, dynamic>;
|
|
final event = args['event'] as EventModel;
|
|
return AuthGuard(
|
|
child: EventPreparationPage(
|
|
initialEvent: event,
|
|
),
|
|
);
|
|
},
|
|
'/event_statistics': (context) => const AuthGuard(
|
|
requiredPermission: 'generate_reports',
|
|
child: EventStatisticsPage()),
|
|
},
|
|
home: const AppStartGate(),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|