Files
geo/app/lib/chat/repositories/chat_repository.dart
pierre 599b9fcda0 feat: Gestion des secteurs et migration v3.0.4+304
- Ajout système complet de gestion des secteurs avec contours géographiques
- Import des contours départementaux depuis GeoJSON
- API REST pour la gestion des secteurs (/api/sectors)
- Service de géolocalisation pour déterminer les secteurs
- Migration base de données avec tables x_departements_contours et sectors_adresses
- Interface Flutter pour visualisation et gestion des secteurs
- Ajout thème sombre dans l'application
- Corrections diverses et optimisations

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 11:01:45 +02:00

365 lines
13 KiB
Dart
Executable File

import 'package:hive/hive.dart';
import 'package:uuid/uuid.dart';
import '../../core/constants/app_keys.dart';
import '../models/conversation_model.dart';
import '../models/message_model.dart';
import '../models/participant_model.dart';
import '../services/chat_api_service.dart';
import '../services/notifications/mqtt_notification_service.dart';
/// Repository pour la gestion des fonctionnalités de chat
///
/// Ce repository centralise toutes les opérations liées au chat,
/// y compris la gestion des conversations, des messages et des participants
class ChatRepository {
final ChatApiService _apiService;
final MqttNotificationService _mqttService;
ChatRepository(this._apiService, this._mqttService);
/// Liste des conversations de l'utilisateur
Future<List<ConversationModel>> getConversations({bool forceRefresh = false}) async {
try {
// Récupérer depuis Hive
var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
var localConversations = box.values.toList();
// Si on force le rafraîchissement ou qu'on n'a pas de données locales
if (forceRefresh || localConversations.isEmpty) {
try {
// Récupérer depuis l'API
var apiConversations = await _apiService.getConversations();
// Mettre à jour Hive
await box.clear();
for (var conversation in apiConversations) {
await box.put(conversation.id, conversation);
}
return apiConversations;
} catch (e) {
// Si l'API échoue, utiliser les données locales
if (localConversations.isNotEmpty) {
return localConversations;
}
rethrow;
}
}
return localConversations;
} catch (e) {
throw Exception('Erreur lors de la récupération des conversations: $e');
}
}
/// Récupère une conversation spécifique
Future<ConversationModel> getConversation(String id) async {
try {
// Vérifier d'abord dans Hive
var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
var localConversation = box.get(id);
if (localConversation != null) {
return localConversation;
}
// Sinon récupérer depuis l'API
var apiConversation = await _apiService.getConversation(id);
await box.put(id, apiConversation);
return apiConversation;
} catch (e) {
throw Exception('Erreur lors de la récupération de la conversation: $e');
}
}
/// Crée une nouvelle conversation
Future<ConversationModel> createConversation(Map<String, dynamic> data) async {
try {
// Créer via l'API
var conversation = await _apiService.createConversation(data);
// Sauvegarder dans Hive
var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
await box.put(conversation.id, conversation);
// S'abonner aux notifications de la conversation
await _mqttService.subscribeToConversation(conversation.id);
return conversation;
} catch (e) {
throw Exception('Erreur lors de la création de la conversation: $e');
}
}
/// Supprime une conversation
Future<void> deleteConversation(String id) async {
try {
// Supprimer via l'API
await _apiService.deleteConversation(id);
// Supprimer de Hive
var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
await box.delete(id);
// Se désabonner des notifications
await _mqttService.unsubscribeFromConversation(id);
} catch (e) {
throw Exception('Erreur lors de la suppression de la conversation: $e');
}
}
/// Épingle/désépingle une conversation
Future<void> pinConversation(String id, bool isPinned) async {
try {
await _apiService.pinConversation(id, isPinned);
// Mettre à jour dans Hive
var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
var conversation = box.get(id);
if (conversation != null) {
await box.put(id, conversation.copyWith(isPinned: isPinned));
}
} catch (e) {
throw Exception('Erreur lors de l\'épinglage de la conversation: $e');
}
}
/// Met à jour les permissions de réponse
Future<void> updateReplyPermission(String id, String replyPermission) async {
try {
await _apiService.updateReplyPermission(id, replyPermission);
// Mettre à jour dans Hive
var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
var conversation = box.get(id);
if (conversation != null) {
await box.put(id, conversation.copyWith(replyPermission: replyPermission));
}
} catch (e) {
throw Exception('Erreur lors de la mise à jour des permissions: $e');
}
}
/// Récupère les messages d'une conversation
Future<List<MessageModel>> getMessages(String conversationId, {int page = 1, int limit = 50}) async {
try {
// Récupérer depuis Hive
var box = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
var localMessages = box.values
.where((m) => m.conversationId == conversationId)
.toList()
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
// Si on a assez de messages localement
if (localMessages.length >= page * limit) {
return localMessages.skip((page - 1) * limit).take(limit).toList();
}
try {
// Récupérer depuis l'API
var apiMessages = await _apiService.getMessages(conversationId, page: page, limit: limit);
// Mettre à jour Hive
for (var message in apiMessages) {
await box.put(message.id, message);
}
return apiMessages;
} catch (e) {
// Si l'API échoue, utiliser les données locales
if (localMessages.isNotEmpty) {
return localMessages.skip((page - 1) * limit).take(limit).toList();
}
rethrow;
}
} catch (e) {
throw Exception('Erreur lors de la récupération des messages: $e');
}
}
/// Envoie un message via MQTT
Future<void> sendMessage(String conversationId, Map<String, dynamic> messageData) async {
try {
// Générer un ID unique pour le message
var messageId = const Uuid().v4();
var userId = messageData['senderId'] as String?;
// Créer le message
var message = MessageModel(
id: messageId,
conversationId: conversationId,
senderId: userId,
senderType: 'user',
content: messageData['content'] as String,
contentType: messageData['contentType'] as String? ?? 'text',
createdAt: DateTime.now(),
status: 'sent',
isAnnouncement: messageData['isAnnouncement'] as bool? ?? false,
);
// Sauvegarder temporairement dans Hive
var box = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
await box.put(messageId, message);
// Publier via MQTT
await _mqttService.publishMessage('chat/message/send', {
'messageId': messageId,
'conversationId': conversationId,
'senderId': userId,
'content': message.content,
'contentType': message.contentType,
'timestamp': message.createdAt.toIso8601String(),
'isAnnouncement': message.isAnnouncement,
});
} catch (e) {
throw Exception('Erreur lors de l\'envoi du message: $e');
}
}
/// Marque un message comme lu
Future<void> markMessageAsRead(String messageId) async {
try {
// Mettre à jour via l'API
await _apiService.markMessageAsRead(messageId);
// Mettre à jour dans Hive
var box = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
var message = box.get(messageId);
if (message != null) {
await box.put(messageId, message.copyWith(
status: 'read',
readAt: DateTime.now(),
));
}
} catch (e) {
throw Exception('Erreur lors du marquage comme lu: $e');
}
}
/// Ajoute un participant à une conversation
Future<void> addParticipant(String conversationId, Map<String, dynamic> participantData) async {
try {
await _apiService.addParticipant(conversationId, participantData);
// Mettre à jour la conversation dans Hive
var conversationBox = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
var conversation = conversationBox.get(conversationId);
if (conversation != null) {
var updatedParticipants = List<ParticipantModel>.from(conversation.participants);
updatedParticipants.add(ParticipantModel.fromJson(participantData));
await conversationBox.put(conversationId, conversation.copyWith(participants: updatedParticipants));
}
} catch (e) {
throw Exception('Erreur lors de l\'ajout du participant: $e');
}
}
/// Retire un participant d'une conversation
Future<void> removeParticipant(String conversationId, String participantId) async {
try {
await _apiService.removeParticipant(conversationId, participantId);
// Mettre à jour la conversation dans Hive
var conversationBox = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
var conversation = conversationBox.get(conversationId);
if (conversation != null) {
var updatedParticipants = List<ParticipantModel>.from(conversation.participants);
updatedParticipants.removeWhere((p) => p.id == participantId);
await conversationBox.put(conversationId, conversation.copyWith(participants: updatedParticipants));
}
} catch (e) {
throw Exception('Erreur lors du retrait du participant: $e');
}
}
/// Crée un utilisateur anonyme (pour Resalice)
Future<String> createAnonymousUser({String? name, String? email}) async {
try {
return await _apiService.createAnonymousUser(name: name, email: email);
} catch (e) {
throw Exception('Erreur lors de la création de l\'utilisateur anonyme: $e');
}
}
/// Convertit un utilisateur anonyme en utilisateur authentifié
Future<void> convertAnonymousToUser(String anonymousId, String userId) async {
try {
// Mettre à jour tous les messages de l'utilisateur anonyme
var messageBox = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
var messages = messageBox.values.where((m) => m.senderId == anonymousId).toList();
for (var message in messages) {
await messageBox.put(message.id, message.copyWith(
senderId: userId,
senderType: 'user',
));
}
} catch (e) {
throw Exception('Erreur lors de la conversion de l\'utilisateur: $e');
}
}
/// Récupère les annonces
Future<List<ConversationModel>> getAnnouncements({bool forceRefresh = false}) async {
try {
// Filtrer les conversations pour n'avoir que les annonces
var conversations = await getConversations(forceRefresh: forceRefresh);
return conversations.where((c) => c.type == 'announcement').toList();
} catch (e) {
throw Exception('Erreur lors de la récupération des annonces: $e');
}
}
/// Crée une nouvelle annonce
Future<ConversationModel> createAnnouncement(Map<String, dynamic> data) async {
try {
// Créer la conversation comme une annonce
data['type'] = 'announcement';
return await createConversation(data);
} catch (e) {
throw Exception('Erreur lors de la création de l\'annonce: $e');
}
}
/// Récupère les statistiques d'une annonce
Future<Map<String, dynamic>> getAnnouncementStats(String conversationId) async {
try {
return await _apiService.getAnnouncementStats(conversationId);
} catch (e) {
throw Exception('Erreur lors de la récupération des statistiques: $e');
}
}
/// Récupère les cibles d'audience disponibles
Future<List<Map<String, dynamic>>> getAvailableAudienceTargets() async {
try {
return await _apiService.getAvailableAudienceTargets();
} catch (e) {
throw Exception('Erreur lors de la récupération des cibles: $e');
}
}
/// Ajoute une cible d'audience
Future<void> addAudienceTarget(String conversationId, Map<String, dynamic> targetData) async {
try {
// L'ajout des cibles d'audience est géré lors de la création de l'annonce
// Mais on pourrait avoir besoin de modifier les cibles plus tard
throw UnimplementedError('Ajout de cible non encore implémenté');
} catch (e) {
throw Exception('Erreur lors de l\'ajout de cible: $e');
}
}
/// Retire une cible d'audience
Future<void> removeAudienceTarget(String conversationId, String targetId) async {
try {
// Le retrait des cibles d'audience est géré lors de la création de l'annonce
throw UnimplementedError('Retrait de cible non encore implémenté');
} catch (e) {
throw Exception('Erreur lors du retrait de cible: $e');
}
}
}