/// BLoC de gestion de la messagerie v4 library messaging_bloc; import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:injectable/injectable.dart'; import '../../../../core/utils/logger.dart'; import '../../../../core/websocket/websocket_service.dart'; import '../../domain/repositories/messaging_repository.dart'; import 'messaging_event.dart'; import 'messaging_state.dart'; @injectable class MessagingBloc extends Bloc { final MessagingRepository _repository; final WebSocketService _webSocketService; StreamSubscription? _wsSubscription; String? _currentConversationId; MessagingBloc({ required MessagingRepository repository, required WebSocketService webSocketService, }) : _repository = repository, _webSocketService = webSocketService, super(MessagingInitial()) { on(_onLoadMesConversations); on(_onOpenConversation); on(_onDemarrerConversationDirecte); on(_onDemarrerConversationRole); on(_onArchiverConversation); on(_onEnvoyerMessageTexte); on(_onLoadMessages); on(_onMarquerLu); on(_onSupprimerMessage); on(_onNouveauMessageWebSocket); _listenWebSocket(); } void _listenWebSocket() { _wsSubscription = _webSocketService.eventStream.listen((event) { if (event is ChatMessageEvent && event.conversationId != null) { AppLogger.info('MessagingBloc: NOUVEAU_MESSAGE WS pour conv ${event.conversationId}'); add(NouveauMessageWebSocket(event.conversationId!)); } }); } Future _onLoadMesConversations( LoadMesConversations event, Emitter emit, ) async { emit(MessagingLoading()); try { final conversations = await _repository.getMesConversations(); emit(MesConversationsLoaded(conversations)); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onOpenConversation( OpenConversation event, Emitter emit, ) async { emit(MessagingLoading()); try { _currentConversationId = event.conversationId; final conversation = await _repository.getConversation(event.conversationId); emit(ConversationOuverte(conversation)); // Marquer comme lu en arrière-plan (non-bloquant) _repository.marquerLu(event.conversationId); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onDemarrerConversationDirecte( DemarrerConversationDirecte event, Emitter emit, ) async { emit(MessagingLoading()); try { final conversation = await _repository.demarrerConversationDirecte( destinataireId: event.destinataireId, organisationId: event.organisationId, premierMessage: event.premierMessage, ); emit(ConversationCreee(conversation)); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onDemarrerConversationRole( DemarrerConversationRole event, Emitter emit, ) async { emit(MessagingLoading()); try { final conversation = await _repository.demarrerConversationRole( roleCible: event.roleCible, organisationId: event.organisationId, premierMessage: event.premierMessage, ); emit(ConversationCreee(conversation)); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onArchiverConversation( ArchiverConversation event, Emitter emit, ) async { try { await _repository.archiverConversation(event.conversationId); emit(const MessagingActionOk('archiver')); add(const LoadMesConversations()); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onEnvoyerMessageTexte( EnvoyerMessageTexte event, Emitter emit, ) async { try { final message = await _repository.envoyerMessage( event.conversationId, typeMessage: 'TEXTE', contenu: event.contenu, messageParentId: event.messageParentId, ); emit(MessageEnvoye(message: message, conversationId: event.conversationId)); // Recharger les messages après envoi add(LoadMessages(conversationId: event.conversationId)); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onLoadMessages( LoadMessages event, Emitter emit, ) async { try { final messages = await _repository.getMessages(event.conversationId, page: event.page); emit(MessagesLoaded( conversationId: event.conversationId, messages: messages, hasMore: messages.length >= 20, )); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onMarquerLu( MarquerLu event, Emitter emit, ) async { await _repository.marquerLu(event.conversationId); } Future _onSupprimerMessage( SupprimerMessage event, Emitter emit, ) async { try { await _repository.supprimerMessage(event.conversationId, event.messageId); emit(const MessagingActionOk('supprimer-message')); add(LoadMessages(conversationId: event.conversationId)); } catch (e) { emit(MessagingError(e.toString().replaceFirst('Exception: ', ''))); } } Future _onNouveauMessageWebSocket( NouveauMessageWebSocket event, Emitter emit, ) async { // Si la conversation est actuellement ouverte, recharger les messages if (_currentConversationId == event.conversationId) { add(LoadMessages(conversationId: event.conversationId)); } else { // Sinon, rafraîchir la liste pour mettre à jour le badge non-lus add(const LoadMesConversations()); } } @override Future close() { _wsSubscription?.cancel(); _currentConversationId = null; return super.close(); } }