Починил optimistic и сохранение групповых фото при отправке
This commit is contained in:
@@ -3500,15 +3500,101 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
return
|
||||
}
|
||||
|
||||
val recipient = opponentKey
|
||||
val sender = myPublicKey
|
||||
val privateKey = myPrivateKey
|
||||
val context = getApplication<Application>()
|
||||
val groupDebugId = UUID.randomUUID().toString().replace("-", "").take(8)
|
||||
|
||||
if (recipient == null || sender == null || privateKey == null) {
|
||||
ProtocolManager.addLog(
|
||||
"❌ IMG-GROUP $groupDebugId | aborted: missing keys (recipient=${recipient != null}, sender=${sender != null}, private=${privateKey != null})"
|
||||
)
|
||||
return
|
||||
}
|
||||
if (isSending) {
|
||||
ProtocolManager.addLog("⚠️ IMG-GROUP $groupDebugId | skipped: another send in progress")
|
||||
return
|
||||
}
|
||||
|
||||
isSending = true
|
||||
|
||||
val messageId = UUID.randomUUID().toString().replace("-", "").take(32)
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val text = caption.trim()
|
||||
val attachmentIds = imageUris.indices.map { index -> "img_${timestamp}_$index" }
|
||||
|
||||
val optimisticAttachments =
|
||||
imageUris.mapIndexed { index, uri ->
|
||||
MessageAttachment(
|
||||
id = attachmentIds[index],
|
||||
blob = "",
|
||||
type = AttachmentType.IMAGE,
|
||||
preview = "",
|
||||
width = 0,
|
||||
height = 0,
|
||||
localUri = uri.toString()
|
||||
)
|
||||
}
|
||||
|
||||
addMessageSafely(
|
||||
ChatMessage(
|
||||
id = messageId,
|
||||
text = text,
|
||||
isOutgoing = true,
|
||||
timestamp = Date(timestamp),
|
||||
status = MessageStatus.SENDING,
|
||||
attachments = optimisticAttachments
|
||||
)
|
||||
)
|
||||
_inputText.value = ""
|
||||
|
||||
ProtocolManager.addLog(
|
||||
"📸 IMG-GROUP $groupDebugId | prepare start: count=${imageUris.size}, captionLen=${caption.trim().length}"
|
||||
)
|
||||
|
||||
backgroundUploadScope.launch {
|
||||
try {
|
||||
val optimisticAttachmentsJson =
|
||||
JSONArray().apply {
|
||||
imageUris.forEachIndexed { index, uri ->
|
||||
put(
|
||||
JSONObject().apply {
|
||||
put("id", attachmentIds[index])
|
||||
put("type", AttachmentType.IMAGE.value)
|
||||
put("preview", "")
|
||||
put("blob", "")
|
||||
put("width", 0)
|
||||
put("height", 0)
|
||||
put("localUri", uri.toString())
|
||||
}
|
||||
)
|
||||
}
|
||||
}.toString()
|
||||
|
||||
saveMessageToDatabase(
|
||||
messageId = messageId,
|
||||
text = text,
|
||||
encryptedContent = "",
|
||||
encryptedKey = "",
|
||||
timestamp = timestamp,
|
||||
isFromMe = true,
|
||||
delivered = 0,
|
||||
attachmentsJson = optimisticAttachmentsJson,
|
||||
opponentPublicKey = recipient
|
||||
)
|
||||
|
||||
saveDialog(
|
||||
lastMessage = if (text.isNotEmpty()) text else "📷 ${imageUris.size} photos",
|
||||
timestamp = timestamp,
|
||||
opponentPublicKey = recipient
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
ProtocolManager.addLog("⚠️ IMG-GROUP $groupDebugId | optimistic DB save skipped (non-fatal)")
|
||||
}
|
||||
|
||||
val preparedImages =
|
||||
imageUris.mapIndexedNotNull { index, uri ->
|
||||
imageUris.mapIndexed { index, uri ->
|
||||
val (width, height) =
|
||||
com.rosetta.messenger.utils.MediaUtils.getImageDimensions(
|
||||
context,
|
||||
@@ -3523,7 +3609,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
ProtocolManager.addLog(
|
||||
"❌ IMG-GROUP $groupDebugId | item#$index base64 conversion failed"
|
||||
)
|
||||
return@mapIndexedNotNull null
|
||||
throw IllegalStateException(
|
||||
"group item#$index base64 conversion failed"
|
||||
)
|
||||
}
|
||||
val blurhash =
|
||||
com.rosetta.messenger.utils.MediaUtils.generateBlurhash(
|
||||
@@ -3533,26 +3621,156 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
ProtocolManager.addLog(
|
||||
"📸 IMG-GROUP $groupDebugId | item#$index prepared: ${width}x$height, base64Len=${imageBase64.length}, blurhashLen=${blurhash.length}"
|
||||
)
|
||||
ImageData(
|
||||
base64 = imageBase64,
|
||||
blurhash = blurhash,
|
||||
width = width,
|
||||
height = height
|
||||
)
|
||||
index to
|
||||
ImageData(
|
||||
base64 = imageBase64,
|
||||
blurhash = blurhash,
|
||||
width = width,
|
||||
height = height
|
||||
)
|
||||
}
|
||||
|
||||
if (preparedImages.isEmpty()) {
|
||||
ProtocolManager.addLog(
|
||||
"❌ IMG-GROUP $groupDebugId | no prepared images, send canceled"
|
||||
)
|
||||
updateMessageStatusInDb(messageId, DeliveryStatus.ERROR.value)
|
||||
withContext(Dispatchers.Main) { updateMessageStatus(messageId, MessageStatus.ERROR) }
|
||||
isSending = false
|
||||
return@launch
|
||||
}
|
||||
ProtocolManager.addLog(
|
||||
"📸 IMG-GROUP $groupDebugId | prepare done: ready=${preparedImages.size}"
|
||||
)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
sendImageGroup(preparedImages, caption)
|
||||
try {
|
||||
val groupStartedAt = System.currentTimeMillis()
|
||||
val encryptionContext =
|
||||
buildEncryptionContext(
|
||||
plaintext = text,
|
||||
recipient = recipient,
|
||||
privateKey = privateKey
|
||||
) ?: throw IllegalStateException("Cannot resolve chat encryption context")
|
||||
val encryptedContent = encryptionContext.encryptedContent
|
||||
val encryptedKey = encryptionContext.encryptedKey
|
||||
val aesChachaKey = encryptionContext.aesChachaKey
|
||||
val privateKeyHash = CryptoManager.generatePrivateKeyHash(privateKey)
|
||||
val isSavedMessages = sender == recipient
|
||||
|
||||
val networkAttachments = mutableListOf<MessageAttachment>()
|
||||
val finalDbAttachments = JSONArray()
|
||||
val finalAttachmentsById = mutableMapOf<String, MessageAttachment>()
|
||||
|
||||
for ((originalIndex, imageData) in preparedImages) {
|
||||
val attachmentId = attachmentIds[originalIndex]
|
||||
val encryptedImageBlob =
|
||||
encryptAttachmentPayload(imageData.base64, encryptionContext)
|
||||
val uploadTag =
|
||||
if (!isSavedMessages) {
|
||||
TransportManager.uploadFile(attachmentId, encryptedImageBlob)
|
||||
} else {
|
||||
""
|
||||
}
|
||||
val previewWithTag =
|
||||
if (uploadTag.isNotEmpty()) "$uploadTag::${imageData.blurhash}"
|
||||
else imageData.blurhash
|
||||
|
||||
AttachmentFileManager.saveAttachment(
|
||||
context = context,
|
||||
blob = imageData.base64,
|
||||
attachmentId = attachmentId,
|
||||
publicKey = sender,
|
||||
privateKey = privateKey
|
||||
)
|
||||
|
||||
val finalAttachment =
|
||||
MessageAttachment(
|
||||
id = attachmentId,
|
||||
blob = if (uploadTag.isNotEmpty()) "" else encryptedImageBlob,
|
||||
type = AttachmentType.IMAGE,
|
||||
preview = previewWithTag,
|
||||
width = imageData.width,
|
||||
height = imageData.height,
|
||||
localUri = ""
|
||||
)
|
||||
networkAttachments.add(finalAttachment)
|
||||
finalAttachmentsById[attachmentId] = finalAttachment
|
||||
|
||||
finalDbAttachments.put(
|
||||
JSONObject().apply {
|
||||
put("id", attachmentId)
|
||||
put("type", AttachmentType.IMAGE.value)
|
||||
put("preview", previewWithTag)
|
||||
put("blob", "")
|
||||
put("width", imageData.width)
|
||||
put("height", imageData.height)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val packet =
|
||||
PacketMessage().apply {
|
||||
fromPublicKey = sender
|
||||
toPublicKey = recipient
|
||||
content = encryptedContent
|
||||
chachaKey = encryptedKey
|
||||
this.aesChachaKey = aesChachaKey
|
||||
this.timestamp = timestamp
|
||||
this.privateKey = privateKeyHash
|
||||
this.messageId = messageId
|
||||
attachments = networkAttachments
|
||||
}
|
||||
|
||||
if (!isSavedMessages) {
|
||||
ProtocolManager.send(packet)
|
||||
}
|
||||
|
||||
updateMessageStatusAndAttachmentsInDb(
|
||||
messageId = messageId,
|
||||
delivered = 1,
|
||||
attachmentsJson = finalDbAttachments.toString()
|
||||
)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
_messages.value =
|
||||
_messages.value.map { msg ->
|
||||
if (msg.id != messageId) return@map msg
|
||||
msg.copy(
|
||||
status = MessageStatus.SENT,
|
||||
attachments =
|
||||
msg.attachments.map { current ->
|
||||
val final = finalAttachmentsById[current.id]
|
||||
if (final != null) {
|
||||
current.copy(
|
||||
preview = final.preview,
|
||||
width = final.width,
|
||||
height = final.height,
|
||||
localUri = ""
|
||||
)
|
||||
} else {
|
||||
current.copy(localUri = "")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
updateCacheFromCurrentMessages()
|
||||
}
|
||||
|
||||
saveDialog(
|
||||
lastMessage = if (text.isNotEmpty()) text else "📷 ${imageUris.size} photos",
|
||||
timestamp = timestamp,
|
||||
opponentPublicKey = recipient
|
||||
)
|
||||
logPhotoPipeline(
|
||||
messageId,
|
||||
"group-from-uri completed; totalElapsed=${System.currentTimeMillis() - groupStartedAt}ms"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logPhotoPipelineError(messageId, "group-from-uri", e)
|
||||
updateMessageStatusInDb(messageId, DeliveryStatus.ERROR.value)
|
||||
withContext(Dispatchers.Main) { updateMessageStatus(messageId, MessageStatus.ERROR) }
|
||||
} finally {
|
||||
isSending = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user