diff --git a/app/src/main/java/io/zenandroid/onlinego/data/db/GameDao.kt b/app/src/main/java/io/zenandroid/onlinego/data/db/GameDao.kt index 838b420e..005bd1f2 100644 --- a/app/src/main/java/io/zenandroid/onlinego/data/db/GameDao.kt +++ b/app/src/main/java/io/zenandroid/onlinego/data/db/GameDao.kt @@ -23,6 +23,7 @@ import io.zenandroid.onlinego.data.model.local.GameNotificationWithDetails import io.zenandroid.onlinego.data.model.local.HistoricGamesMetadata import io.zenandroid.onlinego.data.model.local.InitialState import io.zenandroid.onlinego.data.model.local.Message +import io.zenandroid.onlinego.data.model.local.PauseControl import io.zenandroid.onlinego.data.model.local.Player import io.zenandroid.onlinego.data.model.local.Puzzle import io.zenandroid.onlinego.data.model.local.PuzzleCollection @@ -31,8 +32,7 @@ import io.zenandroid.onlinego.data.model.local.Score import io.zenandroid.onlinego.data.model.local.VisitedPuzzleCollection import io.zenandroid.onlinego.data.model.ogs.JosekiPosition import io.zenandroid.onlinego.data.model.ogs.Phase -import io.zenandroid.onlinego.data.model.ogs.PuzzleRating -import io.zenandroid.onlinego.data.model.ogs.PuzzleSolution +import io.zenandroid.onlinego.data.ogs.Pause import kotlinx.coroutines.flow.Flow private const val MAX_ALLOWED_SQL_PARAMS = 999 @@ -230,12 +230,15 @@ abstract class GameDao { open fun updateClock( id: Long, playerToMoveId: Long?, - clock: Clock?) { + clock: Clock?, + pause: Pause?) { getGame(id).blockingGet().let { update(it.copy( undoRequested = if(it.playerToMoveId != playerToMoveId) null else it.undoRequested, playerToMoveId = playerToMoveId, clock = clock, + pauseControl = (it.pauseControl ?: PauseControl()) + .copy(pausedByThirdParty = pause?.paused), pausedSince = when(clock?.newPausedState) { true -> clock.newPausedSince false -> null diff --git a/app/src/main/java/io/zenandroid/onlinego/data/model/local/PauseControl.kt b/app/src/main/java/io/zenandroid/onlinego/data/model/local/PauseControl.kt index 30679a55..671878ea 100644 --- a/app/src/main/java/io/zenandroid/onlinego/data/model/local/PauseControl.kt +++ b/app/src/main/java/io/zenandroid/onlinego/data/model/local/PauseControl.kt @@ -20,3 +20,10 @@ fun PauseControl?.isPaused() = || this.moderator == true || this.pausedByThirdParty == true } ?: false + +fun PauseControl?.isPlayerPaused() = + when { + this?.moderator == true -> true + this?.pausedByThirdParty == true -> true + else -> false + } diff --git a/app/src/main/java/io/zenandroid/onlinego/data/ogs/GameConnection.kt b/app/src/main/java/io/zenandroid/onlinego/data/ogs/GameConnection.kt index 379585e4..20ed07f4 100644 --- a/app/src/main/java/io/zenandroid/onlinego/data/ogs/GameConnection.kt +++ b/app/src/main/java/io/zenandroid/onlinego/data/ogs/GameConnection.kt @@ -172,6 +172,22 @@ class GameConnection( } } + fun pause() { + socketService.emit("game/pause") { + "auth" - gameAuth + "game_id" - gameId + "player_id" - getCurrentUserId() + } + } + + fun resume() { + socketService.emit("game/resume") { + "auth" - gameAuth + "game_id" - gameId + "player_id" - getCurrentUserId() + } + } + fun rejectRemovedStones() { socketService.emit("game/removed_stones/reject") { "auth" - gameAuth @@ -317,4 +333,4 @@ data class RemovedStonesAccepted( val player_id: Long?, val stones: String?, val players: Players? -) \ No newline at end of file +) diff --git a/app/src/main/java/io/zenandroid/onlinego/data/repositories/ActiveGamesRepository.kt b/app/src/main/java/io/zenandroid/onlinego/data/repositories/ActiveGamesRepository.kt index dc364660..365ba293 100644 --- a/app/src/main/java/io/zenandroid/onlinego/data/repositories/ActiveGamesRepository.kt +++ b/app/src/main/java/io/zenandroid/onlinego/data/repositories/ActiveGamesRepository.kt @@ -210,7 +210,8 @@ class ActiveGamesRepository( gameDao.updateClock( id = gameId, playerToMoveId = clock.current_player, - clock = Clock.fromOGSClock(clock) + clock = Clock.fromOGSClock(clock), + pause = clock.pause ) } @@ -388,4 +389,4 @@ class ActiveGamesRepository( recordException(Exception(message, t)) Log.e("ActiveGameRespository", message, t) } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/zenandroid/onlinego/ui/screens/game/GameViewModel.kt b/app/src/main/java/io/zenandroid/onlinego/ui/screens/game/GameViewModel.kt index 89da4617..231073d5 100644 --- a/app/src/main/java/io/zenandroid/onlinego/ui/screens/game/GameViewModel.kt +++ b/app/src/main/java/io/zenandroid/onlinego/ui/screens/game/GameViewModel.kt @@ -8,6 +8,9 @@ import androidx.compose.material.icons.rounded.Functions import androidx.compose.material.icons.rounded.HighlightOff import androidx.compose.material.icons.rounded.NextPlan import androidx.compose.material.icons.rounded.OutlinedFlag +import androidx.compose.material.icons.rounded.Pause +import androidx.compose.material.icons.rounded.PlayArrow +import androidx.compose.material.icons.rounded.PlayCircle import androidx.compose.material.icons.rounded.SkipNext import androidx.compose.material.icons.rounded.SkipPrevious import androidx.compose.material.icons.rounded.Stop @@ -45,6 +48,7 @@ import io.zenandroid.onlinego.data.model.local.Player import io.zenandroid.onlinego.data.model.local.Score import io.zenandroid.onlinego.data.model.local.UserStats import io.zenandroid.onlinego.data.model.local.isPaused +import io.zenandroid.onlinego.data.model.local.isPlayerPaused import io.zenandroid.onlinego.data.model.ogs.Phase import io.zenandroid.onlinego.data.model.ogs.VersusStats import io.zenandroid.onlinego.data.ogs.GameConnection @@ -69,9 +73,11 @@ import io.zenandroid.onlinego.ui.screens.game.Button.ExitEstimate import io.zenandroid.onlinego.ui.screens.game.Button.Next import io.zenandroid.onlinego.ui.screens.game.Button.NextGame import io.zenandroid.onlinego.ui.screens.game.Button.Pass +import io.zenandroid.onlinego.ui.screens.game.Button.Pause import io.zenandroid.onlinego.ui.screens.game.Button.Previous import io.zenandroid.onlinego.ui.screens.game.Button.RejectStoneRemoval import io.zenandroid.onlinego.ui.screens.game.Button.Resign +import io.zenandroid.onlinego.ui.screens.game.Button.Resume import io.zenandroid.onlinego.ui.screens.game.Button.Undo import io.zenandroid.onlinego.ui.screens.game.PendingNavigation.NavigateToGame import io.zenandroid.onlinego.ui.screens.game.PendingNavigation.OpenURL @@ -289,6 +295,7 @@ class GameViewModel( } ?: game?.moves?.size ?: 0 val nextButton = Next(analysisShownMoveNumber < maxAnalysisMoveNumber) val chatButton = Chat(if(unreadMessagesCount > 0) unreadMessagesCount.toString() else null) + val pauseButton = if (game?.pauseControl.isPlayerPaused()) Resume else Pause val visibleButtons = when { @@ -297,9 +304,9 @@ class GameViewModel( gameFinished == true -> listOf(chatButton, Estimate(true), Previous, nextButton) analyzeMode -> listOf(ExitAnalysis, Estimate(!isAnalysisDisabled()), Previous, nextButton) pendingMove != null -> emptyList() - isMyTurn && candidateMove == null -> listOf(Analyze, Pass, endGameButton, chatButton, nextGameButton) + isMyTurn && candidateMove == null -> listOf(Analyze, Pass, pauseButton, endGameButton, chatButton, nextGameButton) isMyTurn && candidateMove != null -> listOf(ConfirmMove, DiscardMove) - !isMyTurn && game?.phase == Phase.PLAY -> listOf(Analyze, Undo, endGameButton, chatButton, nextGameButton) + !isMyTurn && game?.phase == Phase.PLAY -> listOf(Analyze, Undo, pauseButton, endGameButton, chatButton, nextGameButton) else -> emptyList() } @@ -801,6 +808,8 @@ class GameViewModel( analyzeMode = true } Pass -> passDialogShowing = true + Pause -> gameConnection.pause() + Resume -> gameConnection.resume() Resign -> resignDialogShowing = true CancelGame -> cancelDialogShowing = true is Chat -> { @@ -992,6 +1001,8 @@ sealed class Button( object RejectStoneRemoval : Button(Icons.Rounded.ThumbDown, "Reject") object Analyze : Button(Icons.Rounded.Biotech, "Analyze") object Pass : Button(Icons.Rounded.Stop, "Pass") + object Pause : Button(Icons.Rounded.Pause, "Pause") + object Resume : Button(Icons.Rounded.PlayCircle, "Resume") object Resign : Button(Icons.Rounded.OutlinedFlag, "Resign") object CancelGame : Button(Icons.Rounded.Cancel, "Cancel Game") class Chat(bubbleText: String? = null) : Button(bubbleText = bubbleText, icon = Icons.Rounded.Forum, label = "Chat") diff --git a/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/LargeGameItem.kt b/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/LargeGameItem.kt index 586bbb80..a31cdea6 100644 --- a/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/LargeGameItem.kt +++ b/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/LargeGameItem.kt @@ -93,8 +93,16 @@ fun LargeGameItem(game: Game, boardTheme: BoardTheme, userId: Long, onAction: (A fontSize = 12.sp, ) if (game.pauseControl.isPaused()) { + val paused = when { + game.pauseControl?.vacationWhite == true -> "vacation (white)" + game.pauseControl?.vacationBlack == true -> "vacation (black)" + game.pauseControl?.weekend == true -> "weekend" + game.pauseControl?.stoneRemoval == true -> "scoring" + game.pauseControl?.server == true -> "paused (server)" + else -> "paused" + } Text( - text = " · paused", + text = " · $paused", style = TextStyle.Default.copy( fontSize = 12.sp, ), @@ -128,4 +136,4 @@ private fun Preview() { onAction = {} ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/SmallGameItem.kt b/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/SmallGameItem.kt index 6a70e1bd..f64eada0 100644 --- a/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/SmallGameItem.kt +++ b/app/src/main/java/io/zenandroid/onlinego/ui/screens/mygames/composables/SmallGameItem.kt @@ -82,8 +82,16 @@ fun SmallGameItem(game: Game, boardTheme: BoardTheme, userId: Long, onAction: (A ), ) if (game.pauseControl.isPaused()) { + val paused = when { + game.pauseControl?.vacationWhite == true -> "vacation (white)" + game.pauseControl?.vacationBlack == true -> "vacation (black)" + game.pauseControl?.weekend == true -> "weekend" + game.pauseControl?.stoneRemoval == true -> "scoring" + game.pauseControl?.server == true -> "paused (server)" + else -> "paused" + } Text( - text = " · paused", + text = " · $paused", color = MaterialTheme.colors.onSurface, style = TextStyle.Default.copy( fontSize = 12.sp,