diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestFullScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestFullScreen.kt index de4aa99ce..bb8faae86 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestFullScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestFullScreen.kt @@ -20,14 +20,12 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.screen -import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.AlertDialog import androidx.compose.material3.Badge import androidx.compose.material3.BadgedBox @@ -39,7 +37,6 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -52,11 +49,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R @@ -86,11 +81,13 @@ import kotlinx.coroutines.launch /** * Full-screen layout for [com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.activity.NestActivity]. Vertically split into: * - * 1. TopAppBar — room title + overflow menu (Share, host's Edit). - * 2. Header strip — LIVE chip, listener count, optional 1-line summary. - * 3. Stage — vertical adaptive grid of host/speakers (height-bounded - * so a 30-speaker room scrolls inside the strip and never pushes - * the chat below the fold). + * 1. TopAppBar — room title (tap to expand/collapse summary) + overflow + * menu (Share, host's Edit). + * 2. Optional summary strip — only visible when the user taps the title. + * 3. Stage — vertical adaptive grid of host/speakers, with the LIVE + * chip and listener count rendered in the stage header row + * (height-bounded so a 30-speaker room scrolls inside the strip + * and never pushes the chat below the fold). * 4. Tabs — `Chat | Audience · N | Hands · N` (Hands host-only, * shown only while there's at least one raised hand). * 5. Tab content — fills the remaining vertical space: @@ -122,6 +119,11 @@ internal fun NestFullScreen( var showHostLeaveConfirm by rememberSaveable { mutableStateOf(false) } var showReactionPicker by rememberSaveable { mutableStateOf(false) } var hostMenuTarget by rememberSaveable { mutableStateOf(null) } + // Summary is collapsed by default; tapping the top-bar title + // toggles it so the user can preview the room description without + // opening a separate sheet, then collapse again to reclaim vertical + // room for the stage and chat. + var summaryExpanded by rememberSaveable { mutableStateOf(false) } // Tab selection survives configuration changes and PIP transitions. // Stored as ordinal so rememberSaveable can persist it without a // custom Saver. @@ -187,6 +189,7 @@ internal fun NestFullScreen( onMinimize = onMinimize, onMenuOpen = { showHostMenu = true }, onMenuDismiss = { showHostMenu = false }, + onTitleClick = { summaryExpanded = !summaryExpanded }, onShare = { showHostMenu = false shareRoomNaddr(topBarContext, event) @@ -223,10 +226,9 @@ internal fun NestFullScreen( .fillMaxSize() .padding(padding), ) { - RoomHeaderStrip( - summary = event.summary(), - listenerCount = presences.size, - ) + if (summaryExpanded) { + RoomSummaryStrip(summary = event.summary()) + } // Self-cell tap toggles mic-mute when broadcasting; null // when not broadcasting so the avatar falls back to the // default no-op tap (rather than offering a button that @@ -245,6 +247,7 @@ internal fun NestFullScreen( onLongPressParticipant = onLongPressParticipant, myPubkey = myPubkey, onTapSelf = onTapSelf, + listenerCount = presences.size, modifier = Modifier.padding(horizontal = 16.dp), ) NestTabRow( @@ -366,55 +369,23 @@ internal fun NestFullScreen( } /** - * Header strip rendered between the TopAppBar and the stage grid. - * Carries the LIVE chip + listener count and (when present) a single- - * line ellipsised summary. Lives at the screen level rather than in - * the TopAppBar so the chip + count have room to breathe and the - * summary's typography matches the body, not the title. + * Expanded summary strip shown between the TopAppBar and the stage grid + * when the user taps the title. The LIVE chip and listener count have + * moved to the stage card's header row, so this strip now carries only + * the room description. */ @Composable -private fun RoomHeaderStrip( - summary: String?, - listenerCount: Int, -) { - Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp)) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - LiveChip() - Text( - text = pluralStringResource(R.plurals.nest_listener_count, listenerCount, listenerCount), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - if (!summary.isNullOrBlank()) { - Text( - text = summary, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - maxLines = 2, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(top = 4.dp), - ) - } - } -} - -@Composable -private fun LiveChip() { - Surface( - shape = RoundedCornerShape(50), - color = MaterialTheme.colorScheme.errorContainer, - contentColor = MaterialTheme.colorScheme.onErrorContainer, - ) { - Text( - text = stringRes(R.string.nest_live_chip), - style = MaterialTheme.typography.labelSmall, - modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp), - ) - } +private fun RoomSummaryStrip(summary: String?) { + if (summary.isNullOrBlank()) return + Text( + text = summary, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) } /** @@ -491,6 +462,7 @@ private fun NestTopAppBar( onMinimize: () -> Unit, onMenuOpen: () -> Unit, onMenuDismiss: () -> Unit, + onTitleClick: () -> Unit, onShare: () -> Unit, onEdit: () -> Unit, ) { @@ -500,6 +472,10 @@ private fun NestTopAppBar( text = title, maxLines = 1, overflow = TextOverflow.Ellipsis, + modifier = + Modifier + .fillMaxWidth() + .clickable(onClick = onTitleClick), ) }, actions = { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt index d4808e309..04605bc0c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn @@ -57,6 +58,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -137,6 +139,7 @@ internal fun StageGrid( onLongPressParticipant: ((String) -> Unit)? = null, myPubkey: String? = null, onTapSelf: (() -> Unit)? = null, + listenerCount: Int = 0, ) { // Float currently-speaking members to the top so the listener can // see who they're hearing without scrolling. sortedBy is stable in @@ -165,12 +168,29 @@ internal fun StageGrid( vertical = STAGE_CARD_PADDING_VERTICAL, ), ) { - Text( - text = stringRes(R.string.nest_stage), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(bottom = 8.dp), - ) + Row( + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringRes(R.string.nest_stage), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.weight(1f), + ) + Text( + text = + pluralStringResource( + R.plurals.nest_listener_count, + listenerCount, + listenerCount, + ), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(end = 8.dp), + ) + LiveChip() + } if (members.isEmpty()) { EmptyStageHint() return@Column @@ -203,6 +223,21 @@ internal fun StageGrid( } } +@Composable +private fun LiveChip() { + Surface( + shape = RoundedCornerShape(50), + color = MaterialTheme.colorScheme.errorContainer, + contentColor = MaterialTheme.colorScheme.onErrorContainer, + ) { + Text( + text = stringRes(R.string.nest_live_chip), + style = MaterialTheme.typography.labelSmall, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp), + ) + } +} + /** * Idle "Waiting for speakers…" placeholder for [StageGrid]. Pairs an * hourglass glyph with the existing copy so the empty state reads as