feat: Enhance FileAttachment component with timestamp and message status display

This commit is contained in:
k1ngsterr1
2026-01-26 13:07:01 +05:00
parent 87932c5fab
commit 99a1156e96

View File

@@ -28,6 +28,7 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -40,6 +41,7 @@ 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.R
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.ui.chats.models.MessageStatus
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
@@ -104,7 +106,9 @@ fun MessageAttachments(
chachaKey = chachaKey,
privateKey = privateKey,
isOutgoing = isOutgoing,
isDarkTheme = isDarkTheme
isDarkTheme = isDarkTheme,
timestamp = timestamp,
messageStatus = messageStatus
)
}
AttachmentType.AVATAR -> {
@@ -490,7 +494,9 @@ fun FileAttachment(
chachaKey: String,
privateKey: String,
isOutgoing: Boolean,
isDarkTheme: Boolean
isDarkTheme: Boolean,
timestamp: java.util.Date,
messageStatus: MessageStatus = MessageStatus.READ
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
@@ -556,98 +562,101 @@ fun FileAttachment(
}
// Telegram-style файл - как в desktop: без внутреннего фона, просто иконка + текст
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(enabled = downloadStatus == DownloadStatus.NOT_DOWNLOADED || downloadStatus == DownloadStatus.ERROR) {
download()
}
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
Box(
modifier = Modifier.fillMaxWidth()
) {
// File icon с индикатором прогресса - круглая иконка как в desktop
Box(
modifier = Modifier.size(40.dp),
contentAlignment = Alignment.Center
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(enabled = downloadStatus == DownloadStatus.NOT_DOWNLOADED || downloadStatus == DownloadStatus.ERROR) {
download()
}
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Круглый фон иконки
// File icon с индикатором прогресса - круглая иконка как в desktop
Box(
modifier = Modifier
.fillMaxSize()
.clip(CircleShape)
.background(
if (downloadStatus == DownloadStatus.ERROR) Color(0xFFE53935)
else PrimaryBlue
),
modifier = Modifier.size(40.dp),
contentAlignment = Alignment.Center
) {
when (downloadStatus) {
DownloadStatus.DOWNLOADING, DownloadStatus.DECRYPTING -> {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = Color.White,
strokeWidth = 2.dp
)
}
DownloadStatus.NOT_DOWNLOADED -> {
Icon(
Icons.Default.ArrowDownward,
contentDescription = "Download",
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
DownloadStatus.DOWNLOADED -> {
Icon(
Icons.Default.InsertDriveFile,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
DownloadStatus.ERROR -> {
Icon(
Icons.Default.Close,
contentDescription = "Error",
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
else -> {
Icon(
Icons.Default.InsertDriveFile,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(20.dp)
)
// Круглый фон иконки
Box(
modifier = Modifier
.fillMaxSize()
.clip(CircleShape)
.background(
if (downloadStatus == DownloadStatus.ERROR) Color(0xFFE53935)
else PrimaryBlue
),
contentAlignment = Alignment.Center
) {
when (downloadStatus) {
DownloadStatus.DOWNLOADING, DownloadStatus.DECRYPTING -> {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = Color.White,
strokeWidth = 2.dp
)
}
DownloadStatus.NOT_DOWNLOADED -> {
Icon(
Icons.Default.ArrowDownward,
contentDescription = "Download",
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
DownloadStatus.DOWNLOADED -> {
Icon(
Icons.Default.InsertDriveFile,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
DownloadStatus.ERROR -> {
Icon(
Icons.Default.Close,
contentDescription = "Error",
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
else -> {
Icon(
Icons.Default.InsertDriveFile,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(20.dp)
)
}
}
}
}
}
Spacer(modifier = Modifier.width(10.dp))
// File info
Column(modifier = Modifier.weight(1f)) {
Text(
text = fileName,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = if (isOutgoing) Color.White else (if (isDarkTheme) Color.White else Color.Black),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(2.dp))
// Размер файла и тип
val fileExtension = fileName.substringAfterLast('.', "").uppercase()
Text(
text = when (downloadStatus) {
DownloadStatus.DOWNLOADING -> "Downloading..."
DownloadStatus.DECRYPTING -> "Decrypting..."
DownloadStatus.ERROR -> "File expired"
else -> "${formatFileSize(fileSize)} $fileExtension"
},
Spacer(modifier = Modifier.width(10.dp))
// File info
Column(modifier = Modifier.weight(1f)) {
Text(
text = fileName,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = if (isOutgoing) Color.White else (if (isDarkTheme) Color.White else Color.Black),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(2.dp))
// Размер файла и тип
val fileExtension = fileName.substringAfterLast('.', "").uppercase()
Text(
text = when (downloadStatus) {
DownloadStatus.DOWNLOADING -> "Downloading..."
DownloadStatus.DECRYPTING -> "Decrypting..."
DownloadStatus.ERROR -> "File expired"
else -> "${formatFileSize(fileSize)} $fileExtension"
},
fontSize = 12.sp,
color = if (downloadStatus == DownloadStatus.ERROR) {
Color(0xFFE53935)
@@ -659,11 +668,69 @@ fun FileAttachment(
)
}
}
// Time and checkmarks (bottom-right overlay) for outgoing files
if (isOutgoing) {
Row(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(bottom = 6.dp, end = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
// Time
Text(
text = android.text.format.DateFormat.format("HH:mm", timestamp).toString(),
fontSize = 11.sp,
color = Color.White.copy(alpha = 0.7f)
)
Spacer(modifier = Modifier.width(4.dp))
// Checkmarks
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, MessageStatus.READ -> {
Icon(
compose.icons.TablerIcons.Checks,
contentDescription = null,
tint = if (messageStatus == MessageStatus.READ) {
Color(0xFF4FC3F7)
} else {
Color.White.copy(alpha = 0.7f)
},
modifier = Modifier.size(14.dp)
)
}
MessageStatus.ERROR -> {
Icon(
Icons.Default.Error,
contentDescription = null,
tint = Color(0xFFE53935),
modifier = Modifier.size(14.dp)
)
}
}
}
}
}
}
/**
* Avatar attachment - Telegram style
*/
@Composable
fun AvatarAttachment(
attachment: MessageAttachment,