diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt index 6fe10f787..702fc5fab 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessGameScreen.kt @@ -350,6 +350,15 @@ fun ChessGameScreen( onResign = { chessViewModel.resign(gameId) }, isSpectatorOverride = isSpectating, onGameEndDismiss = { chessViewModel.dismissGame(gameId) }, + onLeaveSpectating = + if (isSpectating) { + { + chessViewModel.stopSpectating(gameId) + nav.popBack() + } + } else { + null + }, whiteName = whiteDisplayName, whiteHex = whitePubkey, whiteAvatarUrl = whiteAvatarUrl, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt index e6b569e8c..ef1466285 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/ChessViewModelNew.kt @@ -176,7 +176,7 @@ class ChessViewModelNew( fun loadGameAsSpectator(gameId: String) = logic.loadGameAsSpectator(gameId) - fun stopSpectating(gameId: String) = logic.state.removeSpectatingGame(gameId) + fun stopSpectating(gameId: String) = logic.stopSpectating(gameId) // ============================================ // Utility diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt index 6c5df00c5..5005d6edc 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessLobbyLogic.kt @@ -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)) diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/LiveChessGame.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/LiveChessGame.kt index 5553faa40..7ffedeb42 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/LiveChessGame.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/chess/LiveChessGame.kt @@ -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") + } + } } } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt index 0ec8464fe..f80ba8562 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/ChessScreen.kt @@ -281,6 +281,15 @@ fun ChessScreen( }, onResign = { viewModel.resign(gameState.startEventId) }, isSpectatorOverride = isSpectating, + onLeaveSpectating = + if (isSpectating) { + { + viewModel.stopSpectating(gameState.startEventId) + viewModel.selectGame(null) + } + } else { + null + }, compactMode = compactMode, whiteName = viewModel.userMetadataCache.getDisplayName(whitePubkey), whiteHex = whitePubkey, @@ -614,6 +623,7 @@ private fun DesktopChessGameLayout( onMoveMade: (from: String, to: String, san: String) -> Unit, onResign: () -> Unit, isSpectatorOverride: Boolean? = null, + onLeaveSpectating: (() -> Unit)? = null, compactMode: Boolean = false, whiteName: String = "White", whiteHex: String = "", @@ -860,16 +870,28 @@ private fun DesktopChessGameLayout( containerColor = MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.5f), ), ) { - Row( + Column( modifier = Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), ) { - Icon(Icons.Default.Visibility, contentDescription = null) - Text( - "Watching game - spectator mode", - style = MaterialTheme.typography.bodyMedium, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Icon(Icons.Default.Visibility, contentDescription = null) + Text( + "Watching game - spectator mode", + style = MaterialTheme.typography.bodyMedium, + ) + } + onLeaveSpectating?.let { onLeave -> + OutlinedButton( + onClick = onLeave, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Leave Game") + } + } } } } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt index fb23322e7..a09245178 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/chess/DesktopChessViewModelNew.kt @@ -172,7 +172,7 @@ class DesktopChessViewModelNew( fun loadGameAsSpectator(gameId: String) = logic.loadGameAsSpectator(gameId) - fun stopSpectating(gameId: String) = logic.state.removeSpectatingGame(gameId) + fun stopSpectating(gameId: String) = logic.stopSpectating(gameId) // ============================================ // Utility