# Module de Chat pour Geosector ## Vue d'ensemble Module de chat intégré pour l'application Geosector permettant la communication entre membres, administrateurs d'amicale et super-administrateurs, avec synchronisation temps réel et support hors ligne. ## Architecture actuelle ### Gestion du cycle de vie avec ChatManager Le module chat utilise un service singleton `ChatManager` pour gérer son cycle de vie : #### Initialisation au login ```dart // Dans UserRepository.login() await ChatManager.instance.initializeChat(); ``` #### Arrêt au logout ```dart // Dans UserRepository.logout() ChatManager.instance.dispose(); ``` #### Gestion complète du cycle de vie de l'application ```dart // Dans GeosectorApp avec WidgetsBindingObserver void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.resumed: // App au premier plan - reprendre les syncs ChatManager.instance.resumeSyncs(); break; case AppLifecycleState.paused: // App en arrière-plan - pause pour économiser batterie ChatManager.instance.pauseSyncs(); break; case AppLifecycleState.inactive: // État transitoire (appel entrant) - ne rien faire break; case AppLifecycleState.detached: // App vraiment fermée (rare sur mobile) - nettoyer ChatManager.instance.dispose(); break; case AppLifecycleState.hidden: // App masquée (Flutter 3.13+) - pause comme paused ChatManager.instance.pauseSyncs(); break; } } ``` #### Points clés du ChatManager - **Singleton** : Une seule instance pour toute l'application - **Initialisation unique** : Méthode idempotente, peut être appelée plusieurs fois sans effet - **Syncs en arrière-plan** : Timer de 15 secondes actif uniquement quand l'app est au premier plan - **Gestion intelligente** : Vérifie la connexion utilisateur avant chaque utilisation - **Économie de batterie** : Pause automatique des syncs quand l'app passe en arrière-plan - **Sync de rattrapage** : Synchronisation immédiate au retour au premier plan - **Nettoyage automatique** : Arrêt propre des timers et libération des ressources #### Comportement par état d'application | État | Action | Raison | |------|--------|--------| | **resumed** | `resumeSyncs()` | App au premier plan, syncs actives + sync de rattrapage | | **paused** | `pauseSyncs()` | App en arrière-plan, économie de batterie | | **inactive** | Rien | État transitoire (ex: appel entrant sur iOS) | | **detached** | `dispose()` | App vraiment fermée (très rare sur mobile) | | **hidden** | `pauseSyncs()` | App masquée (Flutter 3.13+), traité comme paused | **Note importante** : Sur mobile, "fermer" l'app (swipe) la met généralement en `paused`, pas en `detached`. Le chat arrête donc ses syncs pour économiser la batterie mais reste initialisé pour un retour rapide. ### 1. Structure des modèles #### Room (Conversation) ```dart @HiveType(typeId: 50) class Room { String id; // UUID de la conversation String title; // Nom du destinataire ou titre du groupe String type; // 'private', 'group', 'broadcast' DateTime createdAt; // Date de création DateTime? updatedAt; // Date de dernière modification String? lastMessage; // Dernier message (preview) DateTime? lastMessageAt; // Date du dernier message int unreadCount; // Nombre de messages non lus List>? recentMessages; // 5 derniers messages (cache) } ``` #### Message ```dart @HiveType(typeId: 51) class Message { String id; // UUID du message String roomId; // ID de la conversation String content; // Contenu du message int senderId; // ID de l'expéditeur String senderName; // Nom de l'expéditeur String? senderFirstName; // Prénom de l'expéditeur DateTime sentAt; // Date d'envoi bool isMe; // Message de l'utilisateur actuel bool isRead; // Message lu/non lu int? readCount; // Nombre de personnes ayant lu } ``` ### 2. Endpoints API implémentés #### Synchronisation initiale (au login) ```http GET /api/chat/rooms Réponse: { "status": "success", "sync_timestamp": "2025-08-25T11:45:00Z", "rooms": [ { "id": "c6a4eaef-8efe-49c6-b896-11b4ca1a8056", "title": "VAISSAIRE", "type": "private", "created_at": "2025-08-25 11:43:32", "updated_at": "2025-08-25 11:44:15", "last_message": "Salut, ça va ?", "last_message_at": "2025-08-25 11:44:15", "unread_count": 10, "participant_count": 2, "participants": [...], "recent_messages": [ { "id": "msg-123", "content": "Salut Pierre !", "sender_id": 9999985, "sender_name": "VAISSAIRE", "sender_first_name": "Clément", "sent_at": "2025-08-25 11:44:00", "is_read": false, "is_mine": false } ] } ] } ``` #### Synchronisation incrémentale (toutes les 15 secondes) ```http GET /api/chat/rooms?updated_after=2025-08-25T11:45:00Z Réponse si changements: { "status": "success", "sync_timestamp": "2025-08-25T11:45:15Z", "has_changes": true, "rooms": [ // Seulement les rooms modifiées ] } Réponse si aucun changement: { "status": "success", "sync_timestamp": "2025-08-25T11:45:15Z", "has_changes": false, "rooms": [] } ``` #### Chargement des messages (marque automatiquement comme lu) ```http GET /api/chat/rooms/{room_id}/messages?limit=50 Réponse: { "status": "success", "messages": [...], "has_more": true, "marked_as_read": 10, // Messages marqués comme lus automatiquement "unread_count": 0 // Messages non lus restants } ``` #### Création de conversation ```http POST /api/chat/rooms { "title": "Nom du destinataire", "type": "private", "participants": [user_id], "initial_message": "Message optionnel" } ``` #### Envoi de message ```http POST /api/chat/rooms/{room_id}/messages { "content": "Contenu du message" } ``` #### Récupération des destinataires possibles ```http GET /api/chat/recipients?search=nom Réponse: { "status": "success", "recipients": [ { "id": 10016609, "name": "ANDREZIEUX", "first_name": "Boris", "sect_name": "Tournée Boris", "role": 1, "entite_id": 5, "entite_name": "AMICALE TEST DEV PIERRE" } ] } ``` ### 3. Règles métier par rôle #### Membre (role = 1) - Peut contacter les membres de son amicale - Peut contacter les administrateurs de son amicale - Ne peut pas contacter les super-admins directement #### Admin Amicale (role = 2) - Peut contacter tous les membres de son amicale - Peut contacter les super-admins (GEOSECTOR) - Bouton "Toute l'Amicale" pour message groupé - Bouton "GEOSECTOR" pour contacter les super-admins #### Super Admin (role = 9) - Peut contacter tous les administrateurs d'amicale - Peut créer des groupes broadcast - Accès à toutes les conversations ### 4. Flux de synchronisation optimisé ``` 1. Login/Initialisation └─> UserRepository.login() └─> ChatManager.instance.initializeChat() └─> ChatModule.init() avec infos utilisateur └─> GET /api/chat/rooms (sync complète initiale) └─> Stockage dans Hive └─> Timer démarré (15 secondes) 2. Syncs en arrière-plan (pendant toute la session) └─> Timer toutes les 15 secondes └─> Vérification connectivité └─> GET /api/chat/rooms?updated_after=... └─> Si has_changes=false → skip └─> Si has_changes=true → merge changements 3. Ouverture page communication └─> Vérification ChatManager.isReady └─> Affichage immédiat depuis Hive (pas de requête) └─> ValueListenableBuilder pour UI réactive 4. Ouverture conversation spécifique └─> GET /api/chat/rooms/{id}/messages └─> Marque automatiquement comme lu côté API └─> Met à jour unread_count localement └─> Cache les messages dans Hive 5. Pull to refresh (action utilisateur) └─> GET /api/chat/rooms (sync complète forcée) └─> Remplacement total des données 6. Logout/Fermeture app └─> ChatManager.instance.dispose() └─> Arrêt du timer de sync └─> Nettoyage des ressources ``` ### 5. Gestion hors ligne #### Stockage Hive - **Box chat_rooms** : Conversations avec recent_messages - **Box chat_messages** : Messages (max 100 par room) - **Box chat_metadata** : Timestamp de dernière sync #### Détection de connectivité - Utilise `ConnectivityService` de l'app principale - Pas de sync si déconnecté - Affichage depuis cache local si hors ligne #### Persistance - `last_sync_timestamp` sauvegardé dans Hive - Survit aux redémarrages d'application - Reprise automatique des syncs incrémentales ### 6. Widgets principaux #### ChatModule ```dart // Initialisation au login via ChatManager // Ne pas appeler directement - utiliser ChatManager await ChatManager.instance.initializeChat(); // Vérification avant utilisation dans les pages if (ChatManager.instance.isReady) { return ChatModule.getRoomsPage(); } ``` #### RoomsPageEmbedded - Liste des conversations - Pas de requête à l'ouverture (données depuis Hive) - Pull to refresh pour sync manuelle - Badge de messages non lus - Affichage des 5 derniers messages (recent_messages) - Actualisation automatique via ValueListenableBuilder - **Affichage amélioré des conversations** : - Titre avec prénom + nom complet (ex: "Boris ANDREZIEUX") - Avatar avec 2 initiales (ex: "BA" pour Boris ANDREZIEUX) - Gestion intelligente des titres spéciaux ("GS" pour GEOSECTOR, "AA" pour Administrateurs Amicale) #### ChatPage - Affichage d'une conversation - Chargement automatique + marquage comme lu - Pagination des messages anciens - Envoi de messages avec état temporaire - Mise à jour temps réel des messages reçus #### RecipientSelector - Sélection des destinataires pour admin (role = 2) - Bouton "GEOSECTOR" pour super-admins - Bouton "Toute l'Amicale" (inclut l'expéditeur) - Sélection individuelle (exclut super-admins) - Affichage prénom + nom sur première ligne - Affichage du sectName uniquement (pas de fallback entité) - Avatar avec 2 initiales (prénom + nom) ### 7. Optimisations implémentées #### Performance - ✅ Sync incrémentale avec `updated_after` - ✅ Skip si `has_changes=false` - ✅ Cache des 5 derniers messages par room - ✅ Max 100 messages en cache par room - ✅ Pas de requête à l'ouverture des pages #### Bande passante - ✅ Sync complète uniquement au login - ✅ Sync incrémentale toutes les 15 secondes - ✅ Transmission uniquement des changements - ✅ Compression des réponses API #### UX - ✅ Affichage immédiat depuis cache - ✅ Marquage automatique comme lu - ✅ Badge de non-lus en temps réel via `ChatInfoService` - ✅ Pull to refresh disponible - ✅ Support hors ligne complet - ✅ Affichage prénom + nom dans les titres de conversations - ✅ Avatars avec initiales intelligentes (2 lettres) ### 8. Configuration YAML Le fichier `assets/chat_config.yaml` définit : - Permissions par rôle - Messages UI localisés - Règles de communication entre rôles - Couleurs et labels des rôles ### 9. Intégration Hive #### TypeAdapters Les adapters sont enregistrés dans `HiveAdapters.registerAll()` au démarrage : ```dart // Chat adapters - TypeIds 50-51 if (!Hive.isAdapterRegistered(50)) { Hive.registerAdapter(RoomAdapter()); } if (!Hive.isAdapterRegistered(51)) { Hive.registerAdapter(MessageAdapter()); } ``` #### Boxes gérées centralement Les boxes sont ouvertes dans `splash_page.dart` avec toutes les autres : ```dart // Dans HiveService._createSingleBox() case 'Room': await Hive.openBox(config.name); break; case 'Message': await Hive.openBox(config.name); break; ``` #### Utilisation dans ChatService ```dart // Le ChatService utilise les boxes déjà ouvertes _roomsBox = Hive.box(AppKeys.chatRoomsBoxName); _messagesBox = Hive.box(AppKeys.chatMessagesBoxName); // Pas d'ouverture de box - juste utilisation ``` TypeIds réservés : - 50 : Room - 51 : Message ### 10. Système de badges pour messages non lus #### Architecture du système de badges - **`ChatInfoService`** : Service singleton avec `ChangeNotifier` qui maintient le compte global des messages non lus - **`BadgedIcon`** : Widget avec `AnimatedBuilder` qui écoute `ChatInfoService` et affiche automatiquement le badge - **`createBadgedNavigationDestination`** : Helper pour créer des destinations de navigation avec badge #### Flux de mise à jour des badges ``` Sync toutes les 15 secondes ↓ ChatService calcule le total des unreadCount ↓ ChatInfoService.updateStats() appelé ↓ notifyListeners() déclenché ↓ BadgedIcon se redessine automatiquement ``` #### Intégration dans la navigation - **Mobile** : Badge affiché dans la `NavigationBar` inférieure - **Desktop** : Badge affiché dans la sidebar (version normale et minimisée) - **ResponsiveNavigation** : Préserve les `BadgedIcon` sans les recréer ### 11. Points d'attention #### Sécurité - Token Bearer dans tous les appels API - Vérification des permissions côté serveur - Pas de données sensibles en cache local - ChatManager vérifie l'authentification avant chaque utilisation #### Maintenance - Logs détaillés pour debug (à retirer en production) - Sync incrémentale limite la charge réseau - Gestion des erreurs avec fallback sur cache - Timer de sync s'arrête automatiquement au logout/fermeture #### Evolution - Prêt pour WebSocket (remplacement du polling) - Support des pièces jointes prévu - Indicateurs de frappe possibles - Architecture modulaire facilite les évolutions ## Affichage des noms et avatars ### Format d'affichage - **Conversations privées** : Prénom + Nom (ex: "Boris ANDREZIEUX") - **Conversations de groupe** : Liste des participants avec prénoms + noms - **Avatars** : 2 initiales (prénom + nom) ou codes spéciaux pour groupes ### Initiales intelligentes | Type de conversation | Affichage | Avatar | |---------------------|-----------|---------| | Personne seule | "Boris ANDREZIEUX" | "BA" | | GEOSECTOR Support | "GEOSECTOR Support" | "GS" | | Administrateurs Amicale | "Administrateurs Amicale" | "AA" | | Groupe Amicale | "Amicale - Tous les membres" | "AM" | | Groupe multiple | "Boris A., Pierre D. et 3 autres" | Premières initiales | ## Prochaines étapes ### Court terme - [x] Système de badges pour messages non lus - [x] Affichage prénom + nom dans les conversations - [ ] Retirer les logs de debug en production - [ ] Ajouter la pagination infinie des messages - [ ] Implémenter la recherche dans les conversations ### Moyen terme - [ ] Support des pièces jointes (images, fichiers) - [ ] Indicateurs de frappe en temps réel - [ ] Notifications push pour nouveaux messages ### Long terme - [ ] Migration vers WebSocket pour temps réel - [ ] Support des messages vocaux - [ ] Chiffrement end-to-end des messages ## Migration depuis l'ancien système Le nouveau système remplace complètement l'ancien avec : - Meilleure gestion des permissions par rôle - Synchronisation optimisée avec sync incrémentale - Support hors ligne natif via Hive - UI plus réactive avec ValueListenableBuilder - ChatManager pour gestion centralisée du cycle de vie - Initialisation automatique au login - Syncs en arrière-plan pendant toute la session ## Maintenance ### Monitoring - Vérifier les logs de sync toutes les 15 secondes - Surveiller la taille des boxes Hive - Analyser les erreurs de connectivité ### Debug Les logs actuels affichent : - 🔄 Synchronisations (complètes/incrémentales) - ✅ Rooms parsées et sauvegardées - 📵 Détection perte de connexion - ⏰ Timestamps de synchronisation - 💾 Opérations Hive ### Performance - Sync incrémentale : < 100ms si pas de changements - Sync complète : < 500ms pour 50 conversations - Affichage initial : instantané (depuis cache) - Envoi message : < 200ms en ligne