diff --git a/Blackjack/Blackjack/Views/Game/ActionButtonsView.swift b/Blackjack/Blackjack/Views/Game/ActionButtonsView.swift index a71e51a..52fffe1 100644 --- a/Blackjack/Blackjack/Views/Game/ActionButtonsView.swift +++ b/Blackjack/Blackjack/Views/Game/ActionButtonsView.swift @@ -35,10 +35,8 @@ struct ActionButtonsView: View { EmptyView() } } - .animation(.spring(duration: Design.Animation.quick), value: state.currentPhase) - .animation(.easeInOut(duration: Design.Animation.standard), value: state.currentBet > 0) } - .frame(minHeight: containerHeight) + .frame(height: containerHeight) .padding(.horizontal, Design.Spacing.large) } diff --git a/Blackjack/Blackjack/Views/Game/GameTableView.swift b/Blackjack/Blackjack/Views/Game/GameTableView.swift index e458051..9f80b4c 100644 --- a/Blackjack/Blackjack/Views/Game/GameTableView.swift +++ b/Blackjack/Blackjack/Views/Game/GameTableView.swift @@ -130,20 +130,22 @@ struct GameTableView: View { ) .frame(maxWidth: maxContentWidth) - // Chip selector - only shown during betting phase - if state.currentPhase == .betting { - Spacer() - .debugBorder(showDebugBorders, color: .yellow, label: "ChipSpacer") - + // Chip selector - only shown during betting phase AND when result banner is NOT showing + if state.currentPhase == .betting && !state.showResultBanner { ChipSelectorView( selectedChip: $selectedChip, balance: state.balance, - currentBet: state.minBetForChipSelector, // Use min bet so chips stay enabled if any bet type can accept more + currentBet: state.minBetForChipSelector, maxBet: state.settings.maxBet ) .frame(maxWidth: maxContentWidth) - .transition(.opacity.combined(with: .move(edge: .bottom))) .debugBorder(showDebugBorders, color: .pink, label: "ChipSelector") + .onAppear { + print("🎰 Chip selector APPEARED (banner showing: \(state.showResultBanner))") + } + .onDisappear { + print("🎰 Chip selector DISAPPEARED") + } } // Action buttons - minimal spacing during player turn @@ -152,43 +154,78 @@ struct GameTableView: View { .padding(.bottom, Design.Spacing.small) .debugBorder(showDebugBorders, color: .blue, label: "ActionBtns") } - .frame(maxWidth: .infinity) + .frame(maxWidth: .infinity, alignment: .top) + .zIndex(1) + .onChange(of: state.currentPhase) { oldPhase, newPhase in + print("🔄 Phase changed: \(oldPhase) → \(newPhase)") + } // Insurance popup overlay (covers entire screen) if state.currentPhase == .insurance { - InsurancePopupView( - betAmount: state.currentBet / 2, - balance: state.balance, - onTake: { Task { await state.takeInsurance() } }, - onDecline: { state.declineInsurance() } - ) - .transition(.opacity.combined(with: .scale(scale: 0.9))) + Color.clear + .overlay(alignment: .center) { + InsurancePopupView( + betAmount: state.currentBet / 2, + balance: state.balance, + onTake: { Task { await state.takeInsurance() } }, + onDecline: { state.declineInsurance() } + ) + } + .ignoresSafeArea() + .allowsHitTesting(true) + .transition(.opacity.combined(with: .scale(scale: 0.9))) + .zIndex(100) } // Result banner overlay if state.showResultBanner, let result = state.lastRoundResult { - ResultBannerView( - result: result, - currentBalance: state.balance, - minBet: state.settings.minBet, - onNewRound: { state.newRound() }, - onPlayAgain: { state.resetGame() } - ) + Color.clear + .overlay(alignment: .center) { + ResultBannerView( + result: result, + currentBalance: state.balance, + minBet: state.settings.minBet, + onNewRound: { state.newRound() }, + onPlayAgain: { state.resetGame() } + ) + .onAppear { + print("🎯 RESULT BANNER APPEARED") + } + .onDisappear { + print("❌ RESULT BANNER DISAPPEARED") + } + } + .ignoresSafeArea() + .allowsHitTesting(true) + .zIndex(100) } // Confetti for wins (matching Baccarat pattern) if state.showResultBanner && (state.lastRoundResult?.totalWinnings ?? 0) > 0 { ConfettiView() + .zIndex(101) } // Game over if state.isGameOver && !state.showResultBanner { - GameOverView( - roundsPlayed: state.roundsPlayed, - onPlayAgain: { state.resetGame() } - ) + Color.clear + .overlay(alignment: .center) { + GameOverView( + roundsPlayed: state.roundsPlayed, + onPlayAgain: { state.resetGame() } + ) + } + .ignoresSafeArea() + .allowsHitTesting(true) + .zIndex(100) } } + .onChange(of: state.playerHands.count) { oldCount, newCount in + print("👥 Player hands count: \(oldCount) → \(newCount)") + } + .onChange(of: state.balance) { oldBalance, newBalance in + print("💰 Balance: \(oldBalance) → \(newBalance)") + } } } diff --git a/Blackjack/Blackjack/Views/Sheets/ResultBannerView.swift b/Blackjack/Blackjack/Views/Sheets/ResultBannerView.swift index ed2734e..33515ee 100644 --- a/Blackjack/Blackjack/Views/Sheets/ResultBannerView.swift +++ b/Blackjack/Blackjack/Views/Sheets/ResultBannerView.swift @@ -54,7 +54,6 @@ struct ResultBannerView: View { ZStack { // Full screen dark background Color.black.opacity(Design.Opacity.strong) - .ignoresSafeArea() // Content card VStack(spacing: Design.Spacing.xLarge) { @@ -193,18 +192,18 @@ struct ResultBannerView: View { .shadow(color: mainResultColor.opacity(Design.Opacity.hint), radius: Design.Shadow.radiusXLarge) .frame(maxWidth: CasinoDesign.Size.maxModalWidth) .padding(.horizontal, Design.Spacing.large) // Prevent clipping on sides - .scaleEffect(showContent ? 1.0 : 0.8) + .scaleEffect(showContent ? Design.Scale.normal : Design.Scale.slightShrink) .opacity(showContent ? 1.0 : 0) } .onAppear { - withAnimation(.spring(duration: Design.Animation.springDuration, bounce: 0.3)) { + withAnimation(.spring(duration: Design.Animation.springDuration, bounce: Design.Animation.springBounce)) { showContent = true } // Play game over sound if out of chips (after a delay so it doesn't overlap with result sound) if isGameOver { Task { - try? await Task.sleep(for: .seconds(1)) + try? await Task.sleep(for: .seconds(Design.Delay.gameOverSound)) SoundManager.shared.play(.gameOver) } } @@ -222,6 +221,8 @@ struct ResultRow: View { let result: HandResult var amount: Int? = nil + private var showDebugBorders: Bool { Design.showDebugBorders } + private var amountText: String? { guard let amount = amount else { return nil } if amount > 0 { @@ -243,24 +244,31 @@ struct ResultRow: View { var body: some View { HStack { Text(label) - .font(.system(size: Design.BaseFontSize.body)) + .font(.system(size: Design.BaseFontSize.medium)) .foregroundStyle(.white.opacity(Design.Opacity.strong)) + .debugBorder(showDebugBorders, color: .blue, label: "Label") Spacer() + .debugBorder(showDebugBorders, color: .yellow, label: "Spacer") // Show amount if provided if let amountText = amountText { Text(amountText) - .font(.system(size: Design.BaseFontSize.body, weight: .semibold, design: .rounded)) + .font(.system(size: Design.BaseFontSize.medium, weight: .semibold, design: .rounded)) .foregroundStyle(amountColor) - .frame(width: 70, alignment: .trailing) + .frame(width: Design.Size.resultRowAmountWidth, alignment: .trailing) + .debugBorder(showDebugBorders, color: .green, label: "Amount") } Text(result.displayText) - .font(.system(size: Design.BaseFontSize.body, weight: .bold)) + .font(.system(size: Design.BaseFontSize.large, weight: .bold)) .foregroundStyle(result.color) - .frame(width: 100, alignment: .trailing) + .frame(width: Design.Size.resultRowResultWidth, alignment: .trailing) + .lineLimit(1) + .minimumScaleFactor(Design.MinScaleFactor.comfortable) + .debugBorder(showDebugBorders, color: .red, label: "Result") } + .debugBorder(showDebugBorders, color: .white, label: "ResultRow") } } @@ -272,6 +280,8 @@ struct SideBetResultRow: View { let isWin: Bool let amount: Int + private var showDebugBorders: Bool { Design.showDebugBorders } + private var amountText: String { if amount > 0 { return "+$\(amount)" @@ -295,23 +305,28 @@ struct SideBetResultRow: View { var body: some View { HStack { Text(label) - .font(.system(size: Design.BaseFontSize.body)) + .font(.system(size: Design.BaseFontSize.medium)) .foregroundStyle(.white.opacity(Design.Opacity.strong)) + .debugBorder(showDebugBorders, color: .blue, label: "Label") Spacer() + .debugBorder(showDebugBorders, color: .yellow, label: "Spacer") Text(amountText) - .font(.system(size: Design.BaseFontSize.body, weight: .semibold, design: .rounded)) + .font(.system(size: Design.BaseFontSize.medium, weight: .semibold, design: .rounded)) .foregroundStyle(amountColor) - .frame(width: 70, alignment: .trailing) + .frame(width: Design.Size.resultRowAmountWidth, alignment: .trailing) + .debugBorder(showDebugBorders, color: .green, label: "Amount") Text(resultText) - .font(.system(size: Design.BaseFontSize.body, weight: .bold)) + .font(.system(size: Design.BaseFontSize.large, weight: .bold)) .foregroundStyle(resultColor) - .frame(width: 100, alignment: .trailing) + .frame(width: Design.Size.resultRowResultWidth, alignment: .trailing) .lineLimit(1) .minimumScaleFactor(Design.MinScaleFactor.comfortable) + .debugBorder(showDebugBorders, color: .red, label: "Result") } + .debugBorder(showDebugBorders, color: .white, label: "SideBetRow") } }