feat: update message status handling for error cases in ChatViewModel and UI components
This commit is contained in:
@@ -1158,7 +1158,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
when (entity.delivered) {
|
||||
0 -> MessageStatus.SENDING
|
||||
1 -> if (entity.read == 1) MessageStatus.READ else MessageStatus.DELIVERED
|
||||
2 -> MessageStatus.SENT
|
||||
2 -> MessageStatus.ERROR
|
||||
3 -> MessageStatus.READ
|
||||
else -> MessageStatus.SENT
|
||||
},
|
||||
@@ -2509,8 +2509,11 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
saveDialog(text, timestamp)
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
updateMessageStatus(messageId, MessageStatus.SENT) // Changed from ERROR
|
||||
updateMessageStatus(messageId, MessageStatus.ERROR)
|
||||
}
|
||||
// Update error status in DB + dialog
|
||||
updateMessageStatusInDb(messageId, DeliveryStatus.ERROR.value)
|
||||
saveDialog(text, timestamp)
|
||||
} finally {
|
||||
isSending = false
|
||||
}
|
||||
@@ -3712,7 +3715,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
saveDialog(if (text.isNotEmpty()) text else "file", timestamp)
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) { updateMessageStatus(messageId, MessageStatus.SENT) }
|
||||
withContext(Dispatchers.Main) { updateMessageStatus(messageId, MessageStatus.ERROR) }
|
||||
updateMessageStatusInDb(messageId, DeliveryStatus.ERROR.value)
|
||||
saveDialog(if (text.isNotEmpty()) text else "file", timestamp)
|
||||
} finally {
|
||||
isSending = false
|
||||
}
|
||||
@@ -3942,7 +3947,7 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
saveDialog("\$a=Avatar", timestamp)
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
updateMessageStatus(messageId, MessageStatus.SENT)
|
||||
updateMessageStatus(messageId, MessageStatus.ERROR)
|
||||
android.widget.Toast.makeText(
|
||||
getApplication(),
|
||||
"Failed to send avatar: ${e.message}",
|
||||
@@ -3950,6 +3955,8 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
)
|
||||
.show()
|
||||
}
|
||||
updateMessageStatusInDb(messageId, DeliveryStatus.ERROR.value)
|
||||
saveDialog("\$a=Avatar", timestamp)
|
||||
} finally {
|
||||
isSending = false
|
||||
}
|
||||
|
||||
@@ -3866,24 +3866,8 @@ fun DialogItemContent(
|
||||
)
|
||||
}
|
||||
dialog.lastMessageDelivered == 2 -> {
|
||||
// ERROR - показываем иконку ошибки
|
||||
Icon(
|
||||
imageVector =
|
||||
TablerIcons
|
||||
.AlertCircle,
|
||||
contentDescription =
|
||||
"Sending failed",
|
||||
tint =
|
||||
Color(
|
||||
0xFFFF3B30
|
||||
), // iOS красный
|
||||
modifier =
|
||||
Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.width(4.dp)
|
||||
)
|
||||
// ERROR - не показываем статус рядом с временем,
|
||||
// error badge показывается внизу справа (как в Telegram)
|
||||
}
|
||||
dialog.lastMessageDelivered == 1 -> {
|
||||
// DELIVERED - одна серая галочка
|
||||
@@ -4120,6 +4104,27 @@ fun DialogItemContent(
|
||||
}
|
||||
}
|
||||
|
||||
// Error badge (Telegram-style) — красный кружок с "!" вместо unread badge
|
||||
if (dialog.lastMessageFromMe == 1 && dialog.lastMessageDelivered == 2) {
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.size(22.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color(0xFFE53935)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "!",
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White,
|
||||
lineHeight = 13.sp,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Unread badge
|
||||
if (dialog.unreadCount > 0) {
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
@@ -1650,6 +1650,19 @@ fun AnimatedMessageStatus(
|
||||
onRetry: () -> Unit = {},
|
||||
onDelete: () -> Unit = {}
|
||||
) {
|
||||
// Force recomposition when SENDING message exceeds 80s timeout
|
||||
var timeoutTick by remember { mutableLongStateOf(0L) }
|
||||
if (status == MessageStatus.SENDING && timestamp > 0) {
|
||||
val remainingMs = (timestamp + 80_000L) - System.currentTimeMillis()
|
||||
if (remainingMs > 0) {
|
||||
LaunchedEffect(timestamp) {
|
||||
kotlinx.coroutines.delay(remainingMs + 100)
|
||||
timeoutTick++
|
||||
}
|
||||
}
|
||||
}
|
||||
@Suppress("UNUSED_EXPRESSION") timeoutTick // read to subscribe
|
||||
|
||||
val isTimedOut =
|
||||
status == MessageStatus.SENDING &&
|
||||
timestamp > 0 &&
|
||||
@@ -1706,18 +1719,30 @@ fun AnimatedMessageStatus(
|
||||
label = "statusIcon"
|
||||
) { currentStatus ->
|
||||
if (currentStatus == MessageStatus.ERROR) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.AlertCircle,
|
||||
contentDescription = null,
|
||||
tint = animatedColor,
|
||||
// Telegram-style: red filled circle with white "!" inside
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.size(iconSize)
|
||||
Modifier.padding(start = 4.dp)
|
||||
.size(iconSize)
|
||||
.align(Alignment.CenterStart)
|
||||
.scale(scale)
|
||||
.background(
|
||||
color = Color(0xFFE53935),
|
||||
shape = CircleShape
|
||||
)
|
||||
.clickable {
|
||||
showErrorMenu = true
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "!",
|
||||
color = Color.White,
|
||||
fontSize = 10.sp,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold,
|
||||
lineHeight = 10.sp
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (currentStatus == MessageStatus.READ) {
|
||||
Box(
|
||||
@@ -1764,39 +1789,76 @@ fun AnimatedMessageStatus(
|
||||
}
|
||||
}
|
||||
|
||||
val menuBgColor = if (isDarkTheme) Color(0xFF272829) else Color.White
|
||||
val menuTextColor = if (isDarkTheme) Color.White else Color(0xFF222222)
|
||||
val menuIconColor = if (isDarkTheme) Color.White.copy(alpha = 0.47f) else Color(0xFF676B70)
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = MaterialTheme.colorScheme.copy(
|
||||
surface = menuBgColor,
|
||||
onSurface = menuTextColor
|
||||
)
|
||||
) {
|
||||
DropdownMenu(
|
||||
expanded = showErrorMenu,
|
||||
onDismissRequest = { showErrorMenu = false }
|
||||
onDismissRequest = { showErrorMenu = false },
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = 196.dp)
|
||||
.background(menuBgColor)
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Retry") },
|
||||
onClick = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 48.dp)
|
||||
.clickable {
|
||||
showErrorMenu = false
|
||||
onRetry()
|
||||
},
|
||||
leadingIcon = {
|
||||
}
|
||||
.padding(horizontal = 18.dp),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
painter = TelegramIcons.Retry,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
tint = menuIconColor,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(19.dp))
|
||||
Text(
|
||||
text = "Retry",
|
||||
color = menuTextColor,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Delete", color = Color(0xFFE53935)) },
|
||||
onClick = {
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 48.dp)
|
||||
.clickable {
|
||||
showErrorMenu = false
|
||||
onDelete()
|
||||
},
|
||||
leadingIcon = {
|
||||
}
|
||||
.padding(horizontal = 18.dp),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
painter = TelegramIcons.Delete,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFE53935),
|
||||
modifier = Modifier.size(18.dp)
|
||||
tint = Color(0xFFFF3B30),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(19.dp))
|
||||
Text(
|
||||
text = "Delete",
|
||||
color = Color(0xFFFF3B30),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ fun Message.toChatMessage() = ChatMessage(
|
||||
status = when (deliveryStatus) {
|
||||
DeliveryStatus.WAITING -> MessageStatus.SENDING
|
||||
DeliveryStatus.DELIVERED -> if (isRead) MessageStatus.READ else MessageStatus.DELIVERED
|
||||
DeliveryStatus.ERROR -> MessageStatus.SENT
|
||||
DeliveryStatus.ERROR -> MessageStatus.ERROR
|
||||
DeliveryStatus.READ -> MessageStatus.READ
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user