feat: Enhance logging in MessageRepository and ChatsListViewModel for better debugging and flow tracking
This commit is contained in:
@@ -75,23 +75,44 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
INSTANCE ?: MessageRepository(context.applicationContext).also { INSTANCE = it }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация детерминированного messageId на основе данных сообщения
|
||||
* Аналог generateRandomKeyFormSeed из Архива
|
||||
*/
|
||||
fun generateMessageId(fromPublicKey: String, toPublicKey: String, timestamp: Long): String {
|
||||
val seed = fromPublicKey + toPublicKey + timestamp.toString()
|
||||
val hash = java.security.MessageDigest.getInstance("SHA-256")
|
||||
.digest(seed.toByteArray())
|
||||
// Берём первые 16 символов hex-представления
|
||||
return hash.take(8).joinToString("") { String.format("%02x", it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация с текущим аккаунтом
|
||||
*/
|
||||
fun initialize(publicKey: String, privateKey: String) {
|
||||
android.util.Log.d("MessageRepository", "🔐 initialize() called with publicKey: ${publicKey.take(16)}...")
|
||||
currentAccount = publicKey
|
||||
currentPrivateKey = privateKey
|
||||
|
||||
// Загрузка диалогов
|
||||
scope.launch {
|
||||
dialogDao.getDialogsFlow(publicKey).collect { entities ->
|
||||
android.util.Log.d("MessageRepository", "📋 MessageRepository dialogsFlow emitted: ${entities.size} dialogs")
|
||||
_dialogs.value = entities.map { it.toDialog() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка инициализации
|
||||
*/
|
||||
fun isInitialized(): Boolean {
|
||||
return currentAccount != null && currentPrivateKey != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить поток сообщений для диалога
|
||||
*/
|
||||
@@ -210,13 +231,39 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
* Обработка входящего сообщения
|
||||
*/
|
||||
suspend fun handleIncomingMessage(packet: PacketMessage) {
|
||||
val account = currentAccount ?: return
|
||||
val privateKey = currentPrivateKey ?: return
|
||||
android.util.Log.d("MessageRepository", "═══════════════════════════════════════")
|
||||
android.util.Log.d("MessageRepository", "📩 handleIncomingMessage START")
|
||||
android.util.Log.d("MessageRepository", " from: ${packet.fromPublicKey.take(20)}...")
|
||||
|
||||
// Проверяем, не дубликат ли
|
||||
if (messageDao.messageExists(account, packet.messageId)) return
|
||||
// 🔥 Генерируем messageId если он пустой (как в Архиве - generateRandomKeyFormSeed)
|
||||
val messageId = if (packet.messageId.isBlank()) {
|
||||
generateMessageId(packet.fromPublicKey, packet.toPublicKey, packet.timestamp)
|
||||
} else {
|
||||
packet.messageId
|
||||
}
|
||||
android.util.Log.d("MessageRepository", " messageId: $messageId (original: ${packet.messageId})")
|
||||
android.util.Log.d("MessageRepository", " currentAccount: ${currentAccount?.take(20) ?: "NULL"}...")
|
||||
android.util.Log.d("MessageRepository", " currentPrivateKey: ${if (currentPrivateKey != null) "SET" else "NULL"}")
|
||||
|
||||
val account = currentAccount ?: run {
|
||||
android.util.Log.e("MessageRepository", "❌ ABORT: currentAccount is NULL!")
|
||||
return
|
||||
}
|
||||
val privateKey = currentPrivateKey ?: run {
|
||||
android.util.Log.e("MessageRepository", "❌ ABORT: currentPrivateKey is NULL!")
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем, не дубликат ли (используем сгенерированный messageId)
|
||||
val isDuplicate = messageDao.messageExists(account, messageId)
|
||||
android.util.Log.d("MessageRepository", " isDuplicate: $isDuplicate")
|
||||
if (isDuplicate) {
|
||||
android.util.Log.d("MessageRepository", "⚠️ Skipping duplicate message")
|
||||
return
|
||||
}
|
||||
|
||||
val dialogKey = getDialogKey(packet.fromPublicKey)
|
||||
android.util.Log.d("MessageRepository", " dialogKey: $dialogKey")
|
||||
|
||||
try {
|
||||
// Расшифровываем
|
||||
@@ -247,21 +294,25 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
read = 0,
|
||||
fromMe = 0,
|
||||
delivered = DeliveryStatus.DELIVERED.value,
|
||||
messageId = packet.messageId,
|
||||
messageId = messageId, // 🔥 Используем сгенерированный messageId!
|
||||
plainMessage = encryptedPlainMessage, // 🔒 Зашифрованный текст
|
||||
attachments = attachmentsJson,
|
||||
dialogKey = dialogKey
|
||||
)
|
||||
messageDao.insertMessage(entity)
|
||||
android.util.Log.d("MessageRepository", "✅ Message saved to DB: ${packet.messageId.take(16)}...")
|
||||
|
||||
// Обновляем диалог
|
||||
android.util.Log.d("MessageRepository", "🔄 Calling updateDialog for ${packet.fromPublicKey.take(16)}...")
|
||||
updateDialog(packet.fromPublicKey, plainText, packet.timestamp, incrementUnread = true)
|
||||
android.util.Log.d("MessageRepository", "✅ updateDialog completed!")
|
||||
|
||||
// Обновляем кэш
|
||||
val message = entity.toMessage()
|
||||
updateMessageCache(dialogKey, message)
|
||||
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("MessageRepository", "❌ Error handling incoming message", e)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
@@ -301,13 +352,20 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
|
||||
/**
|
||||
* Отметить диалог как прочитанный
|
||||
* 🔥 После обновления messages обновляем диалог через updateDialogFromMessages
|
||||
*/
|
||||
suspend fun markDialogAsRead(opponentKey: String) {
|
||||
val account = currentAccount ?: return
|
||||
val dialogKey = getDialogKey(opponentKey)
|
||||
|
||||
// Отмечаем сообщения как прочитанные
|
||||
messageDao.markDialogAsRead(account, dialogKey)
|
||||
dialogDao.clearUnreadCount(account, opponentKey)
|
||||
|
||||
// 🔥 КРИТИЧНО: Пересчитываем счетчики из таблицы messages
|
||||
// чтобы unread_count обновился моментально
|
||||
dialogDao.updateDialogFromMessages(account, opponentKey)
|
||||
|
||||
android.util.Log.d("MessageRepository", "✅ Dialog marked as read and updated from messages")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,22 +450,43 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
incrementUnread: Boolean = false
|
||||
) {
|
||||
val account = currentAccount ?: return
|
||||
val privateKey = currentPrivateKey ?: return
|
||||
|
||||
android.util.Log.d("MessageRepository", "📝 Updating dialog for ${opponentKey.take(16)}...")
|
||||
android.util.Log.d("MessageRepository", " lastMessage: ${lastMessage.take(50)}")
|
||||
|
||||
try {
|
||||
// 🔥 КРИТИЧНО: Сначала считаем реальное количество непрочитанных из messages
|
||||
val unreadCount = messageDao.getUnreadCountForDialog(account, opponentKey)
|
||||
android.util.Log.d("MessageRepository", " unreadCount from messages: $unreadCount")
|
||||
|
||||
// 🔒 Шифруем lastMessage
|
||||
val encryptedLastMessage = CryptoManager.encryptWithPassword(lastMessage, privateKey)
|
||||
|
||||
// Проверяем существует ли диалог
|
||||
val existing = dialogDao.getDialog(account, opponentKey)
|
||||
|
||||
if (existing != null) {
|
||||
dialogDao.updateLastMessage(account, opponentKey, lastMessage, timestamp)
|
||||
if (incrementUnread) {
|
||||
dialogDao.incrementUnreadCount(account, opponentKey)
|
||||
}
|
||||
// Обновляем существующий диалог
|
||||
android.util.Log.d("MessageRepository", " ✏️ Updating existing dialog...")
|
||||
dialogDao.updateLastMessage(account, opponentKey, encryptedLastMessage, timestamp)
|
||||
dialogDao.updateUnreadCount(account, opponentKey, unreadCount)
|
||||
} else {
|
||||
// Создаем новый диалог
|
||||
android.util.Log.d("MessageRepository", " ➕ Creating new dialog...")
|
||||
dialogDao.insertDialog(DialogEntity(
|
||||
account = account,
|
||||
opponentKey = opponentKey,
|
||||
lastMessage = lastMessage,
|
||||
lastMessage = encryptedLastMessage,
|
||||
lastMessageTimestamp = timestamp,
|
||||
unreadCount = if (incrementUnread) 1 else 0
|
||||
unreadCount = unreadCount
|
||||
))
|
||||
}
|
||||
|
||||
android.util.Log.d("MessageRepository", " ✅ Dialog updated successfully!")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("MessageRepository", " ❌ Error updating dialog", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -198,6 +198,19 @@ interface MessageDao {
|
||||
@Query("DELETE FROM messages WHERE account = :account AND message_id = :messageId")
|
||||
suspend fun deleteMessage(account: String, messageId: String)
|
||||
|
||||
/**
|
||||
* Получить количество непрочитанных сообщений для диалога
|
||||
* Считает только входящие сообщения (from_me = 0) которые не прочитаны (read = 0)
|
||||
*/
|
||||
@Query("""
|
||||
SELECT COUNT(*) FROM messages
|
||||
WHERE account = :account
|
||||
AND from_public_key = :opponentKey
|
||||
AND from_me = 0
|
||||
AND read = 0
|
||||
""")
|
||||
suspend fun getUnreadCountForDialog(account: String, opponentKey: String): Int
|
||||
|
||||
/**
|
||||
* Удалить все сообщения диалога
|
||||
*/
|
||||
@@ -345,8 +358,74 @@ interface DialogDao {
|
||||
fun getTotalUnreadCountExcludingFlow(account: String, excludeOpponentKey: String): Flow<Int>
|
||||
|
||||
/**
|
||||
* Получить общее количество непрочитанных сообщений
|
||||
* Обновить диалог, пересчитав счетчики из таблицы messages
|
||||
* Этот метод аналогичен updateDialog из Архива - обновляет все поля диалога одним запросом
|
||||
*
|
||||
* Логика:
|
||||
* 1. Берем последнее сообщение (по timestamp DESC)
|
||||
* 2. Считаем количество непрочитанных сообщений (from_me = 0 AND read = 0)
|
||||
* 3. Обновляем диалог или создаем новый
|
||||
*/
|
||||
@Query("SELECT COALESCE(SUM(unread_count), 0) FROM dialogs WHERE account = :account")
|
||||
fun getTotalUnreadCountFlow(account: String): Flow<Int>
|
||||
@Query("""
|
||||
INSERT OR REPLACE INTO dialogs (
|
||||
account,
|
||||
opponent_key,
|
||||
opponent_title,
|
||||
opponent_username,
|
||||
last_message,
|
||||
last_message_timestamp,
|
||||
unread_count,
|
||||
is_online,
|
||||
last_seen,
|
||||
verified
|
||||
)
|
||||
SELECT
|
||||
:account AS account,
|
||||
:opponentKey AS opponent_key,
|
||||
COALESCE(
|
||||
(SELECT opponent_title FROM dialogs WHERE account = :account AND opponent_key = :opponentKey),
|
||||
''
|
||||
) AS opponent_title,
|
||||
COALESCE(
|
||||
(SELECT opponent_username FROM dialogs WHERE account = :account AND opponent_key = :opponentKey),
|
||||
''
|
||||
) AS opponent_username,
|
||||
COALESCE(
|
||||
(SELECT plain_message FROM messages
|
||||
WHERE account = :account
|
||||
AND ((from_public_key = :opponentKey AND to_public_key = :account)
|
||||
OR (from_public_key = :account AND to_public_key = :opponentKey))
|
||||
ORDER BY timestamp DESC LIMIT 1),
|
||||
''
|
||||
) AS last_message,
|
||||
COALESCE(
|
||||
(SELECT MAX(timestamp) FROM messages
|
||||
WHERE account = :account
|
||||
AND ((from_public_key = :opponentKey AND to_public_key = :account)
|
||||
OR (from_public_key = :account AND to_public_key = :opponentKey))),
|
||||
0
|
||||
) AS last_message_timestamp,
|
||||
COALESCE(
|
||||
(SELECT COUNT(*) FROM messages
|
||||
WHERE account = :account
|
||||
AND from_public_key = :opponentKey
|
||||
AND to_public_key = :account
|
||||
AND from_me = 0
|
||||
AND read = 0),
|
||||
0
|
||||
) AS unread_count,
|
||||
COALESCE(
|
||||
(SELECT is_online FROM dialogs WHERE account = :account AND opponent_key = :opponentKey),
|
||||
0
|
||||
) AS is_online,
|
||||
COALESCE(
|
||||
(SELECT last_seen FROM dialogs WHERE account = :account AND opponent_key = :opponentKey),
|
||||
0
|
||||
) AS last_seen,
|
||||
COALESCE(
|
||||
(SELECT verified FROM dialogs WHERE account = :account AND opponent_key = :opponentKey),
|
||||
0
|
||||
) AS verified
|
||||
""")
|
||||
suspend fun updateDialogFromMessages(account: String, opponentKey: String)
|
||||
}
|
||||
|
||||
@@ -392,6 +392,8 @@ class Protocol(
|
||||
*/
|
||||
fun waitPacket(packetId: Int, callback: (Packet) -> Unit) {
|
||||
packetWaiters.getOrPut(packetId) { mutableListOf() }.add(callback)
|
||||
val count = packetWaiters[packetId]?.size ?: 0
|
||||
log("📝 waitPacket(0x${Integer.toHexString(packetId)}) registered. Total handlers for 0x${Integer.toHexString(packetId)}: $count")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,8 +62,10 @@ object ProtocolManager {
|
||||
* Инициализация с контекстом для доступа к MessageRepository
|
||||
*/
|
||||
fun initialize(context: Context) {
|
||||
addLog("🚀 ProtocolManager.initialize() called")
|
||||
messageRepository = MessageRepository.getInstance(context)
|
||||
setupPacketHandlers()
|
||||
addLog("🚀 ProtocolManager.initialize() completed")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,10 +81,15 @@ object ProtocolManager {
|
||||
* Настройка обработчиков пакетов
|
||||
*/
|
||||
private fun setupPacketHandlers() {
|
||||
addLog("📦 setupPacketHandlers() - Registering packet handlers...")
|
||||
|
||||
// Обработчик входящих сообщений (0x06)
|
||||
waitPacket(0x06) { packet ->
|
||||
addLog("📦 ⚡⚡⚡ PACKET 0x06 RECEIVED IN PROTOCOL_MANAGER!!! ⚡⚡⚡")
|
||||
val messagePacket = packet as PacketMessage
|
||||
addLog("📩 Incoming message from ${messagePacket.fromPublicKey.take(16)}...")
|
||||
addLog(" messageRepository = ${if (messageRepository != null) "OK" else "NULL"}")
|
||||
addLog(" messageRepository.isInitialized = ${messageRepository?.isInitialized() ?: false}")
|
||||
|
||||
// ⚡ ВАЖНО: Отправляем подтверждение доставки обратно серверу
|
||||
// Без этого сервер не будет отправлять следующие сообщения!
|
||||
@@ -94,7 +101,12 @@ object ProtocolManager {
|
||||
addLog("✅ Sent delivery confirmation for message ${messagePacket.messageId.take(16)}...")
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
messageRepository?.handleIncomingMessage(messagePacket)
|
||||
addLog("✅ handleIncomingMessage completed!")
|
||||
} catch (e: Exception) {
|
||||
addLog("❌ handleIncomingMessage ERROR: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,7 +464,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
launch(Dispatchers.IO) {
|
||||
// Отмечаем как прочитанные в БД
|
||||
messageDao.markDialogAsRead(account, dialogKey)
|
||||
dialogDao.clearUnreadCount(account, opponent)
|
||||
// 🔥 Пересчитываем счетчики из messages
|
||||
dialogDao.updateDialogFromMessages(account, opponent)
|
||||
|
||||
// Отправляем read receipt собеседнику
|
||||
if (messages.isNotEmpty()) {
|
||||
@@ -519,7 +520,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
// Фоновые операции
|
||||
messageDao.markDialogAsRead(account, dialogKey)
|
||||
dialogDao.clearUnreadCount(account, opponent)
|
||||
// 🔥 Пересчитываем счетчики из messages
|
||||
dialogDao.updateDialogFromMessages(account, opponent)
|
||||
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
@@ -1013,33 +1015,18 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
/**
|
||||
* Сохранить диалог в базу данных
|
||||
* 🔒 lastMessage шифруется для безопасного хранения
|
||||
* <EFBFBD> Используем updateDialogFromMessages для пересчета счетчиков из messages
|
||||
*/
|
||||
private suspend fun saveDialog(lastMessage: String, timestamp: Long) {
|
||||
val account = myPublicKey ?: return
|
||||
val opponent = opponentKey ?: return
|
||||
val privateKey = myPrivateKey ?: return
|
||||
|
||||
try {
|
||||
// 🔒 Шифруем lastMessage перед сохранением
|
||||
val encryptedLastMessage = CryptoManager.encryptWithPassword(lastMessage, privateKey)
|
||||
// 🔥 КРИТИЧНО: Используем updateDialogFromMessages который пересчитывает счетчики
|
||||
// напрямую из таблицы messages, как в Архиве!
|
||||
dialogDao.updateDialogFromMessages(account, opponent)
|
||||
|
||||
val existingDialog = dialogDao.getDialog(account, opponent)
|
||||
|
||||
if (existingDialog != null) {
|
||||
// Обновляем последнее сообщение
|
||||
dialogDao.updateLastMessage(account, opponent, encryptedLastMessage, timestamp)
|
||||
} else {
|
||||
// Создаём новый диалог
|
||||
dialogDao.insertDialog(DialogEntity(
|
||||
account = account,
|
||||
opponentKey = opponent,
|
||||
opponentTitle = opponentTitle,
|
||||
opponentUsername = opponentUsername,
|
||||
lastMessage = encryptedLastMessage,
|
||||
lastMessageTimestamp = timestamp
|
||||
))
|
||||
}
|
||||
Log.d(TAG, "✅ Dialog saved/updated from messages table")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Dialog save error", e)
|
||||
}
|
||||
@@ -1050,34 +1037,14 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
*/
|
||||
private suspend fun updateDialog(opponentKey: String, lastMessage: String, timestamp: Long, incrementUnread: Boolean) {
|
||||
val account = myPublicKey ?: return
|
||||
val privateKey = myPrivateKey ?: return
|
||||
|
||||
try {
|
||||
// 🔒 Шифруем lastMessage для диалога
|
||||
val encryptedLastMessage = CryptoManager.encryptWithPassword(lastMessage, privateKey)
|
||||
// 🔥 КРИТИЧНО: Используем updateDialogFromMessages который пересчитывает счетчики
|
||||
// напрямую из таблицы messages, как в Архиве!
|
||||
// Это гарантирует что unread_count всегда соответствует реальному количеству непрочитанных
|
||||
dialogDao.updateDialogFromMessages(account, opponentKey)
|
||||
|
||||
val existingDialog = dialogDao.getDialog(account, opponentKey)
|
||||
|
||||
if (existingDialog != null) {
|
||||
// Обновляем последнее сообщение
|
||||
dialogDao.updateLastMessage(account, opponentKey, encryptedLastMessage, timestamp)
|
||||
|
||||
// Инкрементируем непрочитанные если нужно
|
||||
if (incrementUnread) {
|
||||
dialogDao.incrementUnreadCount(account, opponentKey)
|
||||
}
|
||||
} else {
|
||||
// Создаём новый диалог
|
||||
dialogDao.insertDialog(DialogEntity(
|
||||
account = account,
|
||||
opponentKey = opponentKey,
|
||||
opponentTitle = opponentTitle,
|
||||
opponentUsername = opponentUsername,
|
||||
lastMessage = encryptedLastMessage, // 🔒 Зашифрованный
|
||||
lastMessageTimestamp = timestamp,
|
||||
unreadCount = if (incrementUnread) 1 else 0
|
||||
))
|
||||
}
|
||||
Log.d(TAG, "✅ Dialog updated from messages table for $opponentKey")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "updateDialog error", e)
|
||||
}
|
||||
@@ -1252,12 +1219,13 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
if (lastIncoming.timestamp.time <= lastReadMessageTimestamp) return
|
||||
|
||||
|
||||
// Отмечаем в БД и очищаем счетчик непрочитанных
|
||||
// Отмечаем в БД и пересчитываем счетчики
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val dialogKey = getDialogKey(account, opponent)
|
||||
messageDao.markDialogAsRead(account, dialogKey)
|
||||
dialogDao.clearUnreadCount(account, opponent)
|
||||
// 🔥 Пересчитываем счетчики из messages
|
||||
dialogDao.updateDialogFromMessages(account, opponent)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Mark as read error", e)
|
||||
}
|
||||
|
||||
@@ -54,14 +54,20 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
|
||||
* Установить текущий аккаунт и загрузить диалоги
|
||||
*/
|
||||
fun setAccount(publicKey: String, privateKey: String) {
|
||||
if (currentAccount == publicKey) return
|
||||
if (currentAccount == publicKey) {
|
||||
android.util.Log.d("ChatsListViewModel", "⚠️ setAccount called again for same account, skipping")
|
||||
return
|
||||
}
|
||||
currentAccount = publicKey
|
||||
currentPrivateKey = privateKey
|
||||
|
||||
android.util.Log.d("ChatsListViewModel", "✅ Setting up dialogs Flow for account: ${publicKey.take(16)}...")
|
||||
|
||||
viewModelScope.launch {
|
||||
dialogDao.getDialogsFlow(publicKey)
|
||||
.flowOn(Dispatchers.IO) // 🚀 Flow работает на IO
|
||||
.map { dialogsList ->
|
||||
android.util.Log.d("ChatsListViewModel", "📋 Dialogs Flow emitted: ${dialogsList.size} dialogs")
|
||||
// 🔓 Расшифровываем lastMessage на IO потоке (PBKDF2 - тяжелая операция!)
|
||||
dialogsList.map { dialog ->
|
||||
val decryptedLastMessage = try {
|
||||
@@ -91,7 +97,9 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
|
||||
}
|
||||
}
|
||||
.flowOn(Dispatchers.Default) // 🚀 map выполняется на Default (CPU)
|
||||
.flowOn(Dispatchers.Main) // 🎯 КРИТИЧНО: Обновляем UI на главном потоке!
|
||||
.collect { decryptedDialogs ->
|
||||
android.util.Log.d("ChatsListViewModel", "✅ Updated UI with ${decryptedDialogs.size} decrypted dialogs")
|
||||
_dialogs.value = decryptedDialogs
|
||||
|
||||
// 🟢 Подписываемся на онлайн-статусы всех собеседников
|
||||
@@ -125,6 +133,7 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
|
||||
|
||||
/**
|
||||
* Создать или обновить диалог после отправки/получения сообщения
|
||||
* 🔥 Используем updateDialogFromMessages для пересчета счетчиков из messages
|
||||
*/
|
||||
suspend fun upsertDialog(
|
||||
opponentKey: String,
|
||||
@@ -136,31 +145,20 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
|
||||
isOnline: Int = 0
|
||||
) {
|
||||
if (currentAccount.isEmpty()) return
|
||||
val privateKey = currentPrivateKey ?: return
|
||||
|
||||
// 🔒 Шифруем lastMessage перед сохранением
|
||||
val encryptedLastMessage = CryptoManager.encryptWithPassword(lastMessage, privateKey)
|
||||
try {
|
||||
// 🔥 КРИТИЧНО: Используем updateDialogFromMessages который пересчитывает счетчики
|
||||
// напрямую из таблицы messages, как в Архиве!
|
||||
dialogDao.updateDialogFromMessages(currentAccount, opponentKey)
|
||||
|
||||
val existingDialog = dialogDao.getDialog(currentAccount, opponentKey)
|
||||
|
||||
if (existingDialog != null) {
|
||||
// Обновляем
|
||||
dialogDao.updateLastMessage(currentAccount, opponentKey, encryptedLastMessage, timestamp)
|
||||
// Обновляем информацию о собеседнике если есть
|
||||
if (opponentTitle.isNotEmpty()) {
|
||||
dialogDao.updateOpponentInfo(currentAccount, opponentKey, opponentTitle, opponentUsername, verified)
|
||||
}
|
||||
} else {
|
||||
// Создаём новый
|
||||
dialogDao.insertDialog(DialogEntity(
|
||||
account = currentAccount,
|
||||
opponentKey = opponentKey,
|
||||
opponentTitle = opponentTitle,
|
||||
opponentUsername = opponentUsername,
|
||||
lastMessage = encryptedLastMessage, // 🔒 Зашифрованный
|
||||
lastMessageTimestamp = timestamp,
|
||||
verified = verified,
|
||||
isOnline = isOnline
|
||||
))
|
||||
|
||||
android.util.Log.d("ChatsListViewModel", "✅ Dialog upserted from messages table")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("ChatsListViewModel", "Error upserting dialog", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user