diff --git a/Baccarat/Engine/GameState.swift b/Baccarat/Engine/GameState.swift index 953a1a1..4ecf734 100644 --- a/Baccarat/Engine/GameState.swift +++ b/Baccarat/Engine/GameState.swift @@ -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 = [] diff --git a/Baccarat/Views/GameTableView.swift b/Baccarat/Views/GameTableView.swift index d315166..b19e6f2 100644 --- a/Baccarat/Views/GameTableView.swift +++ b/Baccarat/Views/GameTableView.swift @@ -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( diff --git a/Baccarat/Views/ResultBannerView.swift b/Baccarat/Views/ResultBannerView.swift index 2cd8d51..2c8c9b9 100644 --- a/Baccarat/Views/ResultBannerView.swift +++ b/Baccarat/Views/ResultBannerView.swift @@ -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: {} ) } } diff --git a/CasinoKit/Sources/CasinoKit/Views/Chips/ChipSelectorView.swift b/CasinoKit/Sources/CasinoKit/Views/Chips/ChipSelectorView.swift index 0b52a61..51984f1 100644 --- a/CasinoKit/Sources/CasinoKit/Views/Chips/ChipSelectorView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Chips/ChipSelectorView.swift @@ -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))