feat: Implement message forwarding feature with chat selection and re-encryption logic
This commit is contained in:
108
app/src/main/java/com/rosetta/messenger/data/ForwardManager.kt
Normal file
108
app/src/main/java/com/rosetta/messenger/data/ForwardManager.kt
Normal file
@@ -0,0 +1,108 @@
|
||||
package com.rosetta.messenger.data
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* 📨 Менеджер для пересылки сообщений (Forward)
|
||||
*
|
||||
* Логика как в десктопе:
|
||||
* 1. Пользователь выбирает сообщения в чате
|
||||
* 2. Нажимает Forward
|
||||
* 3. Открывается список чатов
|
||||
* 4. Выбирает чат куда переслать
|
||||
* 5. Переходит в выбранный чат с сообщениями в Reply панели (как Forward)
|
||||
*
|
||||
* Singleton для передачи данных между экранами
|
||||
*/
|
||||
object ForwardManager {
|
||||
|
||||
/**
|
||||
* Сообщение для пересылки
|
||||
*/
|
||||
data class ForwardMessage(
|
||||
val messageId: String,
|
||||
val text: String,
|
||||
val timestamp: Long,
|
||||
val isOutgoing: Boolean,
|
||||
val senderPublicKey: String, // publicKey отправителя сообщения
|
||||
val originalChatPublicKey: String // publicKey чата откуда пересылается
|
||||
)
|
||||
|
||||
// Сообщения для пересылки
|
||||
private val _forwardMessages = MutableStateFlow<List<ForwardMessage>>(emptyList())
|
||||
val forwardMessages: StateFlow<List<ForwardMessage>> = _forwardMessages.asStateFlow()
|
||||
|
||||
// Флаг показа выбора чата
|
||||
private val _showChatPicker = MutableStateFlow(false)
|
||||
val showChatPicker: StateFlow<Boolean> = _showChatPicker.asStateFlow()
|
||||
|
||||
// Выбранный чат (publicKey собеседника)
|
||||
private val _selectedChatPublicKey = MutableStateFlow<String?>(null)
|
||||
val selectedChatPublicKey: StateFlow<String?> = _selectedChatPublicKey.asStateFlow()
|
||||
|
||||
/**
|
||||
* Установить сообщения для пересылки и показать выбор чата
|
||||
*/
|
||||
fun setForwardMessages(
|
||||
messages: List<ForwardMessage>,
|
||||
showPicker: Boolean = true
|
||||
) {
|
||||
android.util.Log.d("ForwardManager", "📨 Setting forward messages: ${messages.size}")
|
||||
_forwardMessages.value = messages
|
||||
if (showPicker) {
|
||||
_showChatPicker.value = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбрать чат для пересылки
|
||||
*/
|
||||
fun selectChat(publicKey: String) {
|
||||
android.util.Log.d("ForwardManager", "📨 Selected chat: $publicKey")
|
||||
_selectedChatPublicKey.value = publicKey
|
||||
_showChatPicker.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть выбор чата (отмена)
|
||||
*/
|
||||
fun hideChatPicker() {
|
||||
android.util.Log.d("ForwardManager", "📨 Hide chat picker")
|
||||
_showChatPicker.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщения и очистить состояние
|
||||
* Вызывается при открытии выбранного чата
|
||||
*/
|
||||
fun consumeForwardMessages(): List<ForwardMessage> {
|
||||
val messages = _forwardMessages.value
|
||||
android.util.Log.d("ForwardManager", "📨 Consuming forward messages: ${messages.size}")
|
||||
return messages
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистить все данные (после применения или отмены)
|
||||
*/
|
||||
fun clear() {
|
||||
android.util.Log.d("ForwardManager", "📨 Clearing forward state")
|
||||
_forwardMessages.value = emptyList()
|
||||
_showChatPicker.value = false
|
||||
_selectedChatPublicKey.value = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить есть ли сообщения для пересылки
|
||||
*/
|
||||
fun hasForwardMessages(): Boolean = _forwardMessages.value.isNotEmpty()
|
||||
|
||||
/**
|
||||
* Проверить есть ли сообщения для конкретного чата
|
||||
*/
|
||||
fun hasForwardMessagesForChat(publicKey: String): Boolean {
|
||||
val selectedKey = _selectedChatPublicKey.value
|
||||
return selectedKey == publicKey && _forwardMessages.value.isNotEmpty()
|
||||
}
|
||||
}
|
||||
@@ -600,8 +600,11 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Сериализация attachments в JSON с расшифровкой MESSAGES blob
|
||||
* Для MESSAGES типа blob расшифровывается и сохраняется в preview (как в RN)
|
||||
* Сериализация attachments в JSON с RE-ENCRYPTION для хранения в БД
|
||||
* Для MESSAGES типа:
|
||||
* 1. Расшифровываем blob с ChaCha ключом сообщения
|
||||
* 2. Re-encrypt с приватным ключом (как в Desktop Архиве)
|
||||
* 3. Сохраняем зашифрованный blob в БД
|
||||
*/
|
||||
private fun serializeAttachmentsWithDecryption(
|
||||
attachments: List<MessageAttachment>,
|
||||
@@ -614,9 +617,10 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
for (attachment in attachments) {
|
||||
val jsonObj = JSONObject()
|
||||
|
||||
// Для MESSAGES типа расшифровываем blob
|
||||
// Для MESSAGES типа расшифровываем и re-encrypt
|
||||
if (attachment.type == AttachmentType.MESSAGES && attachment.blob.isNotEmpty()) {
|
||||
try {
|
||||
// 1. Расшифровываем с ChaCha ключом сообщения
|
||||
val decryptedBlob = MessageCrypto.decryptAttachmentBlob(
|
||||
attachment.blob,
|
||||
encryptedKey,
|
||||
@@ -624,11 +628,14 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
)
|
||||
|
||||
if (decryptedBlob != null) {
|
||||
// Сохраняем расшифрованный JSON в preview (как в RN)
|
||||
// 2. Re-encrypt с приватным ключом для хранения (как в Desktop Архиве)
|
||||
val reEncryptedBlob = CryptoManager.encryptWithPassword(decryptedBlob, privateKey)
|
||||
|
||||
// 3. Сохраняем ЗАШИФРОВАННЫЙ blob в БД
|
||||
jsonObj.put("id", attachment.id)
|
||||
jsonObj.put("blob", decryptedBlob) // Расшифрованный JSON
|
||||
jsonObj.put("blob", reEncryptedBlob) // 🔒 Зашифрован приватным ключом!
|
||||
jsonObj.put("type", attachment.type.value)
|
||||
jsonObj.put("preview", decryptedBlob) // Для совместимости
|
||||
jsonObj.put("preview", attachment.preview)
|
||||
jsonObj.put("width", attachment.width)
|
||||
jsonObj.put("height", attachment.height)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user