feat: Enhance image attachment handling with local file management and download status logic
This commit is contained in:
@@ -45,6 +45,7 @@ import com.rosetta.messenger.R
|
|||||||
import com.rosetta.messenger.repository.AvatarRepository
|
import com.rosetta.messenger.repository.AvatarRepository
|
||||||
import com.rosetta.messenger.ui.chats.models.MessageStatus
|
import com.rosetta.messenger.ui.chats.models.MessageStatus
|
||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
|
import com.rosetta.messenger.utils.AttachmentFileManager
|
||||||
import com.rosetta.messenger.utils.AvatarFileManager
|
import com.rosetta.messenger.utils.AvatarFileManager
|
||||||
import com.vanniktech.blurhash.BlurHash
|
import com.vanniktech.blurhash.BlurHash
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -98,6 +99,7 @@ fun MessageAttachments(
|
|||||||
attachments = imageAttachments,
|
attachments = imageAttachments,
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -152,6 +154,7 @@ fun ImageCollage(
|
|||||||
attachments: List<MessageAttachment>,
|
attachments: List<MessageAttachment>,
|
||||||
chachaKey: String,
|
chachaKey: String,
|
||||||
privateKey: String,
|
privateKey: String,
|
||||||
|
senderPublicKey: String,
|
||||||
isOutgoing: Boolean,
|
isOutgoing: Boolean,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
timestamp: java.util.Date,
|
timestamp: java.util.Date,
|
||||||
@@ -175,6 +178,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[0],
|
attachment = attachments[0],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -194,6 +198,7 @@ fun ImageCollage(
|
|||||||
attachment = attachment,
|
attachment = attachment,
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -219,6 +224,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[0],
|
attachment = attachments[0],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -237,6 +243,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[1],
|
attachment = attachments[1],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -250,6 +257,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[2],
|
attachment = attachments[2],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -276,6 +284,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[0],
|
attachment = attachments[0],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -289,6 +298,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[1],
|
attachment = attachments[1],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -307,6 +317,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[2],
|
attachment = attachments[2],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -320,6 +331,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[3],
|
attachment = attachments[3],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -347,6 +359,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[0],
|
attachment = attachments[0],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -360,6 +373,7 @@ fun ImageCollage(
|
|||||||
attachment = attachments[1],
|
attachment = attachments[1],
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -384,6 +398,7 @@ fun ImageCollage(
|
|||||||
attachment = attachment,
|
attachment = attachment,
|
||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
|
senderPublicKey = senderPublicKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
@@ -407,12 +422,19 @@ fun ImageCollage(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Image attachment - Telegram style с blurhash placeholder
|
* Image attachment - Telegram style с blurhash placeholder
|
||||||
|
*
|
||||||
|
* Логика определения статуса (как в Desktop):
|
||||||
|
* 1. Если preview НЕ содержит UUID → это локальный файл → DOWNLOADED
|
||||||
|
* 2. Если есть UUID (download tag) → проверяем файловую систему через AttachmentFileManager
|
||||||
|
* 3. Если файл есть локально → DOWNLOADED (загружаем из файла)
|
||||||
|
* 4. Иначе → NOT_DOWNLOADED (нужно скачать с CDN)
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ImageAttachment(
|
fun ImageAttachment(
|
||||||
attachment: MessageAttachment,
|
attachment: MessageAttachment,
|
||||||
chachaKey: String,
|
chachaKey: String,
|
||||||
privateKey: String,
|
privateKey: String,
|
||||||
|
senderPublicKey: String,
|
||||||
isOutgoing: Boolean,
|
isOutgoing: Boolean,
|
||||||
isDarkTheme: Boolean,
|
isDarkTheme: Boolean,
|
||||||
timestamp: java.util.Date,
|
timestamp: java.util.Date,
|
||||||
@@ -439,19 +461,36 @@ fun ImageAttachment(
|
|||||||
label = "progress"
|
label = "progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Определяем начальный статус и декодируем blurhash
|
// Определяем начальный статус и декодируем blurhash (как в Desktop calcDownloadStatus)
|
||||||
LaunchedEffect(attachment.id) {
|
LaunchedEffect(attachment.id) {
|
||||||
// Определяем статус
|
// Определяем статус (логика из Desktop useAttachment.ts)
|
||||||
downloadStatus = when {
|
withContext(Dispatchers.IO) {
|
||||||
attachment.blob.isNotEmpty() && !isDownloadTag(attachment.preview) -> {
|
downloadStatus = when {
|
||||||
// Blob уже есть в сообщении (локальный файл)
|
// 1. Если blob уже есть в памяти → DOWNLOADED
|
||||||
DownloadStatus.DOWNLOADED
|
attachment.blob.isNotEmpty() -> {
|
||||||
|
Log.d(TAG, "📦 Blob already in memory for ${attachment.id}")
|
||||||
|
DownloadStatus.DOWNLOADED
|
||||||
|
}
|
||||||
|
// 2. Если preview НЕ содержит UUID → это наш локальный файл → DOWNLOADED
|
||||||
|
!isDownloadTag(attachment.preview) -> {
|
||||||
|
Log.d(TAG, "📦 No download tag, local file for ${attachment.id}")
|
||||||
|
DownloadStatus.DOWNLOADED
|
||||||
|
}
|
||||||
|
// 3. Есть UUID (download tag) → проверяем файловую систему
|
||||||
|
else -> {
|
||||||
|
// Проверяем есть ли файл локально (как в Desktop: readFile(`m/${md5hash}`))
|
||||||
|
val hasLocal = AttachmentFileManager.hasAttachment(
|
||||||
|
context, attachment.id, senderPublicKey
|
||||||
|
)
|
||||||
|
if (hasLocal) {
|
||||||
|
Log.d(TAG, "📦 Found local file for ${attachment.id}")
|
||||||
|
DownloadStatus.DOWNLOADED
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "📥 Need to download ${attachment.id}")
|
||||||
|
DownloadStatus.NOT_DOWNLOADED
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isDownloadTag(attachment.preview) -> {
|
|
||||||
// Нужно скачать с CDN
|
|
||||||
DownloadStatus.NOT_DOWNLOADED
|
|
||||||
}
|
|
||||||
else -> DownloadStatus.DOWNLOADED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Декодируем blurhash для placeholder (если есть)
|
// Декодируем blurhash для placeholder (если есть)
|
||||||
@@ -473,10 +512,27 @@ fun ImageAttachment(
|
|||||||
Log.d(TAG, "⚠️ No valid blurhash preview (preview='${preview.take(20)}...', isDownloadTag=${isDownloadTag(preview)})")
|
Log.d(TAG, "⚠️ No valid blurhash preview (preview='${preview.take(20)}...', isDownloadTag=${isDownloadTag(preview)})")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Декодируем изображение если уже скачано
|
// Загружаем изображение если статус DOWNLOADED
|
||||||
if (downloadStatus == DownloadStatus.DOWNLOADED && attachment.blob.isNotEmpty()) {
|
if (downloadStatus == DownloadStatus.DOWNLOADED) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
imageBitmap = base64ToBitmap(attachment.blob)
|
// 1. Сначала пробуем blob из памяти
|
||||||
|
if (attachment.blob.isNotEmpty()) {
|
||||||
|
Log.d(TAG, "🖼️ Loading image from blob")
|
||||||
|
imageBitmap = base64ToBitmap(attachment.blob)
|
||||||
|
} else {
|
||||||
|
// 2. Читаем из файловой системы (как в Desktop getBlob)
|
||||||
|
Log.d(TAG, "🖼️ Loading image from local file")
|
||||||
|
val localBlob = AttachmentFileManager.readAttachment(
|
||||||
|
context, attachment.id, senderPublicKey, privateKey
|
||||||
|
)
|
||||||
|
if (localBlob != null) {
|
||||||
|
imageBitmap = base64ToBitmap(localBlob)
|
||||||
|
Log.d(TAG, "✅ Image loaded from local file")
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "⚠️ Failed to read local file, need to download")
|
||||||
|
downloadStatus = DownloadStatus.NOT_DOWNLOADED
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,6 +567,16 @@ fun ImageAttachment(
|
|||||||
if (decrypted != null) {
|
if (decrypted != null) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
imageBitmap = base64ToBitmap(decrypted)
|
imageBitmap = base64ToBitmap(decrypted)
|
||||||
|
|
||||||
|
// 💾 Сохраняем в файловую систему (как в Desktop)
|
||||||
|
AttachmentFileManager.saveAttachment(
|
||||||
|
context = context,
|
||||||
|
blob = decrypted,
|
||||||
|
attachmentId = attachment.id,
|
||||||
|
publicKey = senderPublicKey,
|
||||||
|
privateKey = privateKey
|
||||||
|
)
|
||||||
|
Log.d(TAG, "💾 Image saved to local storage")
|
||||||
}
|
}
|
||||||
downloadProgress = 1f
|
downloadProgress = 1f
|
||||||
downloadStatus = DownloadStatus.DOWNLOADED
|
downloadStatus = DownloadStatus.DOWNLOADED
|
||||||
|
|||||||
Reference in New Issue
Block a user