diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt index 838a4f7..bbf1c84 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt @@ -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 } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 47abeec..3ca56b4 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -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)) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt index e0c9728..0f578d1 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/components/ChatDetailComponents.kt @@ -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( } } - DropdownMenu( - expanded = showErrorMenu, - onDismissRequest = { showErrorMenu = false } + 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 + ) ) { - DropdownMenuItem( - text = { Text("Retry") }, - onClick = { - showErrorMenu = false - onRetry() - }, - leadingIcon = { - Icon( - painter = TelegramIcons.Retry, - contentDescription = null, - modifier = Modifier.size(18.dp) - ) + DropdownMenu( + expanded = showErrorMenu, + onDismissRequest = { showErrorMenu = false }, + modifier = Modifier + .defaultMinSize(minWidth = 196.dp) + .background(menuBgColor) + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .defaultMinSize(minHeight = 48.dp) + .clickable { + showErrorMenu = false + onRetry() + } + .padding(horizontal = 18.dp), + contentAlignment = Alignment.CenterStart + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + painter = TelegramIcons.Retry, + contentDescription = null, + 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 = { - showErrorMenu = false - onDelete() - }, - leadingIcon = { - Icon( - painter = TelegramIcons.Delete, - contentDescription = null, - tint = Color(0xFFE53935), - modifier = Modifier.size(18.dp) - ) + Box( + modifier = Modifier + .fillMaxWidth() + .defaultMinSize(minHeight = 48.dp) + .clickable { + showErrorMenu = false + onDelete() + } + .padding(horizontal = 18.dp), + contentAlignment = Alignment.CenterStart + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + painter = TelegramIcons.Delete, + contentDescription = null, + tint = Color(0xFFFF3B30), + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(19.dp)) + Text( + text = "Delete", + color = Color(0xFFFF3B30), + fontSize = 16.sp + ) + } } - ) + } } } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/models/ChatDetailModels.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/models/ChatDetailModels.kt index d60b174..1275de6 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/models/ChatDetailModels.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/models/ChatDetailModels.kt @@ -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 } )