feat: Add timestamp and message status to MessageBubble and ImageAttachment components

This commit is contained in:
k1ngsterr1
2026-01-25 02:54:56 +05:00
parent c8214cdfa3
commit 3608af99c3
2 changed files with 102 additions and 5 deletions

View File

@@ -3,13 +3,14 @@ package com.rosetta.messenger.ui.chats.components
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@@ -23,18 +24,23 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.geometry.CornerRadius
import compose.icons.TablerIcons
import compose.icons.tablericons.*
import com.rosetta.messenger.crypto.CryptoManager import com.rosetta.messenger.crypto.CryptoManager
import com.rosetta.messenger.crypto.MessageCrypto import com.rosetta.messenger.crypto.MessageCrypto
import com.rosetta.messenger.network.AttachmentType import com.rosetta.messenger.network.AttachmentType
import com.rosetta.messenger.network.MessageAttachment import com.rosetta.messenger.network.MessageAttachment
import com.rosetta.messenger.network.TransportManager import com.rosetta.messenger.network.TransportManager
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.onboarding.PrimaryBlue import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.rosetta.messenger.utils.AvatarFileManager import com.rosetta.messenger.utils.AvatarFileManager
import com.vanniktech.blurhash.BlurHash import com.vanniktech.blurhash.BlurHash
@@ -67,6 +73,8 @@ fun MessageAttachments(
isOutgoing: Boolean, isOutgoing: Boolean,
isDarkTheme: Boolean, isDarkTheme: Boolean,
senderPublicKey: String, senderPublicKey: String,
timestamp: java.util.Date,
messageStatus: MessageStatus = MessageStatus.READ,
avatarRepository: AvatarRepository? = null, avatarRepository: AvatarRepository? = null,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
@@ -84,7 +92,9 @@ fun MessageAttachments(
chachaKey = chachaKey, chachaKey = chachaKey,
privateKey = privateKey, privateKey = privateKey,
isOutgoing = isOutgoing, isOutgoing = isOutgoing,
isDarkTheme = isDarkTheme isDarkTheme = isDarkTheme,
timestamp = timestamp,
messageStatus = messageStatus
) )
} }
AttachmentType.FILE -> { AttachmentType.FILE -> {
@@ -124,7 +134,9 @@ fun ImageAttachment(
chachaKey: String, chachaKey: String,
privateKey: String, privateKey: String,
isOutgoing: Boolean, isOutgoing: Boolean,
isDarkTheme: Boolean isDarkTheme: Boolean,
timestamp: java.util.Date,
messageStatus: MessageStatus = MessageStatus.READ
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -224,12 +236,20 @@ fun ImageAttachment(
} }
} }
// Telegram-style image с blurhash placeholder // Telegram-style image с blurhash placeholder и тонким бордером
val timeFormat = remember { java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault()) }
val borderColor = if (isOutgoing) {
Color.White.copy(alpha = 0.15f)
} else {
if (isDarkTheme) Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.08f)
}
Box( Box(
modifier = Modifier modifier = Modifier
.widthIn(min = 180.dp, max = 260.dp) .widthIn(min = 180.dp, max = 260.dp)
.heightIn(min = 140.dp, max = 300.dp) .heightIn(min = 140.dp, max = 300.dp)
.clip(RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(12.dp))
.background(Color.Transparent)
.clickable { .clickable {
when (downloadStatus) { when (downloadStatus) {
DownloadStatus.NOT_DOWNLOADED -> download() DownloadStatus.NOT_DOWNLOADED -> download()
@@ -272,6 +292,81 @@ fun ImageAttachment(
} }
} }
// Тонкий бордер как в Telegram (просто через border modifier)
Box(
modifier = Modifier
.fillMaxSize()
.border(
width = 1.dp,
color = borderColor,
shape = RoundedCornerShape(12.dp)
)
)
// Время в правом нижнем углу (только если изображение загружено)
if (downloadStatus == DownloadStatus.DOWNLOADED) {
Box(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(8.dp)
.background(
Color.Black.copy(alpha = 0.5f),
shape = RoundedCornerShape(10.dp)
)
.padding(horizontal = 6.dp, vertical = 3.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
Text(
text = timeFormat.format(timestamp),
color = Color.White,
fontSize = 11.sp,
fontWeight = FontWeight.Medium
)
if (isOutgoing) {
// Статус доставки для исходящих
when (messageStatus) {
MessageStatus.SENDING -> {
Icon(
compose.icons.TablerIcons.Clock,
contentDescription = null,
tint = Color.White.copy(alpha = 0.7f),
modifier = Modifier.size(14.dp)
)
}
MessageStatus.SENT -> {
Icon(
compose.icons.TablerIcons.Check,
contentDescription = null,
tint = Color.White.copy(alpha = 0.7f),
modifier = Modifier.size(14.dp)
)
}
MessageStatus.DELIVERED -> {
Icon(
compose.icons.TablerIcons.Checks,
contentDescription = null,
tint = Color.White.copy(alpha = 0.7f),
modifier = Modifier.size(14.dp)
)
}
MessageStatus.READ -> {
Icon(
compose.icons.TablerIcons.Checks,
contentDescription = null,
tint = Color(0xFF4FC3F7),
modifier = Modifier.size(14.dp)
)
}
else -> {}
}
}
}
}
}
// Оверлей для статуса скачивания // Оверлей для статуса скачивания
if (downloadStatus != DownloadStatus.DOWNLOADED) { if (downloadStatus != DownloadStatus.DOWNLOADED) {
Box( Box(

View File

@@ -345,7 +345,9 @@ fun MessageBubble(
privateKey = privateKey, privateKey = privateKey,
isOutgoing = message.isOutgoing, isOutgoing = message.isOutgoing,
isDarkTheme = isDarkTheme, isDarkTheme = isDarkTheme,
senderPublicKey = senderPublicKey senderPublicKey = senderPublicKey,
timestamp = message.timestamp,
messageStatus = message.status
) )
if (message.text.isNotEmpty()) { if (message.text.isNotEmpty()) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))