feat: Release version 3.1.4 - Mode terrain et génération PDF

 Nouvelles fonctionnalités:
- Ajout du mode terrain pour utilisation mobile hors connexion
- Génération automatique de reçus PDF avec template personnalisé
- Révision complète du système de cartes avec amélioration des performances

🔧 Améliorations techniques:
- Refactoring du module chat avec architecture simplifiée
- Optimisation du système de sécurité NIST SP 800-63B
- Amélioration de la gestion des secteurs géographiques
- Support UTF-8 étendu pour les noms d'utilisateurs

📱 Application mobile:
- Nouveau mode terrain dans user_field_mode_page
- Interface utilisateur adaptative pour conditions difficiles
- Synchronisation offline améliorée

🗺️ Cartographie:
- Optimisation des performances MapBox
- Meilleure gestion des tuiles hors ligne
- Amélioration de l'affichage des secteurs

📄 Documentation:
- Ajout guide Android (ANDROID-GUIDE.md)
- Documentation sécurité API (API-SECURITY.md)
- Guide module chat (CHAT_MODULE.md)

🐛 Corrections:
- Résolution des erreurs 400 lors de la création d'utilisateurs
- Correction de la validation des noms d'utilisateurs
- Fix des problèmes de synchronisation chat

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-19 19:38:03 +02:00
parent c1f23c4345
commit 5ab03751e1
1823 changed files with 272663 additions and 198438 deletions

View File

@@ -17,7 +17,7 @@ class AppKeys {
static const String settingsBoxName = 'settings';
static const String membresBoxName = 'membres';
static const String userSectorBoxName = 'user_sector';
static const String chatConversationsBoxName = 'chat_conversations';
static const String chatRoomsBoxName = 'chat_rooms';
static const String chatMessagesBoxName = 'chat_messages';
static const String regionsBoxName = 'regions';

View File

@@ -17,6 +17,7 @@ import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/presentation/widgets/loading_spin_overlay.dart';
import 'package:geosector_app/core/models/loading_state.dart';
import 'package:geosector_app/chat/services/chat_info_service.dart';
class UserRepository extends ChangeNotifier {
bool _isLoading = false;
@@ -276,7 +277,17 @@ class UserRepository extends ChangeNotifier {
}
}
// Étape 5: Traitement de toutes les autres données via DataLoadingService
// Étape 5: Traitement des infos chat
if (apiResult['chat'] != null) {
try {
ChatInfoService.instance.updateFromLogin(apiResult);
debugPrint('💬 Infos chat mises à jour');
} catch (chatError) {
debugPrint('⚠️ Erreur traitement infos chat: $chatError');
}
}
// Étape 6: Traitement de toutes les autres données via DataLoadingService
try {
await DataLoadingService.instance.processLoginData(apiResult);
} catch (processingError) {
@@ -316,6 +327,9 @@ class UserRepository extends ChangeNotifier {
// Effacer les données via les services singleton
await CurrentUserService.instance.clearUser();
await CurrentAmicaleService.instance.clearAmicale();
// Réinitialiser les infos chat
ChatInfoService.instance.reset();
// Nettoyage des données via HiveService (préserve les utilisateurs)
await HiveService.instance.cleanDataOnLogout();

View File

@@ -65,6 +65,14 @@ class ConnectivityService extends ChangeNotifier {
/// Constructeur du service de connectivité
ConnectivityService() {
// Initialiser avec une connexion par défaut (supposée disponible)
// Ceci sera mis à jour rapidement par _initConnectivity()
if (kIsWeb) {
_connectionStatus = [ConnectivityResult.wifi];
} else {
// Sur mobile, on suppose qu'il y a au moins une connexion jusqu'à vérification
_connectionStatus = [ConnectivityResult.wifi];
}
_initConnectivity();
}

View File

@@ -11,8 +11,7 @@ import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/data/models/user_sector_model.dart';
import 'package:geosector_app/core/data/models/amicale_model.dart';
import 'package:geosector_app/core/repositories/client_repository.dart';
import 'package:geosector_app/chat/models/conversation_model.dart';
import 'package:geosector_app/chat/models/message_model.dart';
// Chat imports removed - using new simplified chat module
import 'package:geosector_app/core/models/loading_state.dart';
/// Service singleton pour gérer le chargement et la gestion des données au login
@@ -54,10 +53,7 @@ class DataLoadingService extends ChangeNotifier {
Hive.box<UserSectorModel>(AppKeys.userSectorBoxName);
Box<AmicaleModel> get _amicaleBox =>
Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
Box<ConversationModel> get _chatConversationBox =>
Hive.box<ConversationModel>(AppKeys.chatConversationsBoxName);
Box<MessageModel> get _chatMessageBox =>
Hive.box<MessageModel>(AppKeys.chatMessagesBoxName);
// Chat boxes removed - handled by new chat module
Box get _settingsBox => Hive.box(AppKeys.settingsBoxName);
/// Traite toutes les données reçues de l'API lors du login
@@ -569,7 +565,7 @@ class DataLoadingService extends ChangeNotifier {
AppKeys.membresBoxName,
AppKeys.userSectorBoxName,
AppKeys.amicaleBoxName,
AppKeys.chatConversationsBoxName,
AppKeys.chatRoomsBoxName,
AppKeys.chatMessagesBoxName,
AppKeys.settingsBoxName,
];

View File

@@ -8,7 +8,7 @@ import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/data/models/user_sector_model.dart';
import 'package:geosector_app/core/data/models/region_model.dart';
import 'package:geosector_app/chat/models/chat_adapters.dart';
// Chat adapters removed - handled by new chat module
class HiveAdapters {
/// Enregistre tous les TypeAdapters nécessaires
@@ -42,24 +42,7 @@ class HiveAdapters {
Hive.registerAdapter(AmicaleModelAdapter());
}
// Chat adapters
if (!Hive.isAdapterRegistered(20)) {
Hive.registerAdapter(ConversationModelAdapter());
}
if (!Hive.isAdapterRegistered(21)) {
Hive.registerAdapter(MessageModelAdapter());
}
if (!Hive.isAdapterRegistered(22)) {
Hive.registerAdapter(ParticipantModelAdapter());
}
if (!Hive.isAdapterRegistered(23)) {
Hive.registerAdapter(AnonymousUserModelAdapter());
}
if (!Hive.isAdapterRegistered(24)) {
Hive.registerAdapter(AudienceTargetModelAdapter());
}
if (!Hive.isAdapterRegistered(25)) {
Hive.registerAdapter(NotificationSettingsAdapter());
}
// Chat adapters are now handled by the chat module itself
// TypeIds 50-60 are reserved for chat module
}
}

View File

@@ -13,7 +13,8 @@ import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/data/models/user_sector_model.dart';
import 'package:geosector_app/core/data/models/region_model.dart';
import 'package:geosector_app/chat/models/chat_adapters.dart';
import 'package:geosector_app/chat/models/room.dart';
import 'package:geosector_app/chat/models/message.dart';
/// Service pour réinitialiser et recréer les Hive Boxes
/// Utilisé pour résoudre les problèmes d'incompatibilité après mise à jour des modèles
@@ -91,12 +92,8 @@ class HiveResetService {
Hive.registerAdapter(UserSectorModelAdapter());
// Enregistrer les adaptateurs pour le chat
Hive.registerAdapter(ConversationModelAdapter());
Hive.registerAdapter(MessageModelAdapter());
Hive.registerAdapter(ParticipantModelAdapter());
Hive.registerAdapter(AnonymousUserModelAdapter());
Hive.registerAdapter(AudienceTargetModelAdapter());
Hive.registerAdapter(NotificationSettingsAdapter());
Hive.registerAdapter(RoomAdapter());
Hive.registerAdapter(MessageAdapter());
// Vérifier si RegionModelAdapter est disponible
try {
@@ -117,7 +114,7 @@ class HiveResetService {
await Hive.openBox(AppKeys.settingsBoxName);
// Ouvrir les boîtes de chat
await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
await Hive.openBox<Room>(AppKeys.chatRoomsBoxName);
await Hive.openBox<Message>(AppKeys.chatMessagesBoxName);
}
}

View File

@@ -15,8 +15,7 @@ import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/data/models/user_sector_model.dart';
import 'package:geosector_app/chat/models/conversation_model.dart';
import 'package:geosector_app/chat/models/message_model.dart';
// Chat imports removed - using new simplified chat module
/// Service singleton centralisé pour la gestion complète des Box Hive
/// Utilisé par main.dart pour l'initialisation et par logout pour le nettoyage
@@ -35,8 +34,7 @@ class HiveService {
HiveBoxConfig<PassageModel>(AppKeys.passagesBoxName, 'PassageModel'),
HiveBoxConfig<MembreModel>(AppKeys.membresBoxName, 'MembreModel'),
HiveBoxConfig<UserSectorModel>(AppKeys.userSectorBoxName, 'UserSectorModel'),
HiveBoxConfig<ConversationModel>(AppKeys.chatConversationsBoxName, 'ConversationModel'),
HiveBoxConfig<MessageModel>(AppKeys.chatMessagesBoxName, 'MessageModel'),
// Chat boxes removed - handled by new chat module
HiveBoxConfig<dynamic>(AppKeys.settingsBoxName, 'Settings'),
HiveBoxConfig<dynamic>(AppKeys.regionsBoxName, 'Regions'),
];
@@ -403,12 +401,7 @@ class HiveService {
case 'UserSectorModel':
await Hive.openBox<UserSectorModel>(config.name);
break;
case 'ConversationModel':
await Hive.openBox<ConversationModel>(config.name);
break;
case 'MessageModel':
await Hive.openBox<MessageModel>(config.name);
break;
// Chat boxes removed - handled by new chat module
default:
// Pour Settings, Regions, etc.
await Hive.openBox(config.name);
@@ -455,6 +448,37 @@ class HiveService {
return true;
}
/// Vérification rapide que les boxes critiques sont initialisées
/// Utilisé par LoginPage pour détecter si une redirection vers SplashPage est nécessaire
bool areBoxesInitialized() {
try {
// Vérifier seulement les boxes critiques pour le login
final criticalBoxes = [
AppKeys.userBoxName, // Nécessaire pour getCurrentUser
AppKeys.membresBoxName, // Nécessaire pour le pré-remplissage
AppKeys.settingsBoxName, // Nécessaire pour les préférences
];
for (final boxName in criticalBoxes) {
if (!Hive.isBoxOpen(boxName)) {
debugPrint('⚠️ Box critique non ouverte: $boxName');
return false;
}
}
// Vérifier aussi le flag d'initialisation
if (!_isInitialized) {
debugPrint('⚠️ HiveService non initialisé');
return false;
}
return true;
} catch (e) {
debugPrint('❌ Erreur vérification boxes: $e');
return false;
}
}
/// Récupération sécurisée d'une Box typée
Box<T> getTypedBox<T>(String boxName) {
if (!Hive.isBoxOpen(boxName)) {