feat: Add detailed logging for dialog management and message loading in ChatViewModel
This commit is contained in:
@@ -328,6 +328,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
* Открыть диалог
|
||||
*/
|
||||
fun openDialog(publicKey: String, title: String = "", username: String = "") {
|
||||
Log.d(TAG, "🚪 openDialog START: publicKey=$publicKey, title=$title")
|
||||
Log.d(TAG, "🔍 Current state: opponentKey=$opponentKey, myPublicKey=$myPublicKey")
|
||||
|
||||
// 🔥 ВСЕГДА перезагружаем данные - не кешируем, т.к. диалог мог быть удалён
|
||||
// if (opponentKey == publicKey) {
|
||||
// return
|
||||
@@ -345,9 +348,11 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val forwardMessages = ForwardManager.getForwardMessagesForChat(publicKey)
|
||||
val hasForward = forwardMessages.isNotEmpty()
|
||||
if (hasForward) {
|
||||
Log.d(TAG, "📨 Has forward messages: ${forwardMessages.size}")
|
||||
}
|
||||
|
||||
// Сбрасываем состояние
|
||||
Log.d(TAG, "🔄 Resetting state, setting messages to empty")
|
||||
_messages.value = emptyList()
|
||||
_opponentOnline.value = false
|
||||
_opponentTyping.value = false
|
||||
@@ -359,6 +364,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
subscribedToOnlineStatus = false // 🔥 Сбрасываем флаг подписки при смене диалога
|
||||
isDialogActive = true // 🔥 Диалог активен!
|
||||
|
||||
Log.d(TAG, "📊 State after reset: messagesCount=${_messages.value.size}, isDialogActive=$isDialogActive")
|
||||
|
||||
// 📨 Применяем Forward сообщения СРАЗУ после сброса
|
||||
if (hasForward) {
|
||||
// Конвертируем ForwardMessage в ReplyMessage
|
||||
@@ -414,17 +421,25 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val opponent = opponentKey ?: return
|
||||
val dialogKey = getDialogKey(account, opponent)
|
||||
|
||||
Log.d(TAG, "📥 loadMessagesFromDatabase START: account=$account, opponent=$opponent, dialogKey=$dialogKey")
|
||||
|
||||
// 📁 Проверяем является ли это Saved Messages
|
||||
val isSavedMessages = (opponent == account)
|
||||
|
||||
if (isLoadingMessages) return
|
||||
if (isLoadingMessages) {
|
||||
Log.d(TAG, "⚠️ loadMessagesFromDatabase SKIP - already loading")
|
||||
return
|
||||
}
|
||||
isLoadingMessages = true
|
||||
|
||||
loadingJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
// 🔥 МГНОВЕННАЯ загрузка из кэша если есть!
|
||||
val cachedMessages = dialogMessagesCache[dialogKey]
|
||||
Log.d(TAG, "📦 Cache check: dialogKey=$dialogKey, cachedCount=${cachedMessages?.size ?: 0}")
|
||||
|
||||
if (cachedMessages != null && cachedMessages.isNotEmpty()) {
|
||||
Log.d(TAG, "✅ Using cached messages: ${cachedMessages.size}")
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
_messages.value = cachedMessages
|
||||
_isLoading.value = false
|
||||
@@ -445,8 +460,11 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
messageDao.getMessageCount(account, dialogKey)
|
||||
}
|
||||
|
||||
Log.d(TAG, "📊 DB message count: totalCount=$totalCount, isSavedMessages=$isSavedMessages")
|
||||
|
||||
if (totalCount == 0) {
|
||||
// Пустой диалог - сразу показываем пустое состояние без скелетона
|
||||
Log.d(TAG, "📭 Empty dialog - showing empty state")
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
_messages.value = emptyList()
|
||||
_isLoading.value = false
|
||||
@@ -457,20 +475,30 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
// 🔥 Есть сообщения - показываем скелетон и загружаем с задержкой для анимации
|
||||
if (delayMs > 0) {
|
||||
Log.d(TAG, "⏳ Delaying load for ${delayMs}ms")
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
_isLoading.value = true // Показываем скелетон
|
||||
}
|
||||
delay(delayMs)
|
||||
Log.d(TAG, "✅ Delay finished, starting DB load")
|
||||
}
|
||||
|
||||
|
||||
// 🔥 Получаем первую страницу - используем специальный метод для saved messages
|
||||
val entities = if (isSavedMessages) {
|
||||
Log.d(TAG, "🔍 Querying DB for messages...")
|
||||
val entities = try {
|
||||
if (isSavedMessages) {
|
||||
messageDao.getMessagesForSavedDialog(account, limit = PAGE_SIZE, offset = 0)
|
||||
} else {
|
||||
messageDao.getMessages(account, dialogKey, limit = PAGE_SIZE, offset = 0)
|
||||
}
|
||||
} catch (e: android.database.sqlite.SQLiteBlobTooBigException) {
|
||||
// 🔥 WORKAROUND: Если сообщение слишком большое - загружаем по одному
|
||||
Log.w(TAG, "⚠️ SQLiteBlobTooBigException - loading messages one by one")
|
||||
loadMessagesOneByOne(account, dialogKey, isSavedMessages, PAGE_SIZE)
|
||||
}
|
||||
|
||||
Log.d(TAG, "📋 Loaded entities from DB: count=${entities.size}")
|
||||
|
||||
hasMoreMessages = entities.size >= PAGE_SIZE
|
||||
currentOffset = entities.size
|
||||
@@ -489,9 +517,11 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "✅ Decrypted messages: count=${messages.size}")
|
||||
|
||||
// 🔥 Сохраняем в кэш для мгновенной повторной загрузки!
|
||||
dialogMessagesCache[dialogKey] = messages.toList()
|
||||
Log.d(TAG, "💾 Saved to cache: dialogKey=$dialogKey, count=${messages.size}")
|
||||
|
||||
// 🔥 СРАЗУ обновляем UI - пользователь видит сообщения мгновенно
|
||||
// НО сохраняем оптимистичные сообщения (SENDING), которые ещё не в БД
|
||||
@@ -528,6 +558,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
isLoadingMessages = false
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ Error loading messages from DB", e)
|
||||
Log.e(TAG, "❌ Exception details: ${e.message}")
|
||||
Log.e(TAG, "❌ Stack trace: ${e.stackTraceToString()}")
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
_isLoading.value = false
|
||||
}
|
||||
@@ -536,6 +569,43 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 WORKAROUND: Загрузка сообщений по одному (для случаев когда сообщение слишком большое)
|
||||
* Пропускает сообщения которые не могут быть загружены из-за размера
|
||||
*/
|
||||
private suspend fun loadMessagesOneByOne(
|
||||
account: String,
|
||||
dialogKey: String,
|
||||
isSavedMessages: Boolean,
|
||||
limit: Int
|
||||
): List<MessageEntity> {
|
||||
val result = mutableListOf<MessageEntity>()
|
||||
var offset = 0
|
||||
var loadedCount = 0
|
||||
|
||||
while (loadedCount < limit && offset < limit * 2) { // limit * 2 чтобы не зациклиться
|
||||
try {
|
||||
val batch = if (isSavedMessages) {
|
||||
messageDao.getMessagesForSavedDialog(account, limit = 1, offset = offset)
|
||||
} else {
|
||||
messageDao.getMessages(account, dialogKey, limit = 1, offset = offset)
|
||||
}
|
||||
|
||||
if (batch.isEmpty()) break
|
||||
|
||||
result.addAll(batch)
|
||||
loadedCount++
|
||||
} catch (e: android.database.sqlite.SQLiteBlobTooBigException) {
|
||||
Log.w(TAG, "⚠️ Skipping message at offset $offset - too big")
|
||||
// Пропускаем это сообщение
|
||||
}
|
||||
offset++
|
||||
}
|
||||
|
||||
Log.d(TAG, "📦 Loaded $loadedCount messages one by one (skipped ${offset - loadedCount})")
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 Фоновое обновление сообщений из БД (проверка новых)
|
||||
* Вызывается когда кэш уже отображён, но нужно проверить есть ли новые сообщения
|
||||
|
||||
Reference in New Issue
Block a user