android: upgrade fullscreen media viewer header and gallery
Some checks failed
CI / test (push) Failing after 2m14s
Some checks failed
CI / test (push) Failing after 2m14s
This commit is contained in:
@@ -275,3 +275,9 @@
|
||||
- Added file-list style attachment rows (icon + filename + type/size metadata).
|
||||
- Upgraded non-voice audio attachment player with play/pause, progress bar, and current/total duration labels.
|
||||
- Updated Telegram UI batch-2 checklist media-bubble items.
|
||||
|
||||
### Step 46 - Media viewer / header and gallery navigation
|
||||
- Upgraded chat image viewer to use global image gallery state (`index / total`) instead of a single URL.
|
||||
- Added fullscreen viewer header with close, index, share placeholder, and delete placeholder actions.
|
||||
- Added image navigation controls (`Prev`/`Next`) for gallery traversal.
|
||||
- Updated Telegram UI batch-2 checklist for fullscreen media header support.
|
||||
|
||||
@@ -123,7 +123,15 @@ fun ChatScreen(
|
||||
onLoadMore: () -> Unit,
|
||||
onPickMedia: () -> Unit,
|
||||
) {
|
||||
var viewerImageUrl by remember { mutableStateOf<String?>(null) }
|
||||
val allImageUrls = remember(state.messages) {
|
||||
state.messages
|
||||
.flatMap { message -> message.attachments }
|
||||
.map { it.fileUrl to it.fileType.lowercase() }
|
||||
.filter { (_, type) -> type.startsWith("image/") }
|
||||
.map { (url, _) -> url }
|
||||
.distinct()
|
||||
}
|
||||
var viewerImageIndex by remember { mutableStateOf<Int?>(null) }
|
||||
var dismissedPinnedMessageId by remember { mutableStateOf<Long?>(null) }
|
||||
var actionMenuMessage by remember { mutableStateOf<MessageItem?>(null) }
|
||||
Column(
|
||||
@@ -241,7 +249,10 @@ fun ChatScreen(
|
||||
message = message,
|
||||
isSelected = isSelected,
|
||||
reactions = state.reactionByMessageId[message.id].orEmpty(),
|
||||
onAttachmentImageClick = { imageUrl -> viewerImageUrl = imageUrl },
|
||||
onAttachmentImageClick = { imageUrl ->
|
||||
val idx = allImageUrls.indexOf(imageUrl)
|
||||
viewerImageIndex = if (idx >= 0) idx else null
|
||||
},
|
||||
onClick = {
|
||||
actionMenuMessage = null
|
||||
if (state.actionState.mode == MessageSelectionMode.MULTI) {
|
||||
@@ -495,16 +506,40 @@ fun ChatScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (viewerImageUrl != null) {
|
||||
if (viewerImageIndex != null) {
|
||||
val currentIndex = viewerImageIndex ?: 0
|
||||
val currentUrl = allImageUrls.getOrNull(currentIndex)
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.7f))
|
||||
.clickable { viewerImageUrl = null },
|
||||
.clickable { },
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Button(onClick = { viewerImageIndex = null }) { Text("✕") }
|
||||
Text(
|
||||
text = "${currentIndex + 1}/${allImageUrls.size.coerceAtLeast(1)}",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Button(onClick = {}, enabled = false) { Text("↗") }
|
||||
Button(onClick = {}, enabled = false) { Text("🗑") }
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.weight(1f),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (currentUrl != null) {
|
||||
AsyncImage(
|
||||
model = viewerImageUrl,
|
||||
model = currentUrl,
|
||||
contentDescription = "Attachment",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -513,6 +548,31 @@ fun ChatScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
viewerImageIndex = if (allImageUrls.isEmpty()) null else {
|
||||
(currentIndex - 1).coerceAtLeast(0)
|
||||
}
|
||||
},
|
||||
enabled = currentIndex > 0,
|
||||
) { Text("Prev") }
|
||||
Button(
|
||||
onClick = {
|
||||
viewerImageIndex = if (allImageUrls.isEmpty()) null else {
|
||||
(currentIndex + 1).coerceAtMost(allImageUrls.lastIndex)
|
||||
}
|
||||
},
|
||||
enabled = currentIndex < allImageUrls.lastIndex,
|
||||
) { Text("Next") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
- [ ] Метаданные поста/канала (просмотры/время/reaction strip) у медиа-сообщений.
|
||||
|
||||
## P1 — Fullscreen Media Viewer
|
||||
- [ ] Fullscreen header: close + index ("1") + share + delete.
|
||||
- [x] Fullscreen header: close + index ("1") + share + delete.
|
||||
- [ ] Поддержка reaction overlay в viewer (как на скрине).
|
||||
- [ ] Свайп между медиа (gallery mode), если в сообщении несколько вложений.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user