Files
geo/app/docs/chat.md

525 lines
16 KiB
Markdown
Executable File

# 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<Map<String, dynamic>>? 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<Room>(config.name);
break;
case 'Message':
await Hive.openBox<Message>(config.name);
break;
```
#### Utilisation dans ChatService
```dart
// Le ChatService utilise les boxes déjà ouvertes
_roomsBox = Hive.box<Room>(AppKeys.chatRoomsBoxName);
_messagesBox = Hive.box<Message>(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