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.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.res.painterResource
|
||||||
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
|
||||||
@@ -40,6 +41,7 @@ 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.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
|
||||||
@@ -104,7 +106,9 @@ fun MessageAttachments(
|
|||||||
chachaKey = chachaKey,
|
chachaKey = chachaKey,
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
isOutgoing = isOutgoing,
|
isOutgoing = isOutgoing,
|
||||||
isDarkTheme = isDarkTheme
|
isDarkTheme = isDarkTheme,
|
||||||
|
timestamp = timestamp,
|
||||||
|
messageStatus = messageStatus
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AttachmentType.AVATAR -> {
|
AttachmentType.AVATAR -> {
|
||||||
@@ -490,7 +494,9 @@ fun FileAttachment(
|
|||||||
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()
|
||||||
@@ -556,98 +562,101 @@ fun FileAttachment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Telegram-style файл - как в desktop: без внутреннего фона, просто иконка + текст
|
// Telegram-style файл - как в desktop: без внутреннего фона, просто иконка + текст
|
||||||
Row(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable(enabled = downloadStatus == DownloadStatus.NOT_DOWNLOADED || downloadStatus == DownloadStatus.ERROR) {
|
|
||||||
download()
|
|
||||||
}
|
|
||||||
.padding(vertical = 4.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
) {
|
||||||
// File icon с индикатором прогресса - круглая иконка как в desktop
|
Row(
|
||||||
Box(
|
modifier = Modifier
|
||||||
modifier = Modifier.size(40.dp),
|
.fillMaxWidth()
|
||||||
contentAlignment = Alignment.Center
|
.clickable(enabled = downloadStatus == DownloadStatus.NOT_DOWNLOADED || downloadStatus == DownloadStatus.ERROR) {
|
||||||
|
download()
|
||||||
|
}
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// Круглый фон иконки
|
// File icon с индикатором прогресса - круглая иконка как в desktop
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier.size(40.dp),
|
||||||
.fillMaxSize()
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(
|
|
||||||
if (downloadStatus == DownloadStatus.ERROR) Color(0xFFE53935)
|
|
||||||
else PrimaryBlue
|
|
||||||
),
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
when (downloadStatus) {
|
// Круглый фон иконки
|
||||||
DownloadStatus.DOWNLOADING, DownloadStatus.DECRYPTING -> {
|
Box(
|
||||||
CircularProgressIndicator(
|
modifier = Modifier
|
||||||
modifier = Modifier.size(24.dp),
|
.fillMaxSize()
|
||||||
color = Color.White,
|
.clip(CircleShape)
|
||||||
strokeWidth = 2.dp
|
.background(
|
||||||
)
|
if (downloadStatus == DownloadStatus.ERROR) Color(0xFFE53935)
|
||||||
}
|
else PrimaryBlue
|
||||||
DownloadStatus.NOT_DOWNLOADED -> {
|
),
|
||||||
Icon(
|
contentAlignment = Alignment.Center
|
||||||
Icons.Default.ArrowDownward,
|
) {
|
||||||
contentDescription = "Download",
|
when (downloadStatus) {
|
||||||
tint = Color.White,
|
DownloadStatus.DOWNLOADING, DownloadStatus.DECRYPTING -> {
|
||||||
modifier = Modifier.size(20.dp)
|
CircularProgressIndicator(
|
||||||
)
|
modifier = Modifier.size(24.dp),
|
||||||
}
|
color = Color.White,
|
||||||
DownloadStatus.DOWNLOADED -> {
|
strokeWidth = 2.dp
|
||||||
Icon(
|
)
|
||||||
Icons.Default.InsertDriveFile,
|
}
|
||||||
contentDescription = null,
|
DownloadStatus.NOT_DOWNLOADED -> {
|
||||||
tint = Color.White,
|
Icon(
|
||||||
modifier = Modifier.size(20.dp)
|
Icons.Default.ArrowDownward,
|
||||||
)
|
contentDescription = "Download",
|
||||||
}
|
tint = Color.White,
|
||||||
DownloadStatus.ERROR -> {
|
modifier = Modifier.size(20.dp)
|
||||||
Icon(
|
)
|
||||||
Icons.Default.Close,
|
}
|
||||||
contentDescription = "Error",
|
DownloadStatus.DOWNLOADED -> {
|
||||||
tint = Color.White,
|
Icon(
|
||||||
modifier = Modifier.size(20.dp)
|
Icons.Default.InsertDriveFile,
|
||||||
)
|
contentDescription = null,
|
||||||
}
|
tint = Color.White,
|
||||||
else -> {
|
modifier = Modifier.size(20.dp)
|
||||||
Icon(
|
)
|
||||||
Icons.Default.InsertDriveFile,
|
}
|
||||||
contentDescription = null,
|
DownloadStatus.ERROR -> {
|
||||||
tint = Color.White,
|
Icon(
|
||||||
modifier = Modifier.size(20.dp)
|
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))
|
|
||||||
|
|
||||||
// Размер файла и тип
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
val fileExtension = fileName.substringAfterLast('.', "").uppercase()
|
|
||||||
Text(
|
// File info
|
||||||
text = when (downloadStatus) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
DownloadStatus.DOWNLOADING -> "Downloading..."
|
Text(
|
||||||
DownloadStatus.DECRYPTING -> "Decrypting..."
|
text = fileName,
|
||||||
DownloadStatus.ERROR -> "File expired"
|
fontSize = 14.sp,
|
||||||
else -> "${formatFileSize(fileSize)} $fileExtension"
|
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,
|
fontSize = 12.sp,
|
||||||
color = if (downloadStatus == DownloadStatus.ERROR) {
|
color = if (downloadStatus == DownloadStatus.ERROR) {
|
||||||
Color(0xFFE53935)
|
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
|
@Composable
|
||||||
fun AvatarAttachment(
|
fun AvatarAttachment(
|
||||||
attachment: MessageAttachment,
|
attachment: MessageAttachment,
|
||||||
|
|||||||
Reference in New Issue
Block a user