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

This commit is contained in:
Matt Bruce 2025-12-28 18:01:37 -06:00
parent dfe3bc4bba
commit 172e224a42

View File

@ -21,8 +21,10 @@ struct ActionButtonsView: View {
switch state.currentPhase { switch state.currentPhase {
case .betting: case .betting:
bettingButtons bettingButtons
.transition(.opacity.combined(with: .scale(scale: 0.9)))
case .playerTurn: case .playerTurn:
playerTurnButtons playerTurnButtons
.transition(.opacity.combined(with: .scale(scale: 0.9)))
case .roundComplete: case .roundComplete:
// Empty - handled by result banner // Empty - handled by result banner
EmptyView() EmptyView()
@ -31,6 +33,7 @@ struct ActionButtonsView: View {
EmptyView() EmptyView()
} }
} }
.animation(.spring(duration: Design.Animation.springDuration), value: state.currentPhase)
} }
.frame(height: containerHeight) .frame(height: containerHeight)
.padding(.horizontal, Design.Spacing.large) .padding(.horizontal, Design.Spacing.large)
@ -51,54 +54,86 @@ struct ActionButtonsView: View {
// MARK: - Player Turn Buttons // MARK: - Player Turn Buttons
/// Available player actions based on current game state.
private var availableActions: [PlayerAction] {
PlayerAction.allCases.filter { action in
switch action {
case .hit: state.canHit
case .stand: state.canStand
case .double: state.canDouble
case .split: state.canSplit
case .surrender: state.canSurrender
}
}
}
/// Tracks available actions for animation purposes.
@State private var animatedActions: [PlayerAction] = []
@ViewBuilder @ViewBuilder
private var playerTurnButtons: some View { private var playerTurnButtons: some View {
// All player actions in a single row
HStack(spacing: Design.Spacing.medium) { HStack(spacing: Design.Spacing.medium) {
if state.canHit { ForEach(animatedActions) { action in
ActionButton( ActionButton(
String(localized: "Hit"), action.title,
style: .custom(Color.Button.hit) style: .custom(action.color)
) { ) {
Task { await state.hit() } Task { await performAction(action) }
}
.transition(.scale.combined(with: .opacity))
}
}
.onAppear {
animatedActions = availableActions
}
.onChange(of: availableActions) { _, newActions in
withAnimation(.spring(duration: Design.Animation.springDuration, bounce: 0.15)) {
animatedActions = newActions
}
} }
} }
if state.canStand { /// Performs the given player action.
ActionButton( private func performAction(_ action: PlayerAction) async {
String(localized: "Stand"), switch action {
style: .custom(Color.Button.stand) case .hit: await state.hit()
) { case .stand: await state.stand()
Task { await state.stand() } case .double: await state.doubleDown()
case .split: await state.split()
case .surrender: await state.surrender()
}
}
}
// MARK: - Player Action
/// Represents a player action button during their turn.
private enum PlayerAction: String, CaseIterable, Identifiable {
case hit
case stand
case double
case split
case surrender
var id: String { rawValue }
var title: String {
switch self {
case .hit: String(localized: "Hit")
case .stand: String(localized: "Stand")
case .double: String(localized: "Double")
case .split: String(localized: "Split")
case .surrender: String(localized: "Surrender")
} }
} }
if state.canDouble { var color: Color {
ActionButton( switch self {
String(localized: "Double"), case .hit: Color.Button.hit
style: .custom(Color.Button.doubleDown) case .stand: Color.Button.stand
) { case .double: Color.Button.doubleDown
Task { await state.doubleDown() } case .split: Color.Button.split
} case .surrender: Color.Button.surrender
}
if state.canSplit {
ActionButton(
String(localized: "Split"),
style: .custom(Color.Button.split)
) {
Task { await state.split() }
}
}
if state.canSurrender {
ActionButton(
String(localized: "Surrender"),
style: .custom(Color.Button.surrender)
) {
Task { await state.surrender() }
}
}
} }
} }
} }