fix: render StageControlsBar inside the Stage card surface
The previous commit placed the controls strip as a sibling row right after StageGrid. Visually it read as a separate band, not as part of the stage card. Adds a bottomBar slot to StageGrid that renders inside the same Surface, below the grid, sharing the card's tonal background and horizontal padding. StageControlsBar drops its own horizontal padding since the card supplies it, and keeps a top inset so it has breathing room from the avatars.
This commit is contained in:
+1
-1
@@ -154,7 +154,7 @@ internal fun StageControlsBar(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
.padding(top = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
|
||||
+14
-12
@@ -246,18 +246,20 @@ internal fun NestFullScreen(
|
||||
onTapSelf = onTapSelf,
|
||||
listenerCount = presences.size,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
// Speaker controls live attached to the Stage card so they
|
||||
// only render when the user is actually on stage. This keeps
|
||||
// the action bar narrow for audience and makes the controls
|
||||
// appearing/disappearing a deliberate signal that the mic
|
||||
// is now (or no longer) available.
|
||||
StageControlsBar(
|
||||
viewModel = viewModel,
|
||||
ui = ui,
|
||||
isOnStage = isOnStageMe,
|
||||
canBroadcast = viewModel.canBroadcast,
|
||||
speakerPubkeyHex = myPubkey,
|
||||
// Speaker controls live inside the Stage card so they
|
||||
// only render when the user is actually on stage. This
|
||||
// keeps the action bar narrow for audience and makes
|
||||
// the controls appearing/disappearing a deliberate
|
||||
// signal that the mic is now (or no longer) available.
|
||||
bottomBar = {
|
||||
StageControlsBar(
|
||||
viewModel = viewModel,
|
||||
ui = ui,
|
||||
isOnStage = isOnStageMe,
|
||||
canBroadcast = viewModel.canBroadcast,
|
||||
speakerPubkeyHex = myPubkey,
|
||||
)
|
||||
},
|
||||
)
|
||||
NestActionBar(
|
||||
viewModel = viewModel,
|
||||
|
||||
+30
-24
@@ -126,6 +126,10 @@ private val STAGE_MAX_HEIGHT = 320.dp
|
||||
*
|
||||
* When [members] is empty, renders an "Waiting for speakers…" hint
|
||||
* instead so the strip stays visually anchored.
|
||||
*
|
||||
* [bottomBar] is rendered inside the same Surface, below the grid —
|
||||
* used to anchor speaker controls to the stage card so they only
|
||||
* appear when the local user is on stage.
|
||||
*/
|
||||
@Composable
|
||||
internal fun StageGrid(
|
||||
@@ -140,6 +144,7 @@ internal fun StageGrid(
|
||||
myPubkey: String? = null,
|
||||
onTapSelf: (() -> Unit)? = null,
|
||||
listenerCount: Int = 0,
|
||||
bottomBar: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
// Float currently-speaking members to the top so the listener can
|
||||
// see who they're hearing without scrolling. sortedBy is stable in
|
||||
@@ -193,32 +198,33 @@ internal fun StageGrid(
|
||||
}
|
||||
if (members.isEmpty()) {
|
||||
EmptyStageHint()
|
||||
return@Column
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(STAGE_CELL_MIN),
|
||||
modifier = Modifier.fillMaxWidth().heightIn(max = STAGE_MAX_HEIGHT),
|
||||
horizontalArrangement = Arrangement.spacedBy(GRID_SPACING),
|
||||
verticalArrangement = Arrangement.spacedBy(GRID_SPACING),
|
||||
) {
|
||||
items(items = sortedMembers, key = { it.pubkey }) { member ->
|
||||
val isSelf = myPubkey != null && member.pubkey == myPubkey
|
||||
MemberCell(
|
||||
member = member,
|
||||
avatarSize = STAGE_AVATAR,
|
||||
isSpeaking = member.pubkey in speakingNow,
|
||||
audioLevel = audioLevels[member.pubkey] ?: 0f,
|
||||
isConnecting = member.pubkey in connectingSpeakers,
|
||||
showMicBadge = true,
|
||||
reactions = reactionsByPubkey[member.pubkey].orEmpty(),
|
||||
accountViewModel = accountViewModel,
|
||||
onLongPressParticipant = onLongPressParticipant,
|
||||
isSelf = isSelf,
|
||||
onTapSelf = if (isSelf) onTapSelf else null,
|
||||
modifier = Modifier.animateItem(),
|
||||
)
|
||||
} else {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(STAGE_CELL_MIN),
|
||||
modifier = Modifier.fillMaxWidth().heightIn(max = STAGE_MAX_HEIGHT),
|
||||
horizontalArrangement = Arrangement.spacedBy(GRID_SPACING),
|
||||
verticalArrangement = Arrangement.spacedBy(GRID_SPACING),
|
||||
) {
|
||||
items(items = sortedMembers, key = { it.pubkey }) { member ->
|
||||
val isSelf = myPubkey != null && member.pubkey == myPubkey
|
||||
MemberCell(
|
||||
member = member,
|
||||
avatarSize = STAGE_AVATAR,
|
||||
isSpeaking = member.pubkey in speakingNow,
|
||||
audioLevel = audioLevels[member.pubkey] ?: 0f,
|
||||
isConnecting = member.pubkey in connectingSpeakers,
|
||||
showMicBadge = true,
|
||||
reactions = reactionsByPubkey[member.pubkey].orEmpty(),
|
||||
accountViewModel = accountViewModel,
|
||||
onLongPressParticipant = onLongPressParticipant,
|
||||
isSelf = isSelf,
|
||||
onTapSelf = if (isSelf) onTapSelf else null,
|
||||
modifier = Modifier.animateItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
bottomBar?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user