diff --git a/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin b/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin index 829e6cb..4ae171b 100644 Binary files a/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin and b/client-mobile/.gradle/8.5/executionHistory/executionHistory.bin differ diff --git a/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock b/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock index 7e6e39b..ecc10a4 100644 Binary files a/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock and b/client-mobile/.gradle/8.5/executionHistory/executionHistory.lock differ diff --git a/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin b/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin index 7d94a58..02038fc 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin and b/client-mobile/.gradle/8.5/fileHashes/fileHashes.bin differ diff --git a/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock b/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock index ee68a6b..518b6e4 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock and b/client-mobile/.gradle/8.5/fileHashes/fileHashes.lock differ diff --git a/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin b/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin index ca66158..539ffe1 100644 Binary files a/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin and b/client-mobile/.gradle/8.5/fileHashes/resourceHashesCache.bin differ diff --git a/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 9f4c4ac..53c51c5 100644 Binary files a/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/client-mobile/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin b/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin index 3e66bf0..4be8ba7 100644 Binary files a/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin and b/client-mobile/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/client-mobile/chats/data/remote/signalr/ChatEvent.kt b/client-mobile/chats/data/remote/signalr/ChatEvent.kt index 5c82b99..c3d4aa9 100644 --- a/client-mobile/chats/data/remote/signalr/ChatEvent.kt +++ b/client-mobile/chats/data/remote/signalr/ChatEvent.kt @@ -7,6 +7,7 @@ sealed class ChatEvent { data class NewMessage(val message: MessageDto) : ChatEvent() data class MessageEdited(val messageId: String, val chatId: String, val content: String) : ChatEvent() data class MessageDeleted(val messageId: String, val chatId: String) : ChatEvent() + data class ChatDeleted(val chatId: String) : ChatEvent() data class MessagesRead(val chatId: String, val userId: String, val lastReadSequenceId: Int) : ChatEvent() data class UserTyping(val chatId: String, val userId: String) : ChatEvent() data class UserStoppedTyping(val chatId: String, val userId: String) : ChatEvent() diff --git a/client-mobile/chats/data/remote/signalr/ChatHubClient.kt b/client-mobile/chats/data/remote/signalr/ChatHubClient.kt index d4c9d20..25ed94a 100644 --- a/client-mobile/chats/data/remote/signalr/ChatHubClient.kt +++ b/client-mobile/chats/data/remote/signalr/ChatHubClient.kt @@ -136,6 +136,11 @@ class ChatHubClient @Inject constructor() { _events.tryEmit(ChatEvent.MessageDeleted(messageId, chatId)) }, String::class.java, String::class.java) + conn.on("chat_deleted", { chatId: String -> + Log.d("ChatHubClient", ">>> chat_deleted event: $chatId") + _events.tryEmit(ChatEvent.ChatDeleted(chatId)) + }, String::class.java) + conn.on("messages_read", { data: MessagesReadEvent -> Log.d("ChatHubClient", ">>> messages_read event: ${data.effectiveChatId}") _events.tryEmit(ChatEvent.MessagesRead( diff --git a/client-mobile/chats/data/repository/ChatRepositoryImpl.kt b/client-mobile/chats/data/repository/ChatRepositoryImpl.kt index 970fa50..4342a6a 100644 --- a/client-mobile/chats/data/repository/ChatRepositoryImpl.kt +++ b/client-mobile/chats/data/repository/ChatRepositoryImpl.kt @@ -83,6 +83,18 @@ class ChatRepositoryImpl @Inject constructor( } } android.util.Log.d(TAG, "Cached ${entities.size} chats with messages") + + // Проверяем удалённые чаты - если чата нет в списке от сервера, удаляем из БД + val serverChatIds = chats.map { it.id }.toSet() + val localChats = chatDao.getAllChats() + localChats.forEach { localChat -> + if (localChat.id !in serverChatIds) { + android.util.Log.d(TAG, "Chat ${localChat.id} was deleted on server, removing from local DB") + chatDao.deleteChat(localChat.id) + messageDao.clearChat(localChat.id) + } + } + chats } catch (e: Exception) { android.util.Log.d(TAG, "Network load failed, using cache") diff --git a/client-mobile/chats/data/signalr/MessageSignalRHandler.kt b/client-mobile/chats/data/signalr/MessageSignalRHandler.kt index 8c5da95..9671cc0 100644 --- a/client-mobile/chats/data/signalr/MessageSignalRHandler.kt +++ b/client-mobile/chats/data/signalr/MessageSignalRHandler.kt @@ -8,6 +8,7 @@ import chats.data.remote.signalr.MessagesReadEvent import chats.data.remote.signalr.ReactionEvent import chats.data.repository.toEntity import com.google.gson.Gson +import core.database.data.ChatDao import core.database.data.MessageDao import core.database.data.MessageEntity import core.database.data.SyncStatus @@ -18,22 +19,11 @@ import kotlinx.coroutines.flow.collectLatest import javax.inject.Inject import javax.inject.Singleton -/** - * Обработчик SignalR событий для обновления локального кэша - * - * Обрабатывает события: - * - new_message: новое сообщение в чате - * - message_edited: сообщение отредактировано - * - message_deleted: сообщение удалено - * - messages_read: сообщения прочитаны - * - reaction_added/removed: реакция добавлена/удалена - * - * Все изменения сразу записываются в Room, UI обновляется через Flow - */ @Singleton class MessageSignalRHandler @Inject constructor( private val hubClient: ChatHubClient, - private val dao: MessageDao, + private val messageDao: MessageDao, + private val chatDao: ChatDao, private val serverConfig: ServerConfig, private val tokenManager: TokenManager ) { @@ -78,6 +68,7 @@ class MessageSignalRHandler @Inject constructor( is ChatEvent.NewMessage -> handleNewMessage(event.message) is ChatEvent.MessageEdited -> handleMessageEdited(event.messageId, event.chatId, event.content) is ChatEvent.MessageDeleted -> handleMessageDeleted(event.messageId, event.chatId) + is ChatEvent.ChatDeleted -> handleChatDeleted(event.chatId) is ChatEvent.MessagesRead -> handleMessagesRead(event.chatId, event.lastReadSequenceId) is ChatEvent.ReactionUpdated -> handleReactionUpdated( event.messageId, @@ -91,6 +82,12 @@ class MessageSignalRHandler @Inject constructor( } } + private suspend fun handleChatDeleted(chatId: String) { + Log.d(TAG, "Chat deleted: $chatId, removing from local database") + chatDao.deleteChat(chatId) + Log.d(TAG, "Successfully removed chat $chatId from local database") + } + private suspend fun handleNewMessage(dto: MessageDto) { Log.d(TAG, "New message received: ${dto.id} in chat ${dto.chatId}") @@ -99,7 +96,7 @@ class MessageSignalRHandler @Inject constructor( syncStatus = SyncStatus.SYNCED ) - val existing = dao.getMessageById(dto.id) + val existing = messageDao.getMessageById(dto.id) if (existing != null && existing.syncStatus == SyncStatus.SYNCING) { val syncedEntity = entity.copy( syncStatus = SyncStatus.SYNCED, @@ -107,10 +104,10 @@ class MessageSignalRHandler @Inject constructor( isEditedLocally = false, editedContent = null ) - dao.insertMessage(syncedEntity) + messageDao.insertMessage(syncedEntity) Log.d(TAG, "Merged local message with server response: ${dto.id}") } else { - dao.insertMessage(entity) + messageDao.insertMessage(entity) Log.d(TAG, "Saved new message: ${dto.id}") } } @@ -118,7 +115,7 @@ class MessageSignalRHandler @Inject constructor( private suspend fun handleMessageEdited(messageId: String, chatId: String, content: String) { Log.d(TAG, "Message edited: $messageId") - val existing = dao.getMessageById(messageId) + val existing = messageDao.getMessageById(messageId) if (existing != null) { if (existing.isEditedLocally) { Log.d(TAG, "Message is being edited locally, skipping server update: $messageId") @@ -129,7 +126,7 @@ class MessageSignalRHandler @Inject constructor( content = content, lastUpdated = System.currentTimeMillis() ) - dao.insertMessage(updated) + messageDao.insertMessage(updated) Log.d(TAG, "Updated edited message: $messageId") } else { Log.w(TAG, "Edited message not found in cache: $messageId") @@ -139,12 +136,12 @@ class MessageSignalRHandler @Inject constructor( private suspend fun handleMessageDeleted(messageId: String, chatId: String) { Log.d(TAG, "Message deleted: $messageId") - val existing = dao.getMessageById(messageId) + val existing = messageDao.getMessageById(messageId) if (existing?.isDeletedLocally == true) { - dao.deleteMessage(messageId) + messageDao.deleteMessage(messageId) Log.d(TAG, "Completed local deletion: $messageId") } else { - dao.deleteMessage(messageId) + messageDao.deleteMessage(messageId) Log.d(TAG, "Removed deleted message from cache: $messageId") } } @@ -156,13 +153,13 @@ class MessageSignalRHandler @Inject constructor( } Log.d(TAG, "Messages read in chat $chatId up to sequenceId $lastReadSequenceId") - dao.markMessagesAsRead(chatId, lastReadSequenceId) + messageDao.markMessagesAsRead(chatId, lastReadSequenceId) } private suspend fun handleReactionUpdated(messageId: String, emoji: String, isRemoved: Boolean) { Log.d(TAG, "Reaction ${if (isRemoved) "removed" else "added"}: $emoji on message $messageId") - val existing = dao.getMessageById(messageId) + val existing = messageDao.getMessageById(messageId) if (existing != null) { val reactions = gson.fromJson>(existing.reactionsJson, Map::class.java) ?: emptyMap() val updatedReactions = reactions.toMutableMap() @@ -182,7 +179,7 @@ class MessageSignalRHandler @Inject constructor( reactionsJson = gson.toJson(updatedReactions), lastUpdated = System.currentTimeMillis() ) - dao.insertMessage(updated) + messageDao.insertMessage(updated) Log.d(TAG, "Updated reactions for message: $messageId") } } diff --git a/client-mobile/chats/di/ChatModule.kt b/client-mobile/chats/di/ChatModule.kt index 25e6cb7..b82c74a 100644 --- a/client-mobile/chats/di/ChatModule.kt +++ b/client-mobile/chats/di/ChatModule.kt @@ -33,11 +33,12 @@ object ChatModule { @Singleton fun provideMessageSignalRHandler( hubClient: ChatHubClient, - dao: MessageDao, + messageDao: MessageDao, + chatDao: ChatDao, serverConfig: ServerConfig, tokenManager: TokenManager ): MessageSignalRHandler { - return MessageSignalRHandler(hubClient, dao, serverConfig, tokenManager) + return MessageSignalRHandler(hubClient, messageDao, chatDao, serverConfig, tokenManager) } @Provides