diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e38bd..09be314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Unified the recent win/loss indicator across the games. Coinflip, Mines, + Crash and Blackjack now share one `WinLossChip` / `RecentResultsStrip` + (the Coinflip lettered W / L / P style); Crash's plain colored squares and + Blackjack's solid chips now match. Roulette keeps its pocket-number strip. + ### Added - Real "Last Played" highlight on the lobby (cu-03): the new diff --git a/app/src/main/java/com/plainstudio/stackcasino/feature/blackjack/BlackjackScreen.kt b/app/src/main/java/com/plainstudio/stackcasino/feature/blackjack/BlackjackScreen.kt index 6cfc15b..69227c6 100644 --- a/app/src/main/java/com/plainstudio/stackcasino/feature/blackjack/BlackjackScreen.kt +++ b/app/src/main/java/com/plainstudio/stackcasino/feature/blackjack/BlackjackScreen.kt @@ -35,9 +35,9 @@ import com.plainstudio.stackcasino.domain.game.BlackjackMath import com.plainstudio.stackcasino.ui.components.GameActionButton import com.plainstudio.stackcasino.ui.components.GameHeader import com.plainstudio.stackcasino.ui.components.OutcomeTone +import com.plainstudio.stackcasino.ui.components.RecentResultsStrip import com.plainstudio.stackcasino.ui.components.StatCell import com.plainstudio.stackcasino.ui.theme.AccentViolet -import com.plainstudio.stackcasino.ui.theme.CenteredGlyph import com.plainstudio.stackcasino.ui.theme.DISABLED_ALPHA import com.plainstudio.stackcasino.ui.theme.MetaFontSize import com.plainstudio.stackcasino.ui.theme.ScreenHorizontalPadding @@ -88,7 +88,16 @@ fun BlackjackScreen( onBack = onBack, onOpenRules = { showRules = true }, ) - RecentStrip(results = state.recentResults) + RecentResultsStrip( + results = + state.recentResults.map { result -> + when (result) { + BlackjackResult.Win -> OutcomeTone.Win + BlackjackResult.Loss -> OutcomeTone.Loss + BlackjackResult.Push -> OutcomeTone.Push + } + }, + ) StatsStrip(state = state) Table(state = state, modifier = Modifier.weight(1f)) BlackjackControls( @@ -109,44 +118,6 @@ fun BlackjackScreen( } } -@Composable -private fun RecentStrip(results: List) { - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = ScreenHorizontalPadding, vertical = 6.dp), - verticalArrangement = Arrangement.spacedBy(4.dp), - ) { - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text(text = "Recent", color = TextLow, fontSize = TinyFontSize, letterSpacing = TrackedLetterSpacing) - Text( - text = if (results.isEmpty()) "no rounds yet" else "${results.size} rounds", - color = TextLow, - fontSize = TinyFontSize, - letterSpacing = TrackedLetterSpacing, - style = TabularNums, - ) - } - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { - results.forEach { result -> HistoryChip(result = result) } - } - } -} - -@Composable -private fun HistoryChip(result: BlackjackResult) { - val (label, color) = - when (result) { - BlackjackResult.Win -> "W" to SemanticOk - BlackjackResult.Loss -> "L" to SemanticDanger - BlackjackResult.Push -> "P" to TextMedium - } - Box( - modifier = Modifier.size(HistoryChipSize).background(color), - contentAlignment = Alignment.Center, - ) { - Text(text = label, color = Color.White, fontSize = 10.sp, fontWeight = FontWeight.Bold, style = CenteredGlyph) - } -} - @Composable private fun StatsStrip(state: BlackjackUiState) { val profitColor = if (state.sessionProfitPositive) SemanticOk else SemanticDanger @@ -482,7 +453,6 @@ private val BET_PRESETS = listOf(5, 10, 25, 100) private val BetTextStyle = TextStyle(color = TextHigh, fontSize = 14.sp, fontFeatureSettings = "tnum") private val ControlsVerticalPadding = 12.dp -private val HistoryChipSize = 20.dp private val HandMinHeight = 72.dp private val CardWidth = 44.dp private val CardHeight = 62.dp diff --git a/app/src/main/java/com/plainstudio/stackcasino/feature/coinflip/CoinflipScreen.kt b/app/src/main/java/com/plainstudio/stackcasino/feature/coinflip/CoinflipScreen.kt index 828a4af..4ceab8c 100644 --- a/app/src/main/java/com/plainstudio/stackcasino/feature/coinflip/CoinflipScreen.kt +++ b/app/src/main/java/com/plainstudio/stackcasino/feature/coinflip/CoinflipScreen.kt @@ -42,12 +42,11 @@ import androidx.compose.ui.unit.sp import com.plainstudio.stackcasino.domain.game.CoinSide import com.plainstudio.stackcasino.ui.components.GameActionButton import com.plainstudio.stackcasino.ui.components.GameHeader +import com.plainstudio.stackcasino.ui.components.OutcomeTone +import com.plainstudio.stackcasino.ui.components.RecentResultsStrip import com.plainstudio.stackcasino.ui.components.StatCell import com.plainstudio.stackcasino.ui.theme.AccentViolet import com.plainstudio.stackcasino.ui.theme.AccentVioletSoft -import com.plainstudio.stackcasino.ui.theme.CHIP_BORDER_ALPHA -import com.plainstudio.stackcasino.ui.theme.CHIP_TINT_ALPHA -import com.plainstudio.stackcasino.ui.theme.CenteredGlyph import com.plainstudio.stackcasino.ui.theme.MetaFontSize import com.plainstudio.stackcasino.ui.theme.ScreenHorizontalPadding import com.plainstudio.stackcasino.ui.theme.SemanticDanger @@ -92,7 +91,7 @@ fun CoinflipScreen( onBack = onBack, onOpenRules = { showRules = true }, ) - RecentStrip(results = state.recentResults) + RecentResultsStrip(results = state.recentResults.map { if (it) OutcomeTone.Win else OutcomeTone.Loss }) StatsStrip(state = state) CoinStage(modifier = Modifier.weight(1f), state = state) CoinflipControls( @@ -111,49 +110,6 @@ fun CoinflipScreen( } } -@Composable -private fun RecentStrip(results: List) { - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = ScreenHorizontalPadding, vertical = 6.dp), - verticalArrangement = Arrangement.spacedBy(6.dp), - ) { - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text(text = "Recent", color = TextLow, fontSize = TinyFontSize, letterSpacing = TrackedLetterSpacing) - Text( - text = if (results.isEmpty()) "no rounds yet" else "${results.size} rounds", - color = TextLow, - fontSize = TinyFontSize, - letterSpacing = TrackedLetterSpacing, - style = TabularNums, - ) - } - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { - results.forEach { won -> RecentChip(won = won) } - } - } -} - -@Composable -private fun RecentChip(won: Boolean) { - val accent = if (won) SemanticOk else SemanticDanger - Box( - modifier = - Modifier - .size(RecentChipSize) - .background(accent.copy(alpha = CHIP_TINT_ALPHA)) - .border(width = 1.dp, color = accent.copy(alpha = CHIP_BORDER_ALPHA)), - contentAlignment = Alignment.Center, - ) { - Text( - text = if (won) "W" else "L", - color = accent, - fontSize = TinyFontSize, - fontWeight = FontWeight.Bold, - style = CenteredGlyph, - ) - } -} - @Composable private fun StatsStrip(state: CoinflipUiState) { val profitColor = if (state.sessionProfitPositive) SemanticOk else SemanticDanger @@ -414,7 +370,6 @@ private const val SHARE_MESSAGE = private val BET_PRESETS = listOf(5, 10, 25, 100) private val ControlsVerticalPadding = 12.dp -private val RecentChipSize = 20.dp private val CoinSize = 180.dp private val CoinLabelOffset = 60.dp diff --git a/app/src/main/java/com/plainstudio/stackcasino/feature/crash/CrashScreen.kt b/app/src/main/java/com/plainstudio/stackcasino/feature/crash/CrashScreen.kt index 2790e3d..622140d 100644 --- a/app/src/main/java/com/plainstudio/stackcasino/feature/crash/CrashScreen.kt +++ b/app/src/main/java/com/plainstudio/stackcasino/feature/crash/CrashScreen.kt @@ -40,6 +40,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.plainstudio.stackcasino.ui.components.GameActionButton import com.plainstudio.stackcasino.ui.components.GameHeader +import com.plainstudio.stackcasino.ui.components.OutcomeTone +import com.plainstudio.stackcasino.ui.components.RecentResultsStrip import com.plainstudio.stackcasino.ui.components.StatCell import com.plainstudio.stackcasino.ui.components.gridBackground import com.plainstudio.stackcasino.ui.theme.AccentViolet @@ -94,7 +96,7 @@ fun CrashScreen( onBack = onBack, onOpenRules = { showRules = true }, ) - RecentStrip(results = state.recentResults) + RecentResultsStrip(results = state.recentResults.map { if (it) OutcomeTone.Win else OutcomeTone.Loss }) StatsStrip(state = state, multiplier = multiplier) CrashChart(state = state, multiplier = multiplier, modifier = Modifier.weight(1f)) CrashControls( @@ -115,38 +117,6 @@ fun CrashScreen( } } -@Composable -private fun RecentStrip(results: List) { - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = ScreenHorizontalPadding, vertical = 6.dp), - verticalArrangement = Arrangement.spacedBy(6.dp), - ) { - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text(text = "Recent", color = TextLow, fontSize = TinyFontSize, letterSpacing = TrackedLetterSpacing) - Text( - text = if (results.isEmpty()) "no rounds yet" else "${results.size} rounds", - color = TextLow, - fontSize = TinyFontSize, - letterSpacing = TrackedLetterSpacing, - style = TabularNums, - ) - } - Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) { - results.forEach { cashed -> RecentChip(cashed = cashed) } - } - } -} - -@Composable -private fun RecentChip(cashed: Boolean) { - Box( - modifier = - Modifier - .size(RecentChipSize) - .background(if (cashed) SemanticOk else SemanticDanger), - ) -} - @Composable private fun StatsStrip( state: CrashUiState, @@ -446,7 +416,6 @@ private val HeroStyle = ) private val ControlsVerticalPadding = 12.dp -private val RecentChipSize = 12.dp private val AutoCashoutWidth = 84.dp private val HeroFontSize = 56.sp diff --git a/app/src/main/java/com/plainstudio/stackcasino/feature/mines/MinesScreen.kt b/app/src/main/java/com/plainstudio/stackcasino/feature/mines/MinesScreen.kt index bffc0fe..9c74612 100644 --- a/app/src/main/java/com/plainstudio/stackcasino/feature/mines/MinesScreen.kt +++ b/app/src/main/java/com/plainstudio/stackcasino/feature/mines/MinesScreen.kt @@ -33,11 +33,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.plainstudio.stackcasino.ui.components.GameActionButton import com.plainstudio.stackcasino.ui.components.GameHeader +import com.plainstudio.stackcasino.ui.components.OutcomeTone +import com.plainstudio.stackcasino.ui.components.RecentResultsStrip import com.plainstudio.stackcasino.ui.components.StatCell import com.plainstudio.stackcasino.ui.theme.AccentViolet import com.plainstudio.stackcasino.ui.theme.CHIP_BORDER_ALPHA -import com.plainstudio.stackcasino.ui.theme.CHIP_TINT_ALPHA -import com.plainstudio.stackcasino.ui.theme.CenteredGlyph import com.plainstudio.stackcasino.ui.theme.MetaFontSize import com.plainstudio.stackcasino.ui.theme.ScreenHorizontalPadding import com.plainstudio.stackcasino.ui.theme.SemanticDanger @@ -86,7 +86,7 @@ fun MinesScreen( onBack = onBack, onOpenRules = { showRules = true }, ) - RecentStrip(results = state.recentResults) + RecentResultsStrip(results = state.recentResults.map { if (it) OutcomeTone.Win else OutcomeTone.Loss }) StatsStrip(state = state) MinesGrid(state = state, onRevealTile = onRevealTile, modifier = Modifier.weight(1f)) MinesControls( @@ -105,49 +105,6 @@ fun MinesScreen( } } -@Composable -private fun RecentStrip(results: List) { - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = ScreenHorizontalPadding, vertical = 6.dp), - verticalArrangement = Arrangement.spacedBy(6.dp), - ) { - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text(text = "Recent", color = TextLow, fontSize = TinyFontSize, letterSpacing = TrackedLetterSpacing) - Text( - text = if (results.isEmpty()) "no rounds yet" else "${results.size} rounds", - color = TextLow, - fontSize = TinyFontSize, - letterSpacing = TrackedLetterSpacing, - style = TabularNums, - ) - } - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { - results.forEach { won -> RecentChip(won = won) } - } - } -} - -@Composable -private fun RecentChip(won: Boolean) { - val accent = if (won) SemanticOk else SemanticDanger - Box( - modifier = - Modifier - .size(RecentChipSize) - .background(accent.copy(alpha = CHIP_TINT_ALPHA)) - .border(width = 1.dp, color = accent.copy(alpha = CHIP_BORDER_ALPHA)), - contentAlignment = Alignment.Center, - ) { - Text( - text = if (won) "W" else "L", - color = accent, - fontSize = TinyFontSize, - fontWeight = FontWeight.Bold, - style = CenteredGlyph, - ) - } -} - @Composable private fun StatsStrip(state: MinesUiState) { val profitColor = if (state.sessionProfitPositive) SemanticOk else SemanticDanger @@ -479,7 +436,6 @@ private const val RISK_MAX = 1.0f private val RiskOrange = Color(0xFFF97316) private val GridHorizontalPadding = 24.dp private val ControlsVerticalPadding = 12.dp -private val RecentChipSize = 18.dp private val GridGap = 6.dp private val TileDotSize = 12.dp private val ProgressBarHeight = 4.dp diff --git a/app/src/main/java/com/plainstudio/stackcasino/ui/components/RecentResultsStrip.kt b/app/src/main/java/com/plainstudio/stackcasino/ui/components/RecentResultsStrip.kt new file mode 100644 index 0000000..b26ef4e --- /dev/null +++ b/app/src/main/java/com/plainstudio/stackcasino/ui/components/RecentResultsStrip.kt @@ -0,0 +1,49 @@ +package com.plainstudio.stackcasino.ui.components + +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.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.plainstudio.stackcasino.ui.theme.ScreenHorizontalPadding +import com.plainstudio.stackcasino.ui.theme.TabularNums +import com.plainstudio.stackcasino.ui.theme.TextLow +import com.plainstudio.stackcasino.ui.theme.TinyFontSize +import com.plainstudio.stackcasino.ui.theme.TrackedLetterSpacing + +/** + * Recent-rounds strip shared by every game screen except Roulette: a + * "Recent" / round-count header over a row of [WinLossChip]s, in the + * Coinflip style. + * + * @param results most-recent-first round outcomes; when empty the header + * shows a "no rounds yet" hint and no chips are drawn. + */ +@Composable +fun RecentResultsStrip( + results: List, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxWidth().padding(horizontal = ScreenHorizontalPadding, vertical = 6.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + Text(text = "Recent", color = TextLow, fontSize = TinyFontSize, letterSpacing = TrackedLetterSpacing) + Text( + text = if (results.isEmpty()) "no rounds yet" else "${results.size} rounds", + color = TextLow, + fontSize = TinyFontSize, + letterSpacing = TrackedLetterSpacing, + style = TabularNums, + ) + } + Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { + results.forEach { tone -> WinLossChip(tone = tone) } + } + } +} diff --git a/app/src/main/java/com/plainstudio/stackcasino/ui/components/WinLossChip.kt b/app/src/main/java/com/plainstudio/stackcasino/ui/components/WinLossChip.kt new file mode 100644 index 0000000..af5dc41 --- /dev/null +++ b/app/src/main/java/com/plainstudio/stackcasino/ui/components/WinLossChip.kt @@ -0,0 +1,64 @@ +package com.plainstudio.stackcasino.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.plainstudio.stackcasino.ui.theme.CHIP_BORDER_ALPHA +import com.plainstudio.stackcasino.ui.theme.CHIP_TINT_ALPHA +import com.plainstudio.stackcasino.ui.theme.CenteredGlyph +import com.plainstudio.stackcasino.ui.theme.SemanticDanger +import com.plainstudio.stackcasino.ui.theme.SemanticOk +import com.plainstudio.stackcasino.ui.theme.TextMedium +import com.plainstudio.stackcasino.ui.theme.TinyFontSize + +/** + * Square chip marking a past round outcome with a single letter (W / L / P) + * over a tint of the outcome color, matching the Coinflip recent-results + * style. Shared by every game's recent strip except Roulette, which shows + * colored pocket numbers instead. + * + * @param tone the round result that drives the letter and color. + */ +@Composable +fun WinLossChip( + tone: OutcomeTone, + modifier: Modifier = Modifier, +) { + val accent = + when (tone) { + OutcomeTone.Win -> SemanticOk + OutcomeTone.Loss -> SemanticDanger + OutcomeTone.Push -> TextMedium + } + val label = + when (tone) { + OutcomeTone.Win -> "W" + OutcomeTone.Loss -> "L" + OutcomeTone.Push -> "P" + } + Box( + modifier = + modifier + .size(WinLossChipSize) + .background(accent.copy(alpha = CHIP_TINT_ALPHA)) + .border(width = 1.dp, color = accent.copy(alpha = CHIP_BORDER_ALPHA)), + contentAlignment = Alignment.Center, + ) { + Text( + text = label, + color = accent, + fontSize = TinyFontSize, + fontWeight = FontWeight.Bold, + style = CenteredGlyph, + ) + } +} + +private val WinLossChipSize = 20.dp