import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../domain/entities/chat.dart'; import '../../domain/entities/message.dart'; import '../../domain/repositories/chat_repository.dart'; import 'chat_event.dart'; import 'chat_state.dart'; class ChatBloc extends Bloc { final ChatRepository chatRepository; StreamSubscription? _messageSubscription; StreamSubscription? _typingSubscription; ChatBloc({required this.chatRepository}) : super(const ChatState.initial()) { on(_onStarted); on(_onChatsLoaded); on(_onChatSelected); on(_onMessagesRequested); on(_onMessageSent); on(_onFavoritesRequested); on(_onTypingUpdated); on(_onSendTypingStatus); on(_onCacheCleared); _messageSubscription = chatRepository.messageStream.listen((message) { add(ChatEvent.messagesRequested(message.chatId)); }); _typingSubscription = chatRepository.typingStream.listen((data) { add(ChatEvent.typingUpdated(data['chatId'], data['userId'], data['isTyping'])); }); } Future _onStarted(ChatEventStarted event, Emitter emit) async { // ignore: avoid_print print('[DEBUG] ChatBloc._onStarted for user: ${event.userId}'); // Start SignalR in background chatRepository.initSignalR().catchError((e) { // Log or handle SignalR init error }); // Immediately trigger chats loading add(ChatEvent.chatsLoaded(currentUserId: event.userId)); add(const ChatEvent.favoritesRequested()); } Future _onChatsLoaded(ChatEventChatsLoaded event, Emitter emit) async { emit(const ChatState.loading()); final result = await chatRepository.getChats(currentUserId: event.currentUserId); result.fold( (error) => emit(ChatState.error(error.message ?? 'Failed to load chats')), (chats) => emit(ChatState.chatsLoaded(chats)), ); } Future _onChatSelected(ChatEventChatSelected event, Emitter emit) async { emit(const ChatState.loading()); final chatResult = await chatRepository.getChatById(event.chatId); final messagesResult = await chatRepository.getMessages(event.chatId); chatResult.fold( (error) => emit(ChatState.error(error.message ?? 'Failed to load chat')), (chat) { messagesResult.fold( (error) => emit(ChatState.chatSelected(chat, [])), (messages) => emit(ChatState.chatSelected(chat, messages)), ); }, ); } Future _onMessagesRequested(ChatEventMessagesRequested event, Emitter emit) async { final result = await chatRepository.getMessages(event.chatId); result.fold( (error) => emit(ChatState.error(error.message ?? 'Failed to load messages')), (messages) => emit(ChatState.messagesLoaded(messages)), ); } Future _onMessageSent(ChatEventMessageSent event, Emitter emit) async { final result = await chatRepository.sendMessage(event.chatId, event.content); result.fold( (error) => emit(ChatState.error(error.message ?? 'Failed to send message')), (_) => add(ChatEvent.messagesRequested(event.chatId)), ); } Future _onFavoritesRequested(ChatEventFavoritesRequested event, Emitter emit) async { emit(const ChatState.loading()); final result = await chatRepository.getOrCreateFavorites(); result.fold( (error) => emit(ChatState.error(error.message ?? 'Failed to open Favorites')), (chat) => add(ChatEvent.chatSelected(chat.id)), ); } Future _onTypingUpdated(ChatEventTypingUpdated event, Emitter emit) async { final Map> newTypingUsers = Map.from(state.typingUsers); final Set chatTypingUsers = Set.from(newTypingUsers[event.chatId] ?? {}); if (event.isTyping) { chatTypingUsers.add(event.userId); } else { chatTypingUsers.remove(event.userId); } if (chatTypingUsers.isEmpty) { newTypingUsers.remove(event.chatId); } else { newTypingUsers[event.chatId] = chatTypingUsers; } emit(state.copyWith(typingUsers: newTypingUsers)); } Future _onSendTypingStatus(ChatEventSendTypingStatus event, Emitter emit) async { await chatRepository.sendTypingStatus(event.chatId, event.isTyping); } Future _onCacheCleared(ChatEventCacheCleared event, Emitter emit) async { // ignore: avoid_print print('[DEBUG] ChatBloc._onCacheCleared START'); await chatRepository.clearCache(); // ignore: avoid_print print('[DEBUG] ChatBloc._onCacheCleared END'); emit(const ChatState.initial()); } @override Future close() { _messageSubscription?.cancel(); _typingSubscription?.cancel(); chatRepository.disposeSignalR(); return super.close(); } }