feat: Enhance message caching and background refresh logic in ChatViewModel; improve decryption methods with multiple attempts for better reliability
This commit is contained in:
@@ -296,6 +296,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
)
|
)
|
||||||
// Просто добавляем как в архиве: setMessages((prev) => ([...prev, newMessage]))
|
// Просто добавляем как в архиве: setMessages((prev) => ([...prev, newMessage]))
|
||||||
_messages.value = _messages.value + message
|
_messages.value = _messages.value + message
|
||||||
|
|
||||||
|
// 🔥 Обновляем кэш чтобы при перезаходе сообщение уже было
|
||||||
|
val dialogKey = getDialogKey(account, packet.fromPublicKey)
|
||||||
|
updateDialogCache(dialogKey, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ НЕ сохраняем в БД здесь - это делает MessageRepository.handleIncomingMessage()!
|
// ✅ НЕ сохраняем в БД здесь - это делает MessageRepository.handleIncomingMessage()!
|
||||||
@@ -517,8 +521,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <EFBFBD> Фоновое обновление сообщений из БД (проверка новых)
|
* 🔥 Фоновое обновление сообщений из БД (проверка новых)
|
||||||
* Вызывается когда кэш уже отображён, но нужно проверить есть ли новые сообщения
|
* Вызывается когда кэш уже отображён, но нужно проверить есть ли новые сообщения
|
||||||
|
* 🔥 ВАЖНО: НЕ заменяем все сообщения - только добавляем новые, сохраняя существующие!
|
||||||
*/
|
*/
|
||||||
private suspend fun refreshMessagesFromDb(
|
private suspend fun refreshMessagesFromDb(
|
||||||
account: String,
|
account: String,
|
||||||
@@ -529,17 +534,27 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
try {
|
try {
|
||||||
val entities = messageDao.getMessages(account, dialogKey, limit = PAGE_SIZE, offset = 0)
|
val entities = messageDao.getMessages(account, dialogKey, limit = PAGE_SIZE, offset = 0)
|
||||||
|
|
||||||
// Если в БД есть новые сообщения
|
// 🔥 Берём ТЕКУЩЕЕ состояние UI (может уже содержать новые сообщения)
|
||||||
if (entities.size != cachedMessages.size) {
|
val currentMessages = _messages.value
|
||||||
val messages = ArrayList<ChatMessage>(entities.size)
|
val existingIds = currentMessages.map { it.id }.toSet()
|
||||||
for (entity in entities.asReversed()) {
|
|
||||||
messages.add(entityToChatMessage(entity))
|
// 🔥 Находим только НОВЫЕ сообщения (которых нет в текущем UI)
|
||||||
|
val newEntities = entities.filter { it.messageId !in existingIds }
|
||||||
|
|
||||||
|
if (newEntities.isNotEmpty()) {
|
||||||
|
val newMessages = ArrayList<ChatMessage>(newEntities.size)
|
||||||
|
for (entity in newEntities) {
|
||||||
|
newMessages.add(entityToChatMessage(entity))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔥 ДОБАВЛЯЕМ новые к текущим, а не заменяем!
|
||||||
|
// Сортируем по timestamp чтобы новые были в конце
|
||||||
|
val updatedMessages = (currentMessages + newMessages).sortedBy { it.timestamp }
|
||||||
|
|
||||||
// Обновляем кэш и UI
|
// Обновляем кэш и UI
|
||||||
dialogMessagesCache[dialogKey] = messages.toList()
|
dialogMessagesCache[dialogKey] = updatedMessages
|
||||||
withContext(Dispatchers.Main.immediate) {
|
withContext(Dispatchers.Main.immediate) {
|
||||||
_messages.value = messages
|
_messages.value = updatedMessages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,10 +575,12 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
*/
|
*/
|
||||||
private fun updateDialogCache(dialogKey: String, newMessage: ChatMessage) {
|
private fun updateDialogCache(dialogKey: String, newMessage: ChatMessage) {
|
||||||
val current = dialogMessagesCache[dialogKey]?.toMutableList() ?: mutableListOf()
|
val current = dialogMessagesCache[dialogKey]?.toMutableList() ?: mutableListOf()
|
||||||
// Добавляем в конец (новые сообщения)
|
// 🔥 Проверяем на дубликат по ID
|
||||||
|
if (current.none { it.id == newMessage.id }) {
|
||||||
current.add(newMessage)
|
current.add(newMessage)
|
||||||
dialogMessagesCache[dialogKey] = current
|
dialogMessagesCache[dialogKey] = current
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <20>🚀 Загрузка следующей страницы (для бесконечной прокрутки)
|
* <20>🚀 Загрузка следующей страницы (для бесконечной прокрутки)
|
||||||
@@ -790,30 +807,61 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
if (dataJson.contains(":") && dataJson.split(":").size == 2) {
|
if (dataJson.contains(":") && dataJson.split(":").size == 2) {
|
||||||
android.util.Log.d("ReplyDebug", " - 🔒 Blob is ENCRYPTED (iv:ciphertext format)")
|
android.util.Log.d("ReplyDebug", " - 🔒 Blob is ENCRYPTED (iv:ciphertext format)")
|
||||||
android.util.Log.d("ReplyDebug", " - Attempting to decrypt with private key...")
|
|
||||||
val privateKey = myPrivateKey
|
val privateKey = myPrivateKey
|
||||||
android.util.Log.d("ReplyDebug", " - Private key available: ${privateKey != null}")
|
var decryptionSuccess = false
|
||||||
|
|
||||||
|
// 🔥 Способ 1: Пробуем расшифровать с приватным ключом (для исходящих сообщений)
|
||||||
if (privateKey != null) {
|
if (privateKey != null) {
|
||||||
android.util.Log.d("ReplyDebug", " - Private key length: ${privateKey.length}")
|
android.util.Log.d("ReplyDebug", " - Attempting to decrypt with private key...")
|
||||||
try {
|
try {
|
||||||
val decrypted = CryptoManager.decryptWithPassword(dataJson, privateKey)
|
val decrypted = CryptoManager.decryptWithPassword(dataJson, privateKey)
|
||||||
android.util.Log.d("ReplyDebug", " - Decryption result: ${if (decrypted != null) "SUCCESS" else "NULL"}")
|
|
||||||
if (decrypted != null) {
|
if (decrypted != null) {
|
||||||
android.util.Log.d("ReplyDebug", " - Decrypted length: ${decrypted.length}")
|
android.util.Log.d("ReplyDebug", " - ✅ Decrypted with private key")
|
||||||
android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}")
|
android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}")
|
||||||
dataJson = decrypted
|
dataJson = decrypted
|
||||||
} else {
|
decryptionSuccess = true
|
||||||
android.util.Log.e("ReplyDebug", " - ❌ Decryption returned null")
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
android.util.Log.e("ReplyDebug", " - ❌ Decryption exception: ${e.message}", e)
|
android.util.Log.d("ReplyDebug", " - Private key decryption failed: ${e.message}")
|
||||||
android.util.Log.e("ReplyDebug", " - Stack trace:", e)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
android.util.Log.e("ReplyDebug", " - ❌ Cannot decrypt: private key is NULL")
|
|
||||||
|
// 🔥 Способ 2: Пробуем расшифровать с ChaCha ключом сообщения (для входящих)
|
||||||
|
if (!decryptionSuccess && content.isNotEmpty() && chachaKey.isNotEmpty() && privateKey != null) {
|
||||||
|
android.util.Log.d("ReplyDebug", " - Attempting to decrypt with ChaCha key...")
|
||||||
|
try {
|
||||||
|
val decrypted = MessageCrypto.decryptAttachmentBlob(dataJson, chachaKey, privateKey)
|
||||||
|
if (decrypted != null) {
|
||||||
|
android.util.Log.d("ReplyDebug", " - ✅ Decrypted with ChaCha key")
|
||||||
|
android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}")
|
||||||
|
dataJson = decrypted
|
||||||
|
decryptionSuccess = true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.d("ReplyDebug", " - ChaCha decryption failed: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 Способ 3: Пробуем decryptReplyBlob с plainKeyAndNonce
|
||||||
|
if (!decryptionSuccess && content.isNotEmpty() && chachaKey.isNotEmpty() && privateKey != null) {
|
||||||
|
android.util.Log.d("ReplyDebug", " - Attempting to decrypt with plainKeyAndNonce...")
|
||||||
|
try {
|
||||||
|
val decryptResult = MessageCrypto.decryptIncomingFull(content, chachaKey, privateKey)
|
||||||
|
val plainKeyAndNonce = decryptResult.plainKeyAndNonce
|
||||||
|
val decrypted = MessageCrypto.decryptReplyBlob(dataJson, plainKeyAndNonce)
|
||||||
|
if (decrypted.isNotEmpty()) {
|
||||||
|
android.util.Log.d("ReplyDebug", " - ✅ Decrypted with plainKeyAndNonce")
|
||||||
|
android.util.Log.d("ReplyDebug", " - Decrypted preview: ${decrypted.take(200)}")
|
||||||
|
dataJson = decrypted
|
||||||
|
decryptionSuccess = true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.d("ReplyDebug", " - plainKeyAndNonce decryption failed: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decryptionSuccess) {
|
||||||
|
android.util.Log.e("ReplyDebug", " - ❌ All decryption methods failed")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user