diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestActionBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestActionBar.kt index dbc0a9566..4c72d5fb6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestActionBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/screen/NestActionBar.kt @@ -126,6 +126,7 @@ internal fun NestActionBar( } EndCluster( isOnStage = isOnStage, + isConnected = ui.connection is ConnectionUiState.Connected, handRaised = handRaised, onHandRaisedChange = onHandRaisedChange, onShowReactionPicker = onShowReactionPicker, @@ -150,6 +151,12 @@ private fun ActionBarStatusStrip(ui: NestUiState) { stringRes(R.string.nest_broadcast_failed, broadcast.reason) } + is BroadcastUiState.Broadcasting -> { + broadcast.muteError?.let { + stringRes(R.string.nest_mute_failed, it) + } + } + else -> { null } @@ -201,33 +208,27 @@ private fun StartCluster( } is ConnectionUiState.Connected -> { - if (canBroadcast && isOnStage) { - OnStageControls( - viewModel = viewModel, - ui = ui, - speakerPubkeyHex = speakerPubkeyHex, - ) - } else { - // Audience: a single mute toggle for the inbound - // listener stream. Mirrors the old ConnectionRow. - FilledTonalIconToggleButton( - checked = ui.isMuted, - onCheckedChange = { viewModel.setMuted(it) }, - modifier = Modifier.size(width = 40.dp, height = ButtonDefaults.MinHeight), - ) { - Icon( - symbol = - if (ui.isMuted) { - MaterialSymbols.AutoMirrored.VolumeOff - } else { - MaterialSymbols.AutoMirrored.VolumeUp - }, - contentDescription = - stringRes( - if (ui.isMuted) R.string.nest_unmute else R.string.nest_mute, - ), + when { + canBroadcast && isOnStage -> { + OnStageControls( + viewModel = viewModel, + ui = ui, + speakerPubkeyHex = speakerPubkeyHex, ) } + + isOnStage -> { + // On stage but cannot broadcast (no signer / no permission). + // Only sensible affordance is to step back down to audience. + OutlinedButton(onClick = { viewModel.setOnStage(false) }) { + Text(stringRes(R.string.nest_leave_stage)) + } + } + + else -> { + // Audience: nothing to do here. System volume keys cover + // local volume; the mute toggle was redundant. + } } } @@ -326,6 +327,14 @@ private fun OnStageControls( enabled = false, label = { Text(stringRes(R.string.nest_broadcast_connecting)) }, ) + // Always offer a way down. stopBroadcast() cancels the + // in-flight speakerConnectJob (NestViewModel.teardownBroadcast). + OutlinedButton(onClick = { + viewModel.stopBroadcast() + viewModel.setOnStage(false) + }) { + Text(stringRes(R.string.nest_leave_stage)) + } } is BroadcastUiState.Broadcasting -> { @@ -398,6 +407,9 @@ private fun OnStageControls( modifier = Modifier.size(28.dp), ) } + OutlinedButton(onClick = { viewModel.setOnStage(false) }) { + Text(stringRes(R.string.nest_leave_stage)) + } } } } @@ -405,6 +417,7 @@ private fun OnStageControls( @Composable private fun EndCluster( isOnStage: Boolean, + isConnected: Boolean, handRaised: Boolean, onHandRaisedChange: (Boolean) -> Unit, onShowReactionPicker: () -> Unit, @@ -414,9 +427,10 @@ private fun EndCluster( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), ) { - // Hand-raise only makes sense for audience: if you're already - // on stage, you don't need to ask. Hidden when on-stage. - if (!isOnStage) { + // Hand-raise only makes sense for audience that's actually + // connected — if you're already on stage you don't need to ask, + // and raising while disconnected can't be delivered to the room. + if (!isOnStage && isConnected) { FilledTonalIconToggleButton( checked = handRaised, onCheckedChange = onHandRaisedChange, diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index a2e65e8dd..679340089 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -523,8 +523,9 @@ Going live… Live Broadcast failed: %1$s + Mute failed: %1$s Microphone access is required to talk in this room. - Open settings + No permissions Nests Keeps audio playing while a room is open. Nest connected