feat: Version 3.3.4 - Nouvelle architecture pages, optimisations widgets Flutter et API
- Mise à jour VERSION vers 3.3.4 - Optimisations et révisions architecture API (deploy-api.sh, scripts de migration) - Ajout documentation Stripe Tap to Pay complète - Migration vers polices Inter Variable pour Flutter - Optimisations build Android et nettoyage fichiers temporaires - Amélioration système de déploiement avec gestion backups - Ajout scripts CRON et migrations base de données 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
899
app/docs/FLOW-BOOT-APP.md
Normal file
899
app/docs/FLOW-BOOT-APP.md
Normal file
@@ -0,0 +1,899 @@
|
||||
# FLOW DE DÉMARRAGE DE L'APPLICATION GEOSECTOR
|
||||
|
||||
**Version** : 3.2.4
|
||||
**Date** : 04 octobre 2025
|
||||
**Objectif** : Cartographie complète du démarrage de l'application jusqu'à `login_page.dart`
|
||||
|
||||
---
|
||||
|
||||
## 📋 Table des matières
|
||||
|
||||
1. [Vue d'ensemble](#-vue-densemble)
|
||||
2. [Flow normal de démarrage](#-flow-normal-de-démarrage)
|
||||
3. [Flow avec nettoyage du cache](#-flow-avec-nettoyage-du-cache)
|
||||
4. [Gestion des Hive Box](#-gestion-des-hive-box)
|
||||
5. [Vérifications et redirections](#-vérifications-et-redirections)
|
||||
6. [Points critiques](#-points-critiques)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'ensemble
|
||||
|
||||
L'application GEOSECTOR utilise une architecture de démarrage en **3 étapes principales** :
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[main.dart] --> B[SplashPage]
|
||||
B --> C[LoginPage]
|
||||
C --> D[UserPage / AdminPage]
|
||||
|
||||
style A fill:#e1f5ff
|
||||
style B fill:#fff4e1
|
||||
style C fill:#e8f5e9
|
||||
style D fill:#f3e5f5
|
||||
```
|
||||
|
||||
**Responsabilités** :
|
||||
- **main.dart** : Initialisation minimale des services et Hive
|
||||
- **SplashPage** : Initialisation complète Hive + vérification permissions GPS
|
||||
- **LoginPage** : Validation Hive + formulaire de connexion
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Flow normal de démarrage
|
||||
|
||||
### **1. Point d'entrée : `main.dart`**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant M as main()
|
||||
participant AS as ApiService
|
||||
participant H as Hive
|
||||
participant App as GeosectorApp
|
||||
|
||||
M->>M: usePathUrlStrategy()
|
||||
M->>M: WidgetsFlutterBinding.ensureInitialized()
|
||||
|
||||
M->>AS: ApiService.initialize()
|
||||
Note over AS: Détection environnement<br/>(DEV/REC/PROD)
|
||||
AS-->>M: ✅ ApiService prêt
|
||||
|
||||
M->>H: Hive.initFlutter()
|
||||
Note over H: Initialisation minimale<br/>PAS d'adaptateurs<br/>PAS de Box
|
||||
H-->>M: ✅ Hive base initialisé
|
||||
|
||||
M->>App: runApp(GeosectorApp())
|
||||
App->>App: Build MaterialApp.router
|
||||
App->>App: Route initiale: '/' (SplashPage)
|
||||
```
|
||||
|
||||
#### **Code : main.dart (lignes 10-32)**
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
usePathUrlStrategy(); // URLs sans #
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await _initializeServices(); // ApiService + autres
|
||||
await _initializeHive(); // Hive.initFlutter() seulement
|
||||
|
||||
runApp(const GeosectorApp()); // Lancer l'app
|
||||
}
|
||||
```
|
||||
|
||||
**🔑 Points clés :**
|
||||
- ✅ **Initialisation minimale** : Pas d'adaptateurs, pas de Box
|
||||
- ✅ **Services singleton** : ApiService, CurrentUserService, etc.
|
||||
- ✅ **Hive base** : Juste `Hive.initFlutter()`, le reste dans SplashPage
|
||||
|
||||
---
|
||||
|
||||
### **2. Étape d'initialisation : `SplashPage`**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant SP as SplashPage
|
||||
participant HS as HiveService
|
||||
participant LS as LocationService
|
||||
participant GPS as Permissions GPS
|
||||
|
||||
SP->>SP: initState()
|
||||
SP->>SP: _getAppVersion()
|
||||
SP->>SP: _startInitialization()
|
||||
|
||||
Note over SP: Progress: 0%
|
||||
|
||||
alt Sur Mobile (non-Web)
|
||||
SP->>LS: checkAndRequestPermission()
|
||||
LS->>GPS: Demande permissions
|
||||
|
||||
alt Permissions OK
|
||||
GPS-->>LS: Granted
|
||||
LS-->>SP: true
|
||||
Note over SP: Progress: 10%
|
||||
else Permissions refusées
|
||||
GPS-->>LS: Denied
|
||||
LS-->>SP: false
|
||||
SP->>SP: _showLocationError = true
|
||||
Note over SP: ❌ ARRÊT de l'initialisation
|
||||
end
|
||||
end
|
||||
|
||||
SP->>HS: initializeAndResetHive()
|
||||
Note over SP: Progress: 15-60%
|
||||
|
||||
HS->>HS: _registerAdapters()
|
||||
Note over HS: Enregistrement 14 adaptateurs
|
||||
|
||||
HS->>HS: _destroyAllData()
|
||||
Note over HS: Fermeture boxes<br/>Suppression fichiers
|
||||
|
||||
HS->>HS: _createAllBoxes()
|
||||
Note over HS: Ouverture 14 boxes typées
|
||||
|
||||
HS-->>SP: ✅ Hive initialisé
|
||||
|
||||
SP->>HS: ensureBoxesAreOpen()
|
||||
Note over SP: Progress: 60-80%
|
||||
HS-->>SP: ✅ Toutes les boxes ouvertes
|
||||
|
||||
SP->>SP: _checkVersionAndCleanIfNeeded()
|
||||
Note over SP: Vérification app_version<br/>Nettoyage si nouvelle version
|
||||
|
||||
SP->>SP: Ouvrir pending_requests box
|
||||
Note over SP: Progress: 80%
|
||||
|
||||
SP->>HS: areAllBoxesOpen()
|
||||
HS-->>SP: true
|
||||
Note over SP: Progress: 95%
|
||||
|
||||
SP->>SP: Sauvegarder hive_initialized = true
|
||||
Note over SP: Progress: 100%
|
||||
|
||||
alt Paramètres URL fournis
|
||||
SP->>SP: _handleAutoRedirect()
|
||||
Note over SP: Redirection auto vers<br/>/login/user ou /login/admin
|
||||
else Pas de paramètres
|
||||
SP->>SP: Afficher boutons de choix
|
||||
Note over SP: User / Admin / Register
|
||||
end
|
||||
```
|
||||
|
||||
#### **Code : SplashPage._startInitialization() (lignes 325-501)**
|
||||
|
||||
```dart
|
||||
void _startInitialization() async {
|
||||
// Étape 1: Permissions GPS (Mobile uniquement) - 0 à 10%
|
||||
if (!kIsWeb) {
|
||||
final hasPermission = await LocationService.checkAndRequestPermission();
|
||||
if (!hasPermission) {
|
||||
setState(() {
|
||||
_showLocationError = true;
|
||||
_isInitializing = false;
|
||||
});
|
||||
return; // ❌ ARRÊT si permissions refusées
|
||||
}
|
||||
}
|
||||
|
||||
// Étape 2: Initialisation Hive complète - 15 à 60%
|
||||
await HiveService.instance.initializeAndResetHive();
|
||||
|
||||
// Étape 3: Ouverture des Box - 60 à 80%
|
||||
await HiveService.instance.ensureBoxesAreOpen();
|
||||
|
||||
// Étape 4: Vérification version + nettoyage auto - 80%
|
||||
await _checkVersionAndCleanIfNeeded();
|
||||
|
||||
// Étape 5: Box pending_requests - 80%
|
||||
await Hive.openBox(AppKeys.pendingRequestsBoxName);
|
||||
|
||||
// Étape 6: Vérification finale - 80 à 95%
|
||||
final allBoxesOpen = HiveService.instance.areAllBoxesOpen();
|
||||
|
||||
// Étape 7: Marquer initialisation terminée - 95 à 100%
|
||||
final settingsBox = Hive.box(AppKeys.settingsBoxName);
|
||||
await settingsBox.put('hive_initialized', true);
|
||||
await settingsBox.put('app_version', _appVersion);
|
||||
|
||||
// Redirection ou affichage boutons
|
||||
if (widget.action != null) {
|
||||
await _handleAutoRedirect();
|
||||
} else {
|
||||
setState(() => _showButtons = true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**🔑 Boxes créées (14 au total) :**
|
||||
|
||||
| Box Name | Type | Usage |
|
||||
|----------|------|-------|
|
||||
| `users` | UserModel | Utilisateur connecté |
|
||||
| `amicales` | AmicaleModel | Organisations |
|
||||
| `clients` | ClientModel | Clients distributions |
|
||||
| `operations` | OperationModel | Campagnes |
|
||||
| `sectors` | SectorModel | Secteurs géographiques |
|
||||
| `passages` | PassageModel | Distributions |
|
||||
| `membres` | MembreModel | Équipes membres |
|
||||
| `user_sector` | UserSectorModel | Affectations secteurs |
|
||||
| `chat_rooms` | Room | Salles de chat |
|
||||
| `chat_messages` | Message | Messages chat |
|
||||
| `pending_requests` | PendingRequest | File requêtes offline |
|
||||
| `temp_entities` | dynamic | Entités temporaires |
|
||||
| `settings` | dynamic | **Paramètres app** ⚠️ |
|
||||
| `regions` | dynamic | Régions |
|
||||
|
||||
**⚠️ Box critique : `settings`**
|
||||
- Contient `hive_initialized` (flag d'initialisation complète)
|
||||
- Contient `app_version` (détection changement de version)
|
||||
|
||||
---
|
||||
|
||||
### **3. Page de connexion : `LoginPage`**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant LP as LoginPage
|
||||
participant HS as HiveService
|
||||
participant S as Settings Box
|
||||
participant UR as UserRepository
|
||||
|
||||
LP->>LP: initState()
|
||||
|
||||
LP->>HS: areBoxesInitialized()
|
||||
HS->>HS: Vérifier boxes critiques:<br/>users, membres, settings
|
||||
|
||||
alt Boxes non initialisées
|
||||
HS-->>LP: false
|
||||
LP->>LP: Redirection: '/?action=login&type=admin'
|
||||
Note over LP: ❌ Retour SplashPage<br/>pour réinitialisation
|
||||
else Boxes initialisées
|
||||
HS-->>LP: true
|
||||
|
||||
LP->>S: get('hive_initialized')
|
||||
|
||||
alt hive_initialized != true
|
||||
S-->>LP: false
|
||||
LP->>LP: Redirection: '/?action=login&type=admin'
|
||||
Note over LP: ❌ Retour SplashPage<br/>pour réinitialisation complète
|
||||
else hive_initialized == true
|
||||
S-->>LP: true
|
||||
|
||||
LP->>LP: Continuer initialisation
|
||||
LP->>LP: Détecter loginType (user/admin)
|
||||
LP->>UR: getAllUsers()
|
||||
LP->>LP: Pré-remplir username si rôle correspond
|
||||
LP->>LP: Afficher formulaire de connexion
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
#### **Code : LoginPage.initState() (lignes 100-162)**
|
||||
|
||||
```dart
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// VÉRIFICATION 1 : Boxes critiques ouvertes ?
|
||||
if (!HiveService.instance.areBoxesInitialized()) {
|
||||
debugPrint('⚠️ Boxes Hive non initialisées, redirection vers SplashPage');
|
||||
|
||||
final loginType = widget.loginType ?? 'admin';
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
context.go('/?action=login&type=$loginType');
|
||||
}
|
||||
});
|
||||
|
||||
_loginType = '';
|
||||
return; // ❌ ARRÊT de initState
|
||||
}
|
||||
|
||||
// VÉRIFICATION 2 : Flag hive_initialized défini ?
|
||||
try {
|
||||
if (Hive.isBoxOpen(AppKeys.settingsBoxName)) {
|
||||
final settingsBox = Hive.box(AppKeys.settingsBoxName);
|
||||
final isInitialized = settingsBox.get('hive_initialized', defaultValue: false);
|
||||
|
||||
if (isInitialized != true) {
|
||||
debugPrint('⚠️ Réinitialisation Hive requise');
|
||||
|
||||
final loginType = widget.loginType ?? 'admin';
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
context.go('/?action=login&type=$loginType');
|
||||
}
|
||||
});
|
||||
|
||||
_loginType = '';
|
||||
return; // ❌ ARRÊT de initState
|
||||
}
|
||||
|
||||
debugPrint('✅ Hive correctement initialisé');
|
||||
}
|
||||
} catch (e) {
|
||||
// En cas d'erreur, forcer réinitialisation
|
||||
final loginType = widget.loginType ?? 'admin';
|
||||
context.go('/?action=login&type=$loginType');
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ Tout est OK : continuer initialisation normale
|
||||
_loginType = widget.loginType!;
|
||||
// ... pré-remplissage username, etc.
|
||||
}
|
||||
```
|
||||
|
||||
**🔑 Vérifications critiques :**
|
||||
1. **`areBoxesInitialized()`** : Vérifie `users`, `membres`, `settings`
|
||||
2. **`hive_initialized`** : Flag dans settings confirmant init complète
|
||||
3. **Redirection automatique** : Si échec → retour SplashPage avec params
|
||||
|
||||
---
|
||||
|
||||
## 🧹 Flow avec nettoyage du cache
|
||||
|
||||
### **Déclenchement manuel (Web uniquement)**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as Utilisateur
|
||||
participant SP as SplashPage
|
||||
participant Clean as _performSelectiveCleanup()
|
||||
participant SW as Service Worker (Web)
|
||||
participant H as Hive
|
||||
participant PR as pending_requests
|
||||
participant Settings as settings box
|
||||
|
||||
U->>SP: Clic "Nettoyer le cache"
|
||||
SP->>U: Dialog confirmation
|
||||
U->>SP: Confirme "Nettoyer"
|
||||
|
||||
SP->>Clean: _performSelectiveCleanup(manual: true)
|
||||
|
||||
Note over Clean: Progress: 10%
|
||||
|
||||
alt Sur Web (kIsWeb)
|
||||
Clean->>SW: Désenregistrer Service Workers
|
||||
Clean->>SW: Supprimer caches navigateur
|
||||
SW-->>Clean: ✅ Caches web nettoyés
|
||||
end
|
||||
|
||||
Note over Clean: Progress: 30%
|
||||
|
||||
Clean->>PR: Sauvegarder en mémoire
|
||||
PR-->>Clean: List<dynamic> pendingRequests
|
||||
|
||||
Clean->>Settings: Sauvegarder app_version en mémoire
|
||||
Settings-->>Clean: String savedAppVersion
|
||||
|
||||
Clean->>PR: Fermer box
|
||||
Clean->>Settings: Fermer box
|
||||
|
||||
Note over Clean: Progress: 50%
|
||||
|
||||
Clean->>H: Fermer toutes les boxes
|
||||
loop Pour chaque box (11 boxes)
|
||||
Clean->>H: close() + deleteBoxFromDisk()
|
||||
end
|
||||
|
||||
Note over Clean: ⚠️ Boxes supprimées:<br/>users, operations, passages,<br/>sectors, membres, amicale,<br/>clients, user_sector,<br/>chatRooms, chatMessages,<br/>settings
|
||||
|
||||
Note over Clean: ✅ Boxes préservées:<br/>pending_requests
|
||||
|
||||
Note over Clean: Progress: 70%
|
||||
|
||||
Clean->>H: Hive.close()
|
||||
Clean->>H: Future.delayed(500ms)
|
||||
Clean->>H: Hive.initFlutter()
|
||||
|
||||
Note over Clean: Progress: 80%
|
||||
|
||||
Clean->>PR: Restaurer pending_requests
|
||||
loop Pour chaque requête
|
||||
Clean->>PR: add(request)
|
||||
end
|
||||
|
||||
Clean->>Settings: Restaurer app_version
|
||||
Clean->>Settings: put('app_version', savedAppVersion)
|
||||
|
||||
Note over Clean: Progress: 100%
|
||||
|
||||
Clean-->>SP: ✅ Nettoyage terminé
|
||||
|
||||
SP->>SP: _startInitialization()
|
||||
Note over SP: Redémarrage complet<br/>de l'application
|
||||
```
|
||||
|
||||
#### **Code : SplashPage._performSelectiveCleanup() (lignes 84-243)**
|
||||
|
||||
```dart
|
||||
Future<void> _performSelectiveCleanup({bool manual = false}) async {
|
||||
debugPrint('🧹 === DÉBUT DU NETTOYAGE DU CACHE === 🧹');
|
||||
|
||||
try {
|
||||
// Étape 1: Service Worker (Web uniquement) - 10%
|
||||
if (kIsWeb) {
|
||||
final registrations = await html.window.navigator.serviceWorker?.getRegistrations();
|
||||
for (final registration in registrations) {
|
||||
await registration.unregister();
|
||||
}
|
||||
|
||||
final cacheNames = await html.window.caches!.keys();
|
||||
for (final cacheName in cacheNames) {
|
||||
await html.window.caches!.delete(cacheName);
|
||||
}
|
||||
}
|
||||
|
||||
// Étape 2: Sauvegarder pending_requests + app_version - 30%
|
||||
List<dynamic>? pendingRequests;
|
||||
String? savedAppVersion;
|
||||
|
||||
if (Hive.isBoxOpen(AppKeys.pendingRequestsBoxName)) {
|
||||
final pendingBox = Hive.box(AppKeys.pendingRequestsBoxName);
|
||||
pendingRequests = pendingBox.values.toList();
|
||||
await pendingBox.close();
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(AppKeys.settingsBoxName)) {
|
||||
final settingsBox = Hive.box(AppKeys.settingsBoxName);
|
||||
savedAppVersion = settingsBox.get('app_version') as String?;
|
||||
}
|
||||
|
||||
// Étape 3: Lister boxes à nettoyer - 50%
|
||||
final boxesToClean = [
|
||||
AppKeys.userBoxName,
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.membresBoxName,
|
||||
AppKeys.amicaleBoxName,
|
||||
AppKeys.clientsBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.settingsBoxName, // ⚠️ Supprimée (mais version sauvegardée)
|
||||
AppKeys.chatRoomsBoxName,
|
||||
AppKeys.chatMessagesBoxName,
|
||||
];
|
||||
|
||||
// Étape 4: Supprimer les boxes - 50%
|
||||
for (final boxName in boxesToClean) {
|
||||
if (Hive.isBoxOpen(boxName)) {
|
||||
await Hive.box(boxName).close();
|
||||
}
|
||||
await Hive.deleteBoxFromDisk(boxName);
|
||||
}
|
||||
|
||||
// Étape 5: Réinitialiser Hive - 70%
|
||||
await Hive.close();
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
await Hive.initFlutter();
|
||||
|
||||
// Étape 6: Restaurer données critiques - 80-100%
|
||||
if (pendingRequests != null && pendingRequests.isNotEmpty) {
|
||||
final pendingBox = await Hive.openBox(AppKeys.pendingRequestsBoxName);
|
||||
for (final request in pendingRequests) {
|
||||
await pendingBox.add(request);
|
||||
}
|
||||
}
|
||||
|
||||
if (savedAppVersion != null) {
|
||||
final settingsBox = await Hive.openBox(AppKeys.settingsBoxName);
|
||||
await settingsBox.put('app_version', savedAppVersion);
|
||||
}
|
||||
|
||||
debugPrint('🎉 === NETTOYAGE TERMINÉ AVEC SUCCÈS === 🎉');
|
||||
|
||||
} catch (e) {
|
||||
debugPrint('❌ ERREUR CRITIQUE lors du nettoyage: $e');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Nettoyage automatique sur changement de version**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant SP as SplashPage
|
||||
participant S as Settings Box
|
||||
participant Check as _checkVersionAndCleanIfNeeded()
|
||||
participant Clean as _performSelectiveCleanup()
|
||||
|
||||
SP->>SP: _startInitialization()
|
||||
SP->>S: Boxes ouvertes
|
||||
|
||||
SP->>Check: _checkVersionAndCleanIfNeeded()
|
||||
|
||||
Check->>S: get('app_version')
|
||||
S-->>Check: lastVersion = "3.2.3"
|
||||
|
||||
Check->>Check: currentVersion = "3.2.4"
|
||||
|
||||
alt Version changée
|
||||
Check->>Check: lastVersion != currentVersion
|
||||
Note over Check: 🆕 NOUVELLE VERSION DÉTECTÉE
|
||||
|
||||
Check->>Clean: _performSelectiveCleanup(manual: false)
|
||||
Clean-->>Check: ✅ Nettoyage auto terminé
|
||||
|
||||
Check->>S: put('app_version', '3.2.4')
|
||||
S-->>Check: ✅ Version mise à jour
|
||||
|
||||
else Même version
|
||||
Check->>Check: lastVersion == currentVersion
|
||||
Note over Check: ✅ Pas de nettoyage nécessaire
|
||||
end
|
||||
|
||||
Check-->>SP: Terminé
|
||||
```
|
||||
|
||||
**🔑 Cas d'usage :**
|
||||
- **Déploiement nouvelle version web** : Cache automatiquement nettoyé
|
||||
- **Update version mobile** : Détection et nettoyage auto
|
||||
- **Préserve** : `pending_requests` (requêtes offline) + `app_version`
|
||||
|
||||
---
|
||||
|
||||
## 📦 Gestion des Hive Box
|
||||
|
||||
### **HiveService : Architecture complète**
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[HiveService Singleton] --> B[Initialisation]
|
||||
A --> C[Nettoyage]
|
||||
A --> D[Utilitaires]
|
||||
|
||||
B --> B1[initializeAndResetHive]
|
||||
B --> B2[ensureBoxesAreOpen]
|
||||
|
||||
B1 --> B1a[_registerAdapters]
|
||||
B1 --> B1b[_destroyAllData]
|
||||
B1 --> B1c[_createAllBoxes]
|
||||
|
||||
B1b --> B1b1[_destroyDataWeb]
|
||||
B1b --> B1b2[_destroyDataIOS]
|
||||
B1b --> B1b3[_destroyDataAndroid]
|
||||
B1b --> B1b4[_destroyDataDesktop]
|
||||
|
||||
C --> C1[cleanDataOnLogout]
|
||||
C --> C2[_clearSingleBox]
|
||||
|
||||
D --> D1[areBoxesInitialized]
|
||||
D --> D2[areAllBoxesOpen]
|
||||
D --> D3[getDiagnostic]
|
||||
|
||||
style A fill:#e1f5ff
|
||||
style B fill:#fff4e1
|
||||
style C fill:#ffe1e1
|
||||
style D fill:#e8f5e9
|
||||
```
|
||||
|
||||
### **Méthodes critiques**
|
||||
|
||||
#### **1. `initializeAndResetHive()` - Initialisation complète**
|
||||
|
||||
**Appelée par** : `SplashPage._startInitialization()`
|
||||
|
||||
```dart
|
||||
Future<void> initializeAndResetHive() async {
|
||||
// 1. Initialisation de base
|
||||
await Hive.initFlutter();
|
||||
|
||||
// 2. Enregistrement adaptateurs (14 types)
|
||||
_registerAdapters();
|
||||
|
||||
// 3. Destruction complète des anciennes données
|
||||
await _destroyAllData();
|
||||
|
||||
// 4. Création de toutes les Box vides et propres
|
||||
await _createAllBoxes();
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ Comportement destructif** :
|
||||
- Supprime TOUTES les boxes existantes
|
||||
- Préserve `pending_requests` si elle contient des données
|
||||
- Recrée des boxes vierges
|
||||
|
||||
---
|
||||
|
||||
#### **2. `areBoxesInitialized()` - Vérification rapide**
|
||||
|
||||
**Appelée par** : `LoginPage.initState()`
|
||||
|
||||
```dart
|
||||
bool areBoxesInitialized() {
|
||||
// Vérifier seulement les boxes critiques
|
||||
final criticalBoxes = [
|
||||
AppKeys.userBoxName, // getCurrentUser
|
||||
AppKeys.membresBoxName, // Pré-remplissage
|
||||
AppKeys.settingsBoxName, // Préférences
|
||||
];
|
||||
|
||||
for (final boxName in criticalBoxes) {
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isInitialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**🔑 Boxes critiques vérifiées** :
|
||||
- ✅ `users` : Nécessaire pour `getCurrentUser()`
|
||||
- ✅ `membres` : Nécessaire pour pré-remplissage username
|
||||
- ✅ `settings` : Contient `hive_initialized` et `app_version`
|
||||
|
||||
---
|
||||
|
||||
#### **3. `cleanDataOnLogout()` - Nettoyage logout**
|
||||
|
||||
**Appelée par** : `LoginPage` (bouton "Nettoyer le cache")
|
||||
|
||||
```dart
|
||||
Future<void> cleanDataOnLogout() async {
|
||||
// Nettoyer toutes les Box SAUF users
|
||||
for (final config in _boxConfigs) {
|
||||
if (config.name != AppKeys.userBoxName) {
|
||||
await _clearSingleBox(config.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ Préserve** : Box `users` (pour pré-remplissage username au prochain login)
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Vérifications et redirections
|
||||
|
||||
### **Système de redirections automatiques**
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Start[Application démarre] --> Main[main.dart]
|
||||
Main --> Splash[SplashPage]
|
||||
|
||||
Splash --> GPS{Permissions GPS?<br/>Mobile uniquement}
|
||||
|
||||
GPS -->|Refusées| ShowError[Afficher erreur GPS<br/>+ Boutons Réessayer/Paramètres]
|
||||
ShowError --> End1[❌ Arrêt initialisation]
|
||||
|
||||
GPS -->|OK ou Web| InitHive[Initialisation Hive complète]
|
||||
|
||||
InitHive --> CheckVersion{Changement version?<br/>Web uniquement}
|
||||
|
||||
CheckVersion -->|Oui| CleanCache[Nettoyage auto du cache]
|
||||
CleanCache --> OpenBoxes[Ouverture boxes]
|
||||
|
||||
CheckVersion -->|Non| OpenBoxes
|
||||
|
||||
OpenBoxes --> AllOpen{Toutes boxes<br/>ouvertes?}
|
||||
|
||||
AllOpen -->|Non| ErrorInit[❌ Erreur initialisation]
|
||||
ErrorInit --> End2[Afficher message d'erreur]
|
||||
|
||||
AllOpen -->|Oui| SaveFlag[settings.put<br/>'hive_initialized' = true]
|
||||
|
||||
SaveFlag --> URLParams{Paramètres URL<br/>fournis?}
|
||||
|
||||
URLParams -->|Oui| AutoRedirect[Redirection auto<br/>/login/user ou /login/admin]
|
||||
URLParams -->|Non| ShowButtons[Afficher boutons choix]
|
||||
|
||||
AutoRedirect --> Login[LoginPage]
|
||||
ShowButtons --> UserClick{Utilisateur clique}
|
||||
UserClick --> Login
|
||||
|
||||
Login --> CheckBoxes{Boxes initialisées?}
|
||||
|
||||
CheckBoxes -->|Non| BackSplash[Redirection<br/>'/?action=login&type=X']
|
||||
BackSplash --> Splash
|
||||
|
||||
CheckBoxes -->|Oui| CheckFlag{hive_initialized<br/>== true?}
|
||||
|
||||
CheckFlag -->|Non| BackSplash
|
||||
CheckFlag -->|Oui| ShowForm[✅ Afficher formulaire]
|
||||
|
||||
ShowForm --> UserLogin[Utilisateur se connecte]
|
||||
UserLogin --> Dashboard[UserPage / AdminPage]
|
||||
|
||||
style Start fill:#e1f5ff
|
||||
style Splash fill:#fff4e1
|
||||
style Login fill:#e8f5e9
|
||||
style Dashboard fill:#f3e5f5
|
||||
style ShowError fill:#ffe1e1
|
||||
style ErrorInit fill:#ffe1e1
|
||||
```
|
||||
|
||||
### **Tableau des redirections**
|
||||
|
||||
| Condition | Action | Paramètres URL |
|
||||
|-----------|--------|----------------|
|
||||
| **Boxes non initialisées** | Redirect → SplashPage | `/?action=login&type=admin` |
|
||||
| **`hive_initialized` != true** | Redirect → SplashPage | `/?action=login&type=user` |
|
||||
| **Permissions GPS refusées** | Afficher erreur | Aucune redirection |
|
||||
| **Changement version (Web)** | Nettoyage auto | Transparent |
|
||||
| **Nettoyage manuel** | Réinitialisation complète | Vers `/` après nettoyage |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Points critiques
|
||||
|
||||
### **1. Box `settings` - Données essentielles**
|
||||
|
||||
**Contenu** :
|
||||
- `hive_initialized` (bool) : Flag confirmant initialisation complète
|
||||
- `app_version` (String) : Version actuelle pour détection changements
|
||||
- Autres paramètres utilisateur
|
||||
|
||||
**⚠️ Importance** :
|
||||
- Si `settings` est supprimée sans sauvegarde → perte de la version
|
||||
- Si `hive_initialized` est absent → boucle de réinitialisation
|
||||
|
||||
**✅ Solution actuelle** :
|
||||
- Nettoyage du cache : sauvegarde `app_version` en mémoire avant suppression
|
||||
- Restauration automatique après réinitialisation Hive
|
||||
|
||||
---
|
||||
|
||||
### **2. Box `pending_requests` - Requêtes offline**
|
||||
|
||||
**Contenu** :
|
||||
- File d'attente des requêtes API en mode hors ligne
|
||||
- Modèle : `PendingRequest`
|
||||
|
||||
**⚠️ Protection** :
|
||||
- JAMAIS supprimée pendant nettoyage si elle contient des données
|
||||
- Sauvegardée en mémoire pendant `_performSelectiveCleanup()`
|
||||
- Restaurée après réinitialisation
|
||||
|
||||
**Code protection** :
|
||||
```dart
|
||||
// Dans _performSelectiveCleanup()
|
||||
if (Hive.isBoxOpen(AppKeys.pendingRequestsBoxName)) {
|
||||
final pendingBox = Hive.box(AppKeys.pendingRequestsBoxName);
|
||||
pendingRequests = pendingBox.values.toList(); // Sauvegarde
|
||||
await pendingBox.close();
|
||||
}
|
||||
|
||||
// ... nettoyage des autres boxes ...
|
||||
|
||||
// Restauration
|
||||
if (pendingRequests != null && pendingRequests.isNotEmpty) {
|
||||
final pendingBox = await Hive.openBox(AppKeys.pendingRequestsBoxName);
|
||||
for (final request in pendingRequests) {
|
||||
await pendingBox.add(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **3. Permissions GPS (Mobile uniquement)**
|
||||
|
||||
**Vérification obligatoire** :
|
||||
- Sur mobile : `LocationService.checkAndRequestPermission()`
|
||||
- Si refusées : affichage erreur + arrêt initialisation
|
||||
- Sur web : vérification ignorée
|
||||
|
||||
**Messages contextuels** :
|
||||
```dart
|
||||
final errorMessage = await LocationService.getLocationErrorMessage();
|
||||
|
||||
// Exemples de messages :
|
||||
// - "Permissions refusées temporairement"
|
||||
// - "Permissions refusées définitivement - ouvrir Paramètres"
|
||||
// - "Service de localisation désactivé"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **4. Bouton "Nettoyer le cache" (Web uniquement)**
|
||||
|
||||
**Restriction plateforme** :
|
||||
```dart
|
||||
// Dans splash_page.dart (ligne 932)
|
||||
if (kIsWeb)
|
||||
AnimatedOpacity(
|
||||
child: TextButton.icon(
|
||||
label: Text('Nettoyer le cache'),
|
||||
// ...
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
**Fonctionnalités Web spécifiques** :
|
||||
- Désenregistrement Service Workers
|
||||
- Suppression caches navigateur (`window.caches`)
|
||||
- Nettoyage localStorage (via Service Worker)
|
||||
|
||||
**⚠️ Sur mobile** : Utilise `HiveService.cleanDataOnLogout()` (dans LoginPage)
|
||||
|
||||
---
|
||||
|
||||
### **5. Détection automatique d'environnement**
|
||||
|
||||
**ApiService** :
|
||||
```dart
|
||||
// Détection basée sur l'URL
|
||||
if (currentUrl.contains('dapp.geosector.fr')) → DEV
|
||||
if (currentUrl.contains('rapp.geosector.fr')) → REC
|
||||
Sinon → PROD
|
||||
```
|
||||
|
||||
**Impact sur le nettoyage** :
|
||||
- Web DEV/REC : nettoyage auto sur changement version
|
||||
- Web PROD : nettoyage auto sur changement version
|
||||
- Mobile : pas de nettoyage auto (version gérée par stores)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Récapitulatif des états
|
||||
|
||||
### **États de l'application**
|
||||
|
||||
| État | Description | Boxes Hive | Flag `hive_initialized` |
|
||||
|------|-------------|-----------|------------------------|
|
||||
| **Démarrage initial** | Premier lancement | Vides | ❌ Absent |
|
||||
| **Initialisé** | SplashPage terminé | Ouvertes et vides | ✅ `true` |
|
||||
| **Connecté** | Utilisateur loggé | Remplies avec données API | ✅ `true` |
|
||||
| **Après nettoyage** | Cache vidé | Réinitialisées | ✅ `true` (restauré) |
|
||||
| **Erreur init** | Échec initialisation | Partielles ou fermées | ❌ Absent ou `false` |
|
||||
|
||||
### **Chemins possibles**
|
||||
|
||||
```
|
||||
main.dart
|
||||
↓
|
||||
SplashPage (initialisation)
|
||||
↓
|
||||
[Web] Vérification version → Nettoyage auto si besoin
|
||||
↓
|
||||
[Mobile] Vérification GPS → Erreur si refusé
|
||||
↓
|
||||
Ouverture 14 boxes Hive
|
||||
↓
|
||||
settings.put('hive_initialized', true)
|
||||
↓
|
||||
LoginPage
|
||||
↓
|
||||
Vérification boxes + hive_initialized
|
||||
↓
|
||||
[OK] Afficher formulaire
|
||||
[KO] Redirection SplashPage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Conclusion
|
||||
|
||||
Le système de démarrage GEOSECTOR v3.2.4 implémente une architecture robuste en **3 étapes** avec des **vérifications multiples** et une **gestion intelligente du cache**.
|
||||
|
||||
**Points forts** :
|
||||
- ✅ Initialisation progressive avec feedback visuel (barre de progression)
|
||||
- ✅ Protection des données critiques (`pending_requests`, `app_version`)
|
||||
- ✅ Détection automatique des problèmes (boxes non ouvertes, version changée)
|
||||
- ✅ Redirections automatiques pour forcer réinitialisation si nécessaire
|
||||
- ✅ Nettoyage sélectif du cache (Web uniquement)
|
||||
|
||||
**Sécurités** :
|
||||
- ⚠️ Vérification permissions GPS (mobile obligatoire)
|
||||
- ⚠️ Double vérification Hive (boxes + flag `hive_initialized`)
|
||||
- ⚠️ Sauvegarde mémoire avant nettoyage (`pending_requests`, `app_version`)
|
||||
- ⚠️ Restriction plateforme (bouton cache Web uniquement)
|
||||
|
||||
---
|
||||
|
||||
**Document généré le** : 04 octobre 2025
|
||||
**Version application** : v3.2.4
|
||||
**Auteur** : Documentation technique GEOSECTOR
|
||||
Reference in New Issue
Block a user