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

View File

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

View File

@ -15,11 +15,13 @@ struct ResultBannerView: View {
let betResults: [BetResult] let betResults: [BetResult]
var playerHadPair: Bool = false var playerHadPair: Bool = false
var bankerHadPair: Bool = false var bankerHadPair: Bool = false
let onNewRound: () -> Void
@State private var showBanner = false @State private var showBanner = false
@State private var showText = false @State private var showText = false
@State private var showBreakdown = false @State private var showBreakdown = false
@State private var showTotal = false @State private var showTotal = false
@State private var showButton = false
// MARK: - Scaled Font Sizes (Dynamic Type) // MARK: - Scaled Font Sizes (Dynamic Type)
@ -110,6 +112,31 @@ struct ResultBannerView: View {
.scaleEffect(showTotal ? Design.Scale.normal : Design.Scale.shrunk) .scaleEffect(showTotal ? Design.Scale.normal : Design.Scale.shrunk)
.opacity(showTotal ? Design.Scale.normal : 0) .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(.horizontal, Design.Spacing.xLarge)
.padding(.vertical, Design.Spacing.xxLarge) .padding(.vertical, Design.Spacing.xxLarge)
@ -161,6 +188,11 @@ struct ResultBannerView: View {
showTotal = true 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 // Announce result to VoiceOver users
announceResult() announceResult()
} }
@ -371,7 +403,8 @@ struct ConfettiView: View {
BetResult(type: .tie, amount: 500, payout: -500) BetResult(type: .tie, amount: 500, payout: -500)
], ],
playerHadPair: true, playerHadPair: true,
bankerHadPair: false bankerHadPair: false,
onNewRound: {}
) )
} }
} }

View File

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