android: add multi-select top and bottom action bars
Some checks failed
CI / test (push) Failing after 2m7s
Some checks failed
CI / test (push) Failing after 2m7s
This commit is contained in:
@@ -217,3 +217,8 @@
|
|||||||
- Reworked chat composer into rounded Telegram-like container with emoji slot, text input, attach button, and send/voice state button.
|
- 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`).
|
- Preserved send/upload state guards and existing insets handling (`navigationBarsPadding` + `imePadding`).
|
||||||
- Updated Telegram UI batch-2 checklist composer-related items.
|
- 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.
|
||||||
|
|||||||
@@ -249,34 +249,44 @@ fun ChatScreen(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.92f))
|
||||||
.padding(horizontal = 12.dp, vertical = 6.dp),
|
.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
Button(onClick = onClearSelection) { Text("Close") }
|
||||||
Text(
|
Text(
|
||||||
text = "${state.actionState.selectedCount} selected",
|
text = "${state.actionState.selectedCount} selected",
|
||||||
style = MaterialTheme.typography.labelLarge,
|
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(false) }) { Text("Delete") }
|
||||||
Button(
|
Button(
|
||||||
onClick = { onDeleteSelected(true) },
|
onClick = { onDeleteSelected(true) },
|
||||||
enabled = state.actionState.mode == MessageSelectionMode.SINGLE && state.selectedCanDeleteForAll,
|
enabled = state.actionState.mode == MessageSelectionMode.SINGLE && state.selectedCanDeleteForAll,
|
||||||
) { Text("Del for all") }
|
) { Text("Del for all") }
|
||||||
Button(onClick = onForwardSelected) {
|
|
||||||
Text(if (state.actionState.mode == MessageSelectionMode.MULTI) "Forward selected" else "Forward")
|
|
||||||
}
|
|
||||||
if (state.actionState.mode == MessageSelectionMode.SINGLE) {
|
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\uDC4D") }) { Text("\uD83D\uDC4D") }
|
||||||
Button(onClick = { onToggleReaction("\uD83D\uDE02") }) { Text("\uD83D\uDE02") }
|
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),
|
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||||
) {
|
) {
|
||||||
|
if (isSelected) {
|
||||||
|
Text(
|
||||||
|
text = "✓ Selected",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
}
|
||||||
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
|
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = message.senderDisplayName,
|
text = message.senderDisplayName,
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
## P0 — Message Actions + Reactions
|
## P0 — Message Actions + Reactions
|
||||||
- [ ] Long press открывает reaction bar над сообщением (emoji + "expand").
|
- [ ] Long press открывает reaction bar над сообщением (emoji + "expand").
|
||||||
- [ ] Контекстное меню действий (reply/save/forward/pin/delete) в floating dark card.
|
- [ ] Контекстное меню действий (reply/save/forward/pin/delete) в floating dark card.
|
||||||
- [ ] Режим multi-select:
|
- [x] Режим multi-select:
|
||||||
- [ ] Верхняя панель (close/count/actions).
|
- [x] Верхняя панель (close/count/actions).
|
||||||
- [ ] Нижняя action bar (reply/forward) в виде rounded pills.
|
- [x] Нижняя action bar (reply/forward) в виде rounded pills.
|
||||||
- [ ] Selected state сообщения с явной подсветкой/overlay.
|
- [x] Selected state сообщения с явной подсветкой/overlay.
|
||||||
|
|
||||||
## P1 — Media & Attachment Bubbles
|
## P1 — Media & Attachment Bubbles
|
||||||
- [ ] Media bubble с превью изображения/видео + таймкод для видео.
|
- [ ] Media bubble с превью изображения/видео + таймкод для видео.
|
||||||
|
|||||||
Reference in New Issue
Block a user