android: align chat message actions with telegram-style selection
Some checks failed
Android CI / android (push) Failing after 5m2s
Android Release / release (push) Has started running
CI / test (push) Has been cancelled

This commit is contained in:
Codex
2026-03-10 01:22:08 +03:00
parent 4aa4946e82
commit 580a6683e3
2 changed files with 122 additions and 87 deletions

View File

@@ -852,3 +852,13 @@
- Refined reactions and attachments rendering inside bubbles: - Refined reactions and attachments rendering inside bubbles:
- chip-like reaction containers, - chip-like reaction containers,
- rounded image/file/media surfaces closer to Telegram-like visual rhythm. - rounded image/file/media surfaces closer to Telegram-like visual rhythm.
### Step 121 - Chat selection and message action UX cleanup
- Added Telegram-like multi-select top bar in chat:
- close selection,
- selected counter,
- quick forward/delete actions.
- Simplified tap action menu flow for single message:
- richer reaction row (`❤️ 👍 👎 🔥 🥰 👏 😁`),
- reply/edit/forward/delete actions kept in one sheet.
- Removed duplicate/conflicting selection controls between top and bottom action rows.

View File

@@ -308,6 +308,43 @@ fun ChatScreen(
.windowInsetsPadding(WindowInsets.safeDrawing) .windowInsetsPadding(WindowInsets.safeDrawing)
.padding(horizontal = adaptiveHorizontalPadding), .padding(horizontal = adaptiveHorizontalPadding),
) { ) {
if (state.actionState.mode == MessageSelectionMode.MULTI && state.actionState.hasSelection) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surface.copy(alpha = 0.92f))
.padding(horizontal = 8.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
IconButton(onClick = onClearSelection) {
Icon(imageVector = Icons.Filled.Close, contentDescription = "Close selection")
}
Text(
text = state.actionState.selectedCount.toString(),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
)
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = onForwardSelected) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Forward,
contentDescription = "Forward selected",
)
}
IconButton(
onClick = {
pendingDeleteForAll = false
showDeleteDialog = true
},
) {
Icon(imageVector = Icons.Filled.DeleteOutline, contentDescription = "Delete selected")
}
IconButton(onClick = onLoadMore, enabled = !state.isLoadingMore) {
Icon(imageVector = Icons.Filled.MoreVert, contentDescription = "Selection menu")
}
}
} else {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -369,6 +406,7 @@ fun ChatScreen(
Icon(imageVector = Icons.Filled.MoreVert, contentDescription = "More") Icon(imageVector = Icons.Filled.MoreVert, contentDescription = "More")
} }
} }
}
if (showInlineSearch) { if (showInlineSearch) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -494,14 +532,21 @@ fun ChatScreen(
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
listOf("👍", "❤️", "🔥", "😂").forEach { emoji -> listOf("❤️", "👍", "👎", "🔥", "🥰", "👏", "😁").forEach { emoji ->
Button( Surface(
onClick = { shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier
.clip(CircleShape)
.clickable {
onSelectMessage(selected) onSelectMessage(selected)
onToggleReaction(emoji) onToggleReaction(emoji)
actionMenuMessage = null actionMenuMessage = null
}, }
) { Text(emoji) } .padding(horizontal = 12.dp, vertical = 8.dp),
) {
Text(text = emoji)
}
} }
} }
Button( Button(
@@ -536,13 +581,6 @@ fun ChatScreen(
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { Text("Delete") } ) { Text("Delete") }
Button(
onClick = {
onEnterMultiSelect(selected)
actionMenuMessage = null
},
modifier = Modifier.fillMaxWidth(),
) { Text("Select") }
TextButton( TextButton(
onClick = { onClick = {
actionMenuMessage = null actionMenuMessage = null
@@ -556,7 +594,8 @@ fun ChatScreen(
} }
if (state.actionState.hasSelection && if (state.actionState.hasSelection &&
!(state.actionState.mode == MessageSelectionMode.SINGLE && actionMenuMessage != null) state.actionState.mode == MessageSelectionMode.SINGLE &&
actionMenuMessage == null
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -602,20 +641,6 @@ fun ChatScreen(
Button(onClick = { onToggleReaction("\uD83D\uDE02") }) { Text("\uD83D\uDE02") } Button(onClick = { onToggleReaction("\uD83D\uDE02") }) { Text("\uD83D\uDE02") }
} }
} }
if (state.actionState.mode == MessageSelectionMode.SINGLE) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.88f))
.padding(horizontal = 12.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
if (state.selectedMessage != null) {
Button(onClick = { onReplySelected(state.selectedMessage) }) { Text("Reply") }
}
Button(onClick = onForwardSelected) { Text("Forward") }
}
}
} }
if (state.forwardingMessageIds.isNotEmpty()) { if (state.forwardingMessageIds.isNotEmpty()) {