Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-12-16 22:23:08 -06:00
parent bde7b0bd3f
commit 754654665c
4 changed files with 64 additions and 28 deletions

View File

@ -388,13 +388,9 @@ final class GameState {
bankerValue: bankerHandValue
))
// Show result banner
// Show result banner - stays until user taps New Round
showResultBanner = true
try? await Task.sleep(for: .seconds(2))
currentPhase = .roundComplete
showResultBanner = false
isAnimating = false
}
@ -402,6 +398,9 @@ final class GameState {
func newRound() {
guard currentPhase == .roundComplete else { return }
// Dismiss result banner
showResultBanner = false
currentBets = []
visiblePlayerCards = []
visibleBankerCards = []

View File

@ -81,7 +81,7 @@ struct GameTableView: View {
)
.padding(.horizontal, Design.Spacing.medium)
Spacer(minLength: Design.Spacing.small)
Spacer(minLength: Design.Spacing.medium)
// Chip selector - shows higher chips as you win more!
ChipSelectorView(
@ -89,7 +89,8 @@ struct GameTableView: View {
balance: state.balance,
maxBet: state.maxBet
)
.padding(.bottom, Design.Spacing.medium)
Spacer(minLength: Design.Spacing.small)
// Action buttons
ActionButtonsView(
@ -114,7 +115,8 @@ struct GameTableView: View {
totalWinnings: state.lastWinnings,
betResults: state.betResults,
playerHadPair: state.playerHadPair,
bankerHadPair: state.bankerHadPair
bankerHadPair: state.bankerHadPair,
onNewRound: { state.newRound() }
)
.transition(.opacity)
@ -660,9 +662,9 @@ struct ActionButtonsView: View {
// MARK: - Fixed font sizes for action buttons
// Fixed because buttons have constrained space and must remain usable
private let buttonFontSize: CGFloat = 16
private let iconSize: CGFloat = 24
private let statusFontSize: CGFloat = 14
private let buttonFontSize: CGFloat = Design.BaseFontSize.xLarge
private let iconSize: CGFloat = Design.BaseFontSize.xxLarge + Design.Spacing.xSmall
private let statusFontSize: CGFloat = Design.BaseFontSize.medium
var body: some View {
HStack(spacing: Design.Spacing.medium) {
@ -672,10 +674,11 @@ struct ActionButtonsView: View {
// Deal button - icon only at accessibility sizes
dealButton
} else if gameState.currentPhase == .roundComplete {
// New round button - icon only at accessibility sizes
} else if gameState.currentPhase == .roundComplete && !gameState.showResultBanner {
// New round button - only shown after banner is dismissed
// (The banner itself has a New Round button)
newRoundButton
} else {
} else if !gameState.showResultBanner {
// Playing indicator
HStack(spacing: Design.Spacing.xSmall) {
ProgressView()
@ -700,7 +703,7 @@ struct ActionButtonsView: View {
.labelStyle(.iconOnly)
.font(.system(size: iconSize, weight: .semibold))
.foregroundStyle(.white)
.padding(Design.Spacing.medium)
.padding(Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Circle()
.fill(Color.Button.destructive)
@ -712,8 +715,8 @@ struct ActionButtonsView: View {
.labelStyle(.titleOnly)
.font(.system(size: buttonFontSize, weight: .semibold))
.foregroundStyle(.white)
.padding(.horizontal, Design.Spacing.xLarge)
.padding(.vertical, Design.Spacing.medium)
.padding(.horizontal, Design.Spacing.xxLarge)
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Capsule()
.fill(Color.Button.destructive)
@ -730,7 +733,7 @@ struct ActionButtonsView: View {
.labelStyle(.iconOnly)
.font(.system(size: iconSize, weight: .bold))
.foregroundStyle(.black)
.padding(Design.Spacing.medium)
.padding(Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Circle()
.fill(
@ -749,8 +752,8 @@ struct ActionButtonsView: View {
.labelStyle(.titleOnly)
.font(.system(size: buttonFontSize, weight: .bold))
.foregroundStyle(.black)
.padding(.horizontal, Design.Spacing.xxxLarge)
.padding(.vertical, Design.Spacing.medium)
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Capsule()
.fill(
@ -774,7 +777,7 @@ struct ActionButtonsView: View {
.labelStyle(.iconOnly)
.font(.system(size: iconSize, weight: .bold))
.foregroundStyle(.black)
.padding(Design.Spacing.medium)
.padding(Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Circle()
.fill(
@ -791,8 +794,8 @@ struct ActionButtonsView: View {
.labelStyle(.titleOnly)
.font(.system(size: buttonFontSize, weight: .bold))
.foregroundStyle(.black)
.padding(.horizontal, Design.Spacing.xxxLarge)
.padding(.vertical, Design.Spacing.medium)
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Capsule()
.fill(

View File

@ -15,11 +15,13 @@ struct ResultBannerView: View {
let betResults: [BetResult]
var playerHadPair: Bool = false
var bankerHadPair: Bool = false
let onNewRound: () -> Void
@State private var showBanner = false
@State private var showText = false
@State private var showBreakdown = false
@State private var showTotal = false
@State private var showButton = false
// MARK: - Scaled Font Sizes (Dynamic Type)
@ -110,6 +112,31 @@ struct ResultBannerView: View {
.scaleEffect(showTotal ? Design.Scale.normal : Design.Scale.shrunk)
.opacity(showTotal ? Design.Scale.normal : 0)
}
// New Round button
Button {
onNewRound()
} label: {
Text("New Round")
.font(.system(size: Design.BaseFontSize.xLarge, weight: .bold))
.foregroundStyle(.black)
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
.background(
Capsule()
.fill(
LinearGradient(
colors: [Color.Button.goldLight, Color.Button.goldDark],
startPoint: .top,
endPoint: .bottom
)
)
)
.shadow(color: .yellow.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium)
}
.scaleEffect(showButton ? Design.Scale.normal : Design.Scale.shrunk)
.opacity(showButton ? Design.Scale.normal : 0)
.padding(.top, Design.Spacing.small)
}
.padding(.horizontal, Design.Spacing.xLarge)
.padding(.vertical, Design.Spacing.xxLarge)
@ -161,6 +188,11 @@ struct ResultBannerView: View {
showTotal = true
}
// Show button after everything else
withAnimation(.spring(duration: Design.Animation.springDuration, bounce: Design.Animation.springBounce).delay(Design.Animation.staggerDelay3 + Design.Animation.staggerDelay1)) {
showButton = true
}
// Announce result to VoiceOver users
announceResult()
}
@ -371,7 +403,8 @@ struct ConfettiView: View {
BetResult(type: .tie, amount: 500, payout: -500)
],
playerHadPair: true,
bankerHadPair: false
bankerHadPair: false,
onNewRound: {}
)
}
}

View File

@ -35,14 +35,14 @@ public struct ChipSelectorView: View {
public var body: some View {
ScrollView(.horizontal) {
HStack(spacing: CasinoDesign.Spacing.small) {
HStack(spacing: CasinoDesign.Spacing.medium) {
ForEach(availableChips) { denomination in
Button {
selectedChip = denomination
} label: {
ChipView(
denomination: denomination,
size: CasinoDesign.Size.chipMedium,
size: CasinoDesign.Size.chipLarge,
isSelected: selectedChip == denomination,
theme: theme
)
@ -52,10 +52,11 @@ public struct ChipSelectorView: View {
.disabled(balance < denomination.rawValue)
}
}
.padding(.horizontal)
.padding(.vertical, CasinoDesign.Spacing.small) // Extra padding for selection scale effect
.padding(.horizontal, CasinoDesign.Spacing.large)
.padding(.vertical, CasinoDesign.Spacing.medium) // Extra padding for selection scale effect
}
.scrollIndicators(.hidden)
.frame(maxWidth: .infinity) // Center the scroll content
.accessibilityElement(children: .contain)
.accessibilityLabel(String(localized: "Chip selector", bundle: .module))
.accessibilityHint(String(localized: "Double tap a chip to select bet amount", bundle: .module))