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 = "") {
|
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) {
|
// if (opponentKey == publicKey) {
|
||||||
// return
|
// return
|
||||||
@@ -345,9 +348,11 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
val forwardMessages = ForwardManager.getForwardMessagesForChat(publicKey)
|
val forwardMessages = ForwardManager.getForwardMessagesForChat(publicKey)
|
||||||
val hasForward = forwardMessages.isNotEmpty()
|
val hasForward = forwardMessages.isNotEmpty()
|
||||||
if (hasForward) {
|
if (hasForward) {
|
||||||
|
Log.d(TAG, "📨 Has forward messages: ${forwardMessages.size}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сбрасываем состояние
|
// Сбрасываем состояние
|
||||||
|
Log.d(TAG, "🔄 Resetting state, setting messages to empty")
|
||||||
_messages.value = emptyList()
|
_messages.value = emptyList()
|
||||||
_opponentOnline.value = false
|
_opponentOnline.value = false
|
||||||
_opponentTyping.value = false
|
_opponentTyping.value = false
|
||||||
@@ -359,6 +364,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
subscribedToOnlineStatus = false // 🔥 Сбрасываем флаг подписки при смене диалога
|
subscribedToOnlineStatus = false // 🔥 Сбрасываем флаг подписки при смене диалога
|
||||||
isDialogActive = true // 🔥 Диалог активен!
|
isDialogActive = true // 🔥 Диалог активен!
|
||||||
|
|
||||||
|
Log.d(TAG, "📊 State after reset: messagesCount=${_messages.value.size}, isDialogActive=$isDialogActive")
|
||||||
|
|
||||||
// 📨 Применяем Forward сообщения СРАЗУ после сброса
|
// 📨 Применяем Forward сообщения СРАЗУ после сброса
|
||||||
if (hasForward) {
|
if (hasForward) {
|
||||||
// Конвертируем ForwardMessage в ReplyMessage
|
// Конвертируем ForwardMessage в ReplyMessage
|
||||||
@@ -414,17 +421,25 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
val opponent = opponentKey ?: return
|
val opponent = opponentKey ?: return
|
||||||
val dialogKey = getDialogKey(account, opponent)
|
val dialogKey = getDialogKey(account, opponent)
|
||||||
|
|
||||||
|
Log.d(TAG, "📥 loadMessagesFromDatabase START: account=$account, opponent=$opponent, dialogKey=$dialogKey")
|
||||||
|
|
||||||
// 📁 Проверяем является ли это Saved Messages
|
// 📁 Проверяем является ли это Saved Messages
|
||||||
val isSavedMessages = (opponent == account)
|
val isSavedMessages = (opponent == account)
|
||||||
|
|
||||||
if (isLoadingMessages) return
|
if (isLoadingMessages) {
|
||||||
|
Log.d(TAG, "⚠️ loadMessagesFromDatabase SKIP - already loading")
|
||||||
|
return
|
||||||
|
}
|
||||||
isLoadingMessages = true
|
isLoadingMessages = true
|
||||||
|
|
||||||
loadingJob = viewModelScope.launch(Dispatchers.IO) {
|
loadingJob = viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
// 🔥 МГНОВЕННАЯ загрузка из кэша если есть!
|
// 🔥 МГНОВЕННАЯ загрузка из кэша если есть!
|
||||||
val cachedMessages = dialogMessagesCache[dialogKey]
|
val cachedMessages = dialogMessagesCache[dialogKey]
|
||||||
|
Log.d(TAG, "📦 Cache check: dialogKey=$dialogKey, cachedCount=${cachedMessages?.size ?: 0}")
|
||||||
|
|
||||||
if (cachedMessages != null && cachedMessages.isNotEmpty()) {
|
if (cachedMessages != null && cachedMessages.isNotEmpty()) {
|
||||||
|
Log.d(TAG, "✅ Using cached messages: ${cachedMessages.size}")
|
||||||
withContext(Dispatchers.Main.immediate) {
|
withContext(Dispatchers.Main.immediate) {
|
||||||
_messages.value = cachedMessages
|
_messages.value = cachedMessages
|
||||||
_isLoading.value = false
|
_isLoading.value = false
|
||||||
@@ -445,8 +460,11 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
messageDao.getMessageCount(account, dialogKey)
|
messageDao.getMessageCount(account, dialogKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "📊 DB message count: totalCount=$totalCount, isSavedMessages=$isSavedMessages")
|
||||||
|
|
||||||
if (totalCount == 0) {
|
if (totalCount == 0) {
|
||||||
// Пустой диалог - сразу показываем пустое состояние без скелетона
|
// Пустой диалог - сразу показываем пустое состояние без скелетона
|
||||||
|
Log.d(TAG, "📭 Empty dialog - showing empty state")
|
||||||
withContext(Dispatchers.Main.immediate) {
|
withContext(Dispatchers.Main.immediate) {
|
||||||
_messages.value = emptyList()
|
_messages.value = emptyList()
|
||||||
_isLoading.value = false
|
_isLoading.value = false
|
||||||
@@ -457,20 +475,30 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
// 🔥 Есть сообщения - показываем скелетон и загружаем с задержкой для анимации
|
// 🔥 Есть сообщения - показываем скелетон и загружаем с задержкой для анимации
|
||||||
if (delayMs > 0) {
|
if (delayMs > 0) {
|
||||||
|
Log.d(TAG, "⏳ Delaying load for ${delayMs}ms")
|
||||||
withContext(Dispatchers.Main.immediate) {
|
withContext(Dispatchers.Main.immediate) {
|
||||||
_isLoading.value = true // Показываем скелетон
|
_isLoading.value = true // Показываем скелетон
|
||||||
}
|
}
|
||||||
delay(delayMs)
|
delay(delayMs)
|
||||||
|
Log.d(TAG, "✅ Delay finished, starting DB load")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 🔥 Получаем первую страницу - используем специальный метод для saved messages
|
// 🔥 Получаем первую страницу - используем специальный метод для saved messages
|
||||||
val entities = if (isSavedMessages) {
|
Log.d(TAG, "🔍 Querying DB for messages...")
|
||||||
messageDao.getMessagesForSavedDialog(account, limit = PAGE_SIZE, offset = 0)
|
val entities = try {
|
||||||
} else {
|
if (isSavedMessages) {
|
||||||
messageDao.getMessages(account, dialogKey, limit = PAGE_SIZE, offset = 0)
|
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
|
hasMoreMessages = entities.size >= PAGE_SIZE
|
||||||
currentOffset = entities.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()
|
dialogMessagesCache[dialogKey] = messages.toList()
|
||||||
|
Log.d(TAG, "💾 Saved to cache: dialogKey=$dialogKey, count=${messages.size}")
|
||||||
|
|
||||||
// 🔥 СРАЗУ обновляем UI - пользователь видит сообщения мгновенно
|
// 🔥 СРАЗУ обновляем UI - пользователь видит сообщения мгновенно
|
||||||
// НО сохраняем оптимистичные сообщения (SENDING), которые ещё не в БД
|
// НО сохраняем оптимистичные сообщения (SENDING), которые ещё не в БД
|
||||||
@@ -528,6 +558,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
isLoadingMessages = false
|
isLoadingMessages = false
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} 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) {
|
withContext(Dispatchers.Main.immediate) {
|
||||||
_isLoading.value = false
|
_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