feat(chess): exit spectator mode with Leave Game button

Adds ability to leave a spectated game on both Android and Desktop:
- Added stopSpectating() to ChessLobbyLogic (removes from state + stops polling)
- Both ViewModels now delegate to logic.stopSpectating() instead of bypassing it
- LiveChessGameScreen accepts onLeaveSpectating callback shown in spectator info area
- Android: Leave Game button pops back stack and stops spectating
- Desktop: Leave Game button clears selectedGameId (returns to lobby) and stops spectating

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
davotoula
2026-03-23 16:28:44 +01:00
parent ed9d942ce0
commit 292c6de08a
6 changed files with 74 additions and 26 deletions
@@ -505,6 +505,11 @@ class ChessLobbyLogic(
// Spectator mode
// ========================================
fun stopSpectating(gameId: String) {
state.removeSpectatingGame(gameId)
pollingDelegate.removeGameId(gameId)
}
fun loadGameAsSpectator(startEventId: String) {
scope.launch(Dispatchers.Default) {
state.setBroadcastStatus(ChessBroadcastStatus.Syncing(0f))
@@ -201,6 +201,7 @@ fun LiveChessGameScreen(
onResign: () -> Unit,
isSpectatorOverride: Boolean? = null,
onGameEndDismiss: (() -> Unit)? = null,
onLeaveSpectating: (() -> Unit)? = null,
whiteName: String = "White",
whiteHex: String = "",
whiteAvatarUrl: String? = null,
@@ -299,7 +300,7 @@ fun LiveChessGameScreen(
}
isSpectator -> {
SpectatorInfo()
SpectatorInfo(onLeaveSpectating = onLeaveSpectating)
}
else -> {
@@ -547,22 +548,33 @@ private fun GameInfoHeader(
* Info banner shown when spectating a game
*/
@Composable
private fun SpectatorInfo() {
Box(
modifier =
Modifier
.fillMaxWidth()
.background(
MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.5f),
RoundedCornerShape(8.dp),
).padding(12.dp),
contentAlignment = Alignment.Center,
private fun SpectatorInfo(onLeaveSpectating: (() -> Unit)? = null) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = "Watching game - spectator mode",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onTertiaryContainer,
)
Box(
modifier =
Modifier
.fillMaxWidth()
.background(
MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.5f),
RoundedCornerShape(8.dp),
).padding(12.dp),
contentAlignment = Alignment.Center,
) {
Text(
text = "Watching game - spectator mode",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onTertiaryContainer,
)
}
onLeaveSpectating?.let { onLeave ->
OutlinedButton(onClick = onLeave) {
Text("Leave Game")
}
}
}
}