android: add multi-select top and bottom action bars
Some checks failed
CI / test (push) Failing after 2m7s

This commit is contained in:
Codex
2026-03-09 14:07:20 +03:00
parent 7381d611cc
commit 6c9501e624
3 changed files with 38 additions and 16 deletions

View File

@@ -217,3 +217,8 @@
- Reworked chat composer into rounded Telegram-like container with emoji slot, text input, attach button, and send/voice state button.
- Preserved send/upload state guards and existing insets handling (`navigationBarsPadding` + `imePadding`).
- Updated Telegram UI batch-2 checklist composer-related items.
### Step 35 - Chat UI / multi-select bars and overlays
- Split message selection UX into dedicated top selection bar (count/close/delete/edit/reactions) and bottom action bar (reply/forward).
- Enhanced selected bubble visual state with explicit selected marker text.
- Updated Telegram UI batch-2 checklist items for multi-select mode.

View File

@@ -249,34 +249,44 @@ fun ChatScreen(
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.92f))
.padding(horizontal = 12.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Button(onClick = onClearSelection) { Text("Close") }
Text(
text = "${state.actionState.selectedCount} selected",
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(top = 12.dp),
modifier = Modifier.weight(1f),
)
if (state.actionState.mode == MessageSelectionMode.SINGLE && state.selectedMessage != null) {
Button(onClick = { onReplySelected(state.selectedMessage) }) { Text("Reply") }
Button(
onClick = { onEditSelected(state.selectedMessage) },
enabled = state.selectedCanEdit,
) { Text("Edit") }
}
Button(onClick = { onDeleteSelected(false) }) { Text("Delete") }
Button(
onClick = { onDeleteSelected(true) },
enabled = state.actionState.mode == MessageSelectionMode.SINGLE && state.selectedCanDeleteForAll,
) { Text("Del for all") }
Button(onClick = onForwardSelected) {
Text(if (state.actionState.mode == MessageSelectionMode.MULTI) "Forward selected" else "Forward")
}
if (state.actionState.mode == MessageSelectionMode.SINGLE) {
Button(
onClick = { state.selectedMessage?.let(onEditSelected) },
enabled = state.selectedCanEdit,
) { Text("Edit") }
Button(onClick = { onToggleReaction("\uD83D\uDC4D") }) { Text("\uD83D\uDC4D") }
Button(onClick = { onToggleReaction("\uD83D\uDE02") }) { Text("\uD83D\uDE02") }
}
Button(onClick = onClearSelection) { Text("Close") }
}
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.actionState.mode == MessageSelectionMode.SINGLE && state.selectedMessage != null) {
Button(onClick = { onReplySelected(state.selectedMessage) }) { Text("Reply") }
}
Button(onClick = onForwardSelected) {
Text(if (state.actionState.mode == MessageSelectionMode.MULTI) "Forward selected" else "Forward")
}
}
}
@@ -477,6 +487,13 @@ private fun MessageBubble(
)
.padding(horizontal = 10.dp, vertical = 8.dp),
) {
if (isSelected) {
Text(
text = "✓ Selected",
style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.SemiBold,
)
}
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
Text(
text = message.senderDisplayName,

View File

@@ -23,10 +23,10 @@
## P0 — Message Actions + Reactions
- [ ] Long press открывает reaction bar над сообщением (emoji + "expand").
- [ ] Контекстное меню действий (reply/save/forward/pin/delete) в floating dark card.
- [ ] Режим multi-select:
- [ ] Верхняя панель (close/count/actions).
- [ ] Нижняя action bar (reply/forward) в виде rounded pills.
- [ ] Selected state сообщения с явной подсветкой/overlay.
- [x] Режим multi-select:
- [x] Верхняя панель (close/count/actions).
- [x] Нижняя action bar (reply/forward) в виде rounded pills.
- [x] Selected state сообщения с явной подсветкой/overlay.
## P1 — Media & Attachment Bubbles
- [ ] Media bubble с превью изображения/видео + таймкод для видео.