Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
21e5d901c7
commit
71b27b671b
@ -56,6 +56,10 @@ final class GameState {
|
||||
/// Whether to show the gameplay hint toast.
|
||||
var showHintToast: Bool = false
|
||||
|
||||
/// Tracks the current hint display session to prevent race conditions.
|
||||
/// When a new hint arrives or is shown, increment this so old dismiss tasks become stale.
|
||||
var hintDisplayID: UUID = UUID()
|
||||
|
||||
/// Whether a reshuffle notification should be shown.
|
||||
var showReshuffleNotification: Bool = false
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ enum Design {
|
||||
static let showDebugBorders = false
|
||||
|
||||
/// Set to true to show debug log statements
|
||||
static let showDebugLogs = false
|
||||
static let showDebugLogs = true
|
||||
|
||||
/// Debug logger - only prints when showDebugLogs is true
|
||||
static func debugLog(_ message: String) {
|
||||
@ -84,7 +84,7 @@ enum Design {
|
||||
|
||||
// Result banner
|
||||
static let resultRowAmountWidth: CGFloat = 70
|
||||
static let resultRowResultWidth: CGFloat = 150
|
||||
static let resultRowResultWidth: CGFloat = 120
|
||||
|
||||
// Side bet zones
|
||||
static let sideBetLabelFontSize: CGFloat = 13
|
||||
|
||||
@ -87,18 +87,25 @@ struct GameTableView: View {
|
||||
icon: "lightbulb.fill",
|
||||
accessibilityLabel: String(localized: "Show Hint")
|
||||
) {
|
||||
// Generate new ID to invalidate any pending dismiss tasks
|
||||
let currentID = UUID()
|
||||
state.hintDisplayID = currentID
|
||||
|
||||
// Show the toast with animation
|
||||
withAnimation(.spring(duration: Design.Animation.springDuration)) {
|
||||
state.showHintToast = true
|
||||
}
|
||||
// Auto-dismiss after delay (same as auto-show behavior)
|
||||
// Auto-dismiss after delay, but only if this is still the active hint session
|
||||
Task { @MainActor in
|
||||
try? await Task.sleep(for: Design.Toast.duration)
|
||||
// Only dismiss if no newer hint has arrived
|
||||
if state.hintDisplayID == currentID {
|
||||
withAnimation(.spring(duration: Design.Animation.springDuration)) {
|
||||
state.showHintToast = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ struct BlackjackTableView: View {
|
||||
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
|
||||
|
||||
// MARK: - Computed from stable screen size
|
||||
|
||||
private var screenWidth: CGFloat { fullScreenSize.width }
|
||||
@ -152,18 +153,26 @@ struct BlackjackTableView: View {
|
||||
.onChange(of: state.currentHint) { oldHint, newHint in
|
||||
// Show toast when a new hint appears
|
||||
if let hint = newHint, hint != oldHint {
|
||||
// Generate new ID to invalidate any pending dismiss tasks
|
||||
let currentID = UUID()
|
||||
state.hintDisplayID = currentID
|
||||
|
||||
withAnimation(.spring(duration: Design.Animation.springDuration)) {
|
||||
state.showHintToast = true
|
||||
}
|
||||
// Auto-dismiss after delay
|
||||
// Auto-dismiss after delay, but only if this is still the active hint session
|
||||
Task { @MainActor in
|
||||
try? await Task.sleep(for: Design.Toast.duration)
|
||||
// Only dismiss if no newer hint has arrived
|
||||
if state.hintDisplayID == currentID {
|
||||
withAnimation(.spring(duration: Design.Animation.springDuration)) {
|
||||
state.showHintToast = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if newHint == nil {
|
||||
// Hide immediately when no hint
|
||||
state.hintDisplayID = UUID() // Invalidate any pending dismiss tasks
|
||||
withAnimation(.spring(duration: Design.Animation.springDuration)) {
|
||||
state.showHintToast = false
|
||||
}
|
||||
|
||||
@ -16,27 +16,32 @@ struct DealerHandView: View {
|
||||
let cardSpacing: CGFloat
|
||||
|
||||
@ScaledMetric(relativeTo: .headline) private var labelFontSize: CGFloat = Design.Size.handLabelFontSize
|
||||
@ScaledMetric(relativeTo: .headline) private var badgeHeight: CGFloat = CasinoDesign.Size.valueBadge
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack(spacing: Design.Spacing.small) {
|
||||
// Label and value
|
||||
// Label and value - fixed height prevents vertical layout shift
|
||||
HStack(spacing: Design.Spacing.small) {
|
||||
Text(String(localized: "DEALER"))
|
||||
.font(.system(size: labelFontSize, weight: .bold, design: .rounded))
|
||||
.foregroundStyle(.white)
|
||||
|
||||
// Show value: full value when hole card visible, otherwise just the face-up card's value
|
||||
// Badge animates in when cards are dealt
|
||||
if !hand.cards.isEmpty {
|
||||
if showHoleCard {
|
||||
// All cards visible - show total hand value
|
||||
ValueBadge(value: hand.value, color: Color.Hand.dealer)
|
||||
.transition(.scale.combined(with: .opacity))
|
||||
} else {
|
||||
// Hole card hidden - show only the first (face-up) card's value
|
||||
ValueBadge(value: hand.cards[0].blackjackValue, color: Color.Hand.dealer)
|
||||
.transition(.scale.combined(with: .opacity))
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minHeight: badgeHeight) // Reserve consistent height
|
||||
.animation(.spring(duration: Design.Animation.springDuration), value: hand.cards.isEmpty)
|
||||
// Cards
|
||||
HStack(spacing: hand.cards.isEmpty ? Design.Spacing.small : cardSpacing) {
|
||||
if hand.cards.isEmpty {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user