feat: Enhance reply functionality with public and private key support for attachment decryption
This commit is contained in:
@@ -851,7 +851,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
messageId = "",
|
||||
senderName = opponentTitle.ifEmpty { opponentUsername.ifEmpty { "User" } },
|
||||
text = replyText,
|
||||
isFromMe = false // Мы не знаем кто автор из fallback формата
|
||||
isFromMe = false, // Мы не знаем кто автор из fallback формата
|
||||
senderPublicKey = opponentKey ?: "",
|
||||
recipientPrivateKey = myPrivateKey ?: ""
|
||||
),
|
||||
mainText
|
||||
)
|
||||
@@ -1005,7 +1007,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
senderName = if (isReplyFromMe) "You" else opponentTitle.ifEmpty { opponentUsername.ifEmpty { "User" } },
|
||||
text = replyText,
|
||||
isFromMe = isReplyFromMe,
|
||||
attachments = originalAttachments
|
||||
attachments = originalAttachments,
|
||||
senderPublicKey = if (isReplyFromMe) myPublicKey ?: "" else opponentKey ?: "",
|
||||
recipientPrivateKey = myPrivateKey ?: ""
|
||||
)
|
||||
return result
|
||||
} else {
|
||||
@@ -1171,7 +1175,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
text = firstReply.text,
|
||||
isFromMe = firstReply.isOutgoing,
|
||||
isForwarded = isForward,
|
||||
attachments = replyAttachments
|
||||
attachments = replyAttachments,
|
||||
senderPublicKey = if (firstReply.isOutgoing) myPublicKey ?: "" else opponentKey ?: "",
|
||||
recipientPrivateKey = myPrivateKey ?: ""
|
||||
)
|
||||
} else null
|
||||
|
||||
|
||||
@@ -39,13 +39,19 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.rosetta.messenger.crypto.MessageCrypto
|
||||
import com.rosetta.messenger.network.AttachmentType
|
||||
import com.rosetta.messenger.network.MessageAttachment
|
||||
import com.rosetta.messenger.network.TransportManager
|
||||
import com.rosetta.messenger.repository.AvatarRepository
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiText
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.chats.models.*
|
||||
import com.rosetta.messenger.ui.chats.utils.*
|
||||
import com.rosetta.messenger.utils.AttachmentFileManager
|
||||
import com.vanniktech.blurhash.BlurHash
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -598,6 +604,7 @@ fun ReplyBubble(
|
||||
isDarkTheme: Boolean,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
val backgroundColor = if (isOutgoing) {
|
||||
Color.Black.copy(alpha = 0.15f)
|
||||
} else {
|
||||
@@ -616,24 +623,56 @@ fun ReplyBubble(
|
||||
val imageAttachment = replyData.attachments.firstOrNull { it.type == AttachmentType.IMAGE }
|
||||
val hasImage = imageAttachment != null
|
||||
|
||||
// Декодируем blurhash для превью
|
||||
var previewBitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
// Загружаем полноценную картинку вместо blur preview
|
||||
var imageBitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
|
||||
LaunchedEffect(imageAttachment?.preview) {
|
||||
if (imageAttachment != null && imageAttachment.preview.isNotEmpty()) {
|
||||
LaunchedEffect(imageAttachment?.id) {
|
||||
if (imageAttachment != null) {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
// Получаем blurhash из preview (может быть в формате "tag::blurhash")
|
||||
val blurhash = if (imageAttachment.preview.contains("::")) {
|
||||
imageAttachment.preview.split("::").lastOrNull() ?: ""
|
||||
// Пробуем сначала из blob
|
||||
if (imageAttachment.blob.isNotEmpty()) {
|
||||
val decoded = try {
|
||||
val cleanBase64 = if (imageAttachment.blob.contains(",")) {
|
||||
imageAttachment.blob.substringAfter(",")
|
||||
} else {
|
||||
imageAttachment.preview
|
||||
imageAttachment.blob
|
||||
}
|
||||
if (blurhash.isNotEmpty() && !blurhash.startsWith("http")) {
|
||||
previewBitmap = BlurHash.decode(blurhash, 40, 40)
|
||||
val decodedBytes = Base64.decode(cleanBase64, Base64.DEFAULT)
|
||||
BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
if (decoded != null) {
|
||||
imageBitmap = decoded
|
||||
return@withContext
|
||||
}
|
||||
}
|
||||
|
||||
// Если blob нет - загружаем из локального файла
|
||||
val localBlob = AttachmentFileManager.readAttachment(
|
||||
context,
|
||||
imageAttachment.id,
|
||||
replyData.senderPublicKey,
|
||||
replyData.recipientPrivateKey
|
||||
)
|
||||
|
||||
if (localBlob != null) {
|
||||
val decoded = try {
|
||||
val cleanBase64 = if (localBlob.contains(",")) {
|
||||
localBlob.substringAfter(",")
|
||||
} else {
|
||||
localBlob
|
||||
}
|
||||
val decodedBytes = Base64.decode(cleanBase64, Base64.DEFAULT)
|
||||
BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
imageBitmap = decoded
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Ignore blurhash decode errors
|
||||
Log.e("ReplyBubble", "Failed to load image: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -696,9 +735,9 @@ fun ReplyBubble(
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(Color.Gray.copy(alpha = 0.3f))
|
||||
) {
|
||||
if (previewBitmap != null) {
|
||||
if (imageBitmap != null) {
|
||||
Image(
|
||||
bitmap = previewBitmap!!.asImageBitmap(),
|
||||
bitmap = imageBitmap!!.asImageBitmap(),
|
||||
contentDescription = "Photo preview",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
|
||||
@@ -23,7 +23,9 @@ data class ReplyData(
|
||||
val text: String,
|
||||
val isFromMe: Boolean,
|
||||
val isForwarded: Boolean = false,
|
||||
val attachments: List<MessageAttachment> = emptyList() // 🖼️ Для превью фото в reply
|
||||
val attachments: List<MessageAttachment> = emptyList(), // 🖼️ Для превью фото в reply
|
||||
val senderPublicKey: String = "", // Для расшифровки attachments в reply
|
||||
val recipientPrivateKey: String = "" // Для расшифровки attachments в reply
|
||||
)
|
||||
|
||||
/** Legacy message model (for compatibility) */
|
||||
|
||||
Reference in New Issue
Block a user