From aeda2924f4393e226282b8dffebe3d84315816a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Nov 2026 18:25:11 +0000 Subject: [PATCH] End-game button on every card game; Resume game menu President, Crazy Eights, Cribbage, Euchre, Golf, Kaiser, Sequence and Wizard gain the "End game" button and the left-the-table dialog the other games have. The main menu's inline Continue list becomes a Resume game button opening a screen of all saved games, each with Resume and Delete (confirmed). Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01TWwLCrXa9aFVg8dZsCdLBZ --- .../java/org/prolibertate/games/ui/AppRoot.kt | 51 ++++++----- .../org/prolibertate/games/ui/ResumeScreen.kt | 87 +++++++++++++++++++ .../games/ui/game/CrazyEightsScreen.kt | 22 ++++- .../games/ui/game/CribbageScreen.kt | 22 ++++- .../games/ui/game/EuchreScreen.kt | 23 ++++- .../prolibertate/games/ui/game/GolfScreen.kt | 22 ++++- .../games/ui/game/KaiserScreen.kt | 23 ++++- .../games/ui/game/PresidentScreen.kt | 26 +++++- .../games/ui/game/SequenceScreen.kt | 23 ++++- .../games/ui/game/WizardScreen.kt | 22 ++++- 10 files changed, 285 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/org/prolibertate/games/ui/ResumeScreen.kt diff --git a/app/src/main/java/org/prolibertate/games/ui/AppRoot.kt b/app/src/main/java/org/prolibertate/games/ui/AppRoot.kt index 7ac58f1..e8544e2 100644 --- a/app/src/main/java/org/prolibertate/games/ui/AppRoot.kt +++ b/app/src/main/java/org/prolibertate/games/ui/AppRoot.kt @@ -88,6 +88,9 @@ sealed interface Route { data class Section(val menu: GameMenu) : Route data object Settings : Route data object Scorekeeper : Route + + /** Every saved game, to carry on from or delete. */ + data object Resume : Route data class Setup(val gameId: String) : Route data class Lobby(val gameId: String, val optionsJson: String, val hosting: Boolean) : Route data class Play( @@ -188,10 +191,8 @@ private fun AppContent( // Dev builds show the games that are not finished yet; production // releases list only what can actually be played. showComingSoon = showComingSoon, - saved = savedGames(env, stack), - onResume = { saved -> - push(Route.Play(saved.gameId, saved.config, true, saved.localSeat, saved.stateJson)) - }, + hasSaved = savedGames(env, stack).isNotEmpty(), + onResume = { push(Route.Resume) }, onPickSection = { push(Route.Section(it)) }, onJoinGame = { push(Route.Lobby(gameId = "", optionsJson = "{}", hosting = false)) }, onScorekeeper = { push(Route.Scorekeeper) }, @@ -206,6 +207,25 @@ private fun AppContent( onBack = pop, ) + is Route.Resume -> { + // Bumped when a game is deleted, so the list is read again. + var deletions by remember(route) { mutableIntStateOf(0) } + val saved by produceState(emptyList(), route, deletions) { + value = withContext(Dispatchers.IO) { env.saves.all() } + } + ResumeScreen( + saved = saved, + onResume = { match -> + push(Route.Play(match.gameId, match.config, true, match.localSeat, match.stateJson)) + }, + onDelete = { match -> + env.saves.clear(match.gameId) + deletions++ + }, + onBack = pop, + ) + } + is Route.Settings -> SettingsScreen( env = env, settings = settings, @@ -271,8 +291,8 @@ private fun AppContent( @Composable fun MainMenuScreen( showComingSoon: Boolean, - saved: List, - onResume: (SavedMatch) -> Unit, + hasSaved: Boolean, + onResume: () -> Unit, onPickSection: (GameMenu) -> Unit, onJoinGame: () -> Unit, onScorekeeper: () -> Unit, @@ -322,20 +342,11 @@ fun MainMenuScreen( ) } - // Games left half-played, most recent first, above everything - // else: coming back to the app is mostly coming back to one. - if (saved.isNotEmpty()) { - Text( - "Continue", - style = MaterialTheme.typography.labelLarge, - modifier = Modifier.fillMaxWidth(), - ) - val now = System.currentTimeMillis() - saved.forEach { match -> - val title = GameCatalog.byId(match.gameId)?.title ?: match.gameId - Button(onClick = { onResume(match) }, modifier = Modifier.fillMaxWidth()) { - Text(savedLine(title, match, now)) - } + // Games left half-played, above everything else: coming back to + // the app is mostly coming back to one. + if (hasSaved) { + Button(onClick = onResume, modifier = Modifier.fillMaxWidth()) { + Text("Resume game") } } diff --git a/app/src/main/java/org/prolibertate/games/ui/ResumeScreen.kt b/app/src/main/java/org/prolibertate/games/ui/ResumeScreen.kt new file mode 100644 index 0000000..eed1273 --- /dev/null +++ b/app/src/main/java/org/prolibertate/games/ui/ResumeScreen.kt @@ -0,0 +1,87 @@ +package org.prolibertate.games.ui + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import org.prolibertate.games.game.GameCatalog +import org.prolibertate.games.game.engine.SavedMatch + +/** + * Every game that can be carried on from, most recently played first, each with + * a way to pick it up and a way to throw it away. + */ +@Composable +fun ResumeScreen( + saved: List, + onResume: (SavedMatch) -> Unit, + onDelete: (SavedMatch) -> Unit, + onBack: () -> Unit, +) { + var deleting by remember { mutableStateOf(null) } + + ScreenScaffold(title = "Resume game", onBack = onBack) { modifier -> + if (saved.isEmpty()) { + Text("No games to resume.", modifier = modifier) + } else { + val now = System.currentTimeMillis() + LazyColumn( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + items(saved, key = { it.gameId }) { match -> + val title = GameCatalog.byId(match.gameId)?.title ?: match.gameId + Card(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(12.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text(title, fontWeight = FontWeight.Bold) + Text( + buildString { + append("Saved ${agoText(now, match.savedAtMillis)}") + if (match.summary.isNotBlank()) append(" · ${match.summary}") + }, + style = MaterialTheme.typography.bodySmall, + ) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Button(onClick = { onResume(match) }) { Text("Resume") } + TextButton(onClick = { deleting = match }) { Text("Delete") } + } + } + } + } + } + } + } + + deleting?.let { match -> + val title = GameCatalog.byId(match.gameId)?.title ?: match.gameId + AlertDialog( + onDismissRequest = { deleting = null }, + title = { Text("Delete this game?") }, + text = { Text("Your saved game of $title will be removed and cannot be resumed.") }, + confirmButton = { + TextButton(onClick = { deleting = null; onDelete(match) }) { Text("Delete") } + }, + dismissButton = { TextButton(onClick = { deleting = null }) { Text("Keep it") } }, + ) + } +} diff --git a/app/src/main/java/org/prolibertate/games/ui/game/CrazyEightsScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/CrazyEightsScreen.kt index 6511970..2477f5f 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/CrazyEightsScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/CrazyEightsScreen.kt @@ -60,6 +60,22 @@ fun CrazyEightsScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() // Playing an eight is two decisions: the card, then the suit it turns the @@ -68,13 +84,15 @@ fun CrazyEightsScreen( val current = state if (current == null) { - ScreenScaffold(title = "Crazy 8s", onBack = onExit) { modifier -> + ScreenScaffold(title = "Crazy 8s", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return } - ScreenScaffold(title = "Crazy 8s", onBack = onExit) { modifier -> + ScreenScaffold(title = "Crazy 8s", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val cardWidth = (minOf(maxWidth, maxHeight) * 0.16f).coerceIn(44.dp, 92.dp) diff --git a/app/src/main/java/org/prolibertate/games/ui/game/CribbageScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/CribbageScreen.kt index 920cd67..a5d89de 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/CribbageScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/CribbageScreen.kt @@ -65,11 +65,28 @@ fun CribbageScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() val current = state if (current == null) { - ScreenScaffold(title = "Cribbage", onBack = onExit) { modifier -> + ScreenScaffold(title = "Cribbage", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return @@ -79,7 +96,8 @@ fun CribbageScreen( // together. Forgotten whenever a new hand is dealt. var chosen by remember(current.handNumber) { mutableStateOf(emptySet()) } - ScreenScaffold(title = "Cribbage", onBack = onExit) { modifier -> + ScreenScaffold(title = "Cribbage", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val cardWidth = (minOf(maxWidth, maxHeight) * 0.16f).coerceIn(44.dp, 92.dp) diff --git a/app/src/main/java/org/prolibertate/games/ui/game/EuchreScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/EuchreScreen.kt index 40bfe03..b638bb5 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/EuchreScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/EuchreScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.LaunchedEffect @@ -72,18 +73,36 @@ fun EuchreScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() val notice by controller.notice.collectAsState() val current = state if (current == null) { - ScreenScaffold(title = "Euchre", onBack = onExit) { modifier -> + ScreenScaffold(title = "Euchre", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return } - ScreenScaffold(title = "Euchre", onBack = onExit) { modifier -> + ScreenScaffold(title = "Euchre", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val compact = maxWidth < 600.dp val cardWidth = (minOf(maxWidth, maxHeight) * if (compact) 0.17f else 0.12f) diff --git a/app/src/main/java/org/prolibertate/games/ui/game/GolfScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/GolfScreen.kt index 5b3a782..1bf6ea4 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/GolfScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/GolfScreen.kt @@ -69,18 +69,36 @@ fun GolfScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() var mode by remember { mutableStateOf(PlaceMode.SWAP) } val current = state if (current == null) { - ScreenScaffold(title = "Golf", onBack = onExit) { modifier -> + ScreenScaffold(title = "Golf", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return } - ScreenScaffold(title = "Golf", onBack = onExit) { modifier -> + ScreenScaffold(title = "Golf", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val shortest = minOf(maxWidth, maxHeight) val ownCard = (shortest * 0.16f).coerceIn(40.dp, 84.dp) diff --git a/app/src/main/java/org/prolibertate/games/ui/game/KaiserScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/KaiserScreen.kt index 3568f59..3e97399 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/KaiserScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/KaiserScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -74,18 +75,36 @@ fun KaiserScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() val notice by controller.notice.collectAsState() val current = state if (current == null) { - ScreenScaffold(title = "Kaiser", onBack = onExit) { modifier -> + ScreenScaffold(title = "Kaiser", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return } - ScreenScaffold(title = "Kaiser", onBack = onExit) { modifier -> + ScreenScaffold(title = "Kaiser", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val compact = maxWidth < 600.dp val cardWidth = (minOf(maxWidth, maxHeight) * if (compact) 0.15f else 0.11f) diff --git a/app/src/main/java/org/prolibertate/games/ui/game/PresidentScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/PresidentScreen.kt index 5b9a2ac..7a51a1a 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/PresidentScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/PresidentScreen.kt @@ -20,11 +20,15 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.TextButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.key +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate @@ -66,16 +70,34 @@ fun PresidentScreen( ) { val state by controller.state.collectAsState() val legal by controller.legalMoves.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val current = state if (current == null) { - ScreenScaffold(title = "President", onBack = onExit) { modifier -> + ScreenScaffold(title = "President", onBack = onExit, actions = endGameAction) { modifier -> Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } + dialogs() } return } - ScreenScaffold(title = "President", onBack = onExit) { modifier -> + ScreenScaffold(title = "President", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val cardWidth = (minOf(maxWidth, maxHeight) * 0.15f).coerceIn(44.dp, 88.dp) diff --git a/app/src/main/java/org/prolibertate/games/ui/game/SequenceScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/SequenceScreen.kt index afa4a21..72c794f 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/SequenceScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/SequenceScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -81,12 +82,29 @@ fun SequenceScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() var selected by remember { mutableStateOf(null) } val current = state if (current == null) { - ScreenScaffold(title = "Sequence", onBack = onExit) { modifier -> + ScreenScaffold(title = "Sequence", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return @@ -97,7 +115,8 @@ fun SequenceScreen( val removals = legal.filterIsInstance().filter { it.card == selected } val highlighted = (placements.map { it.cell } + removals.map { it.cell }).toSet() - ScreenScaffold(title = "Sequence", onBack = onExit) { modifier -> + ScreenScaffold(title = "Sequence", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val wide = maxWidth > maxHeight val boardSide = if (wide) { diff --git a/app/src/main/java/org/prolibertate/games/ui/game/WizardScreen.kt b/app/src/main/java/org/prolibertate/games/ui/game/WizardScreen.kt index 6227fc7..da8cafd 100644 --- a/app/src/main/java/org/prolibertate/games/ui/game/WizardScreen.kt +++ b/app/src/main/java/org/prolibertate/games/ui/game/WizardScreen.kt @@ -75,18 +75,36 @@ fun WizardScreen( onExit: () -> Unit, ) { val state by controller.state.collectAsState() + val abandoned by controller.abandoned.collectAsState() + var confirmingEnd by remember { mutableStateOf(false) } + + val endGameAction: @Composable () -> Unit = { + TextButton(onClick = { confirmingEnd = true }) { Text("End game") } + } + val dialogs: @Composable () -> Unit = { + abandoned?.let { PatienceLeftDialog(it, onExit, controller::dismissAbandoned) } + if (confirmingEnd) { + PatienceEndDialog( + what = "The game ends here. Anyone else at the table is told you have left.", + onEnd = onExit, + onKeepPlaying = { confirmingEnd = false }, + ) + } + } val legal by controller.legalMoves.collectAsState() val notice by controller.notice.collectAsState() val current = state if (current == null) { - ScreenScaffold(title = "Wizard", onBack = onExit) { modifier -> + ScreenScaffold(title = "Wizard", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() Box(modifier, contentAlignment = Alignment.Center) { Text("Dealing…") } } return } - ScreenScaffold(title = "Wizard", onBack = onExit) { modifier -> + ScreenScaffold(title = "Wizard", onBack = onExit, actions = endGameAction) { modifier -> + dialogs() BoxWithConstraints(modifier = modifier) { val cardWidth = (minOf(maxWidth, maxHeight) * 0.14f).coerceIn(40.dp, 88.dp) -- 2.47.3