feat: Enhance FileAttachment component with timestamp and message status display
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user