208 lines
7.4 KiB
Swift
208 lines
7.4 KiB
Swift
//
|
|
// WelcomeSheet.swift
|
|
// CasinoKit
|
|
//
|
|
// First-launch welcome sheet for casino games.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
/// Welcome sheet shown on first launch of a game.
|
|
public struct WelcomeSheet: View {
|
|
let gameName: String
|
|
let gameEmoji: String
|
|
let features: [WelcomeFeature]
|
|
let onStartTutorial: () -> Void
|
|
let onStartPlaying: () -> Void
|
|
|
|
@ScaledMetric(relativeTo: .largeTitle) private var titleSize: CGFloat = CasinoDesign.BaseFontSize.xxLarge
|
|
@ScaledMetric(relativeTo: .title2) private var featureTitleSize: CGFloat = CasinoDesign.BaseFontSize.subheadline
|
|
@ScaledMetric(relativeTo: .body) private var bodySize: CGFloat = CasinoDesign.BaseFontSize.body
|
|
@ScaledMetric(relativeTo: .body) private var iconSize: CGFloat = CasinoDesign.IconSize.large
|
|
@ScaledMetric(relativeTo: .body) private var buttonPadding: CGFloat = CasinoDesign.Spacing.medium
|
|
|
|
/// Creates a welcome sheet with automatic onboarding state management.
|
|
///
|
|
/// This initializer handles the common pattern of:
|
|
/// - "Show Me How" → completes welcome and triggers hint display
|
|
/// - "Start Playing" → skips all hints and completes welcome
|
|
///
|
|
/// - Parameters:
|
|
/// - gameName: The name of the game to display
|
|
/// - gameEmoji: An emoji representing the game
|
|
/// - features: List of features to highlight
|
|
/// - onboarding: The onboarding state to manage (must have hint keys registered)
|
|
/// - onDismiss: Called after the sheet is dismissed
|
|
/// - onShowHints: Called when user chooses "Show Me How" - use this to trigger tooltip display
|
|
public init(
|
|
gameName: String,
|
|
gameEmoji: String = "🎰",
|
|
features: [WelcomeFeature],
|
|
onboarding: OnboardingState,
|
|
onDismiss: @escaping () -> Void,
|
|
onShowHints: @escaping () -> Void
|
|
) {
|
|
self.gameName = gameName
|
|
self.gameEmoji = gameEmoji
|
|
self.features = features
|
|
self.onStartTutorial = {
|
|
onboarding.completeWelcome()
|
|
onDismiss()
|
|
onShowHints()
|
|
}
|
|
self.onStartPlaying = {
|
|
onboarding.skipOnboarding()
|
|
onDismiss()
|
|
}
|
|
}
|
|
|
|
/// Creates a welcome sheet with custom callbacks for full control.
|
|
public init(
|
|
gameName: String,
|
|
gameEmoji: String = "🎰",
|
|
features: [WelcomeFeature],
|
|
onStartTutorial: @escaping () -> Void,
|
|
onStartPlaying: @escaping () -> Void
|
|
) {
|
|
self.gameName = gameName
|
|
self.gameEmoji = gameEmoji
|
|
self.features = features
|
|
self.onStartTutorial = onStartTutorial
|
|
self.onStartPlaying = onStartPlaying
|
|
}
|
|
|
|
public var body: some View {
|
|
SheetContainerView(
|
|
title: String(localized: "Welcome to \(gameName)!", bundle: .module),
|
|
content: {
|
|
ScrollView {
|
|
VStack(spacing: CasinoDesign.Spacing.xLarge) {
|
|
// Game icon/emoji
|
|
Text(gameEmoji)
|
|
.font(.system(size: 60))
|
|
.padding(.top, CasinoDesign.Spacing.medium)
|
|
|
|
// Features list
|
|
VStack(spacing: CasinoDesign.Spacing.large) {
|
|
ForEach(features) { feature in
|
|
FeatureRow(
|
|
feature: feature,
|
|
iconSize: iconSize,
|
|
titleSize: featureTitleSize,
|
|
bodySize: bodySize
|
|
)
|
|
}
|
|
}
|
|
|
|
// Action buttons
|
|
VStack(spacing: CasinoDesign.Spacing.medium) {
|
|
Button(action: onStartTutorial) {
|
|
HStack {
|
|
Image(systemName: "play.circle.fill")
|
|
Text("Show Me How")
|
|
}
|
|
.font(.system(size: bodySize, weight: .semibold))
|
|
.frame(maxWidth: .infinity)
|
|
.padding(buttonPadding)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: CasinoDesign.CornerRadius.large)
|
|
.fill(Color.Sheet.accent)
|
|
)
|
|
.foregroundStyle(.black)
|
|
}
|
|
|
|
Button(action: onStartPlaying) {
|
|
Text("Start Playing")
|
|
.font(.system(size: bodySize, weight: .medium))
|
|
.foregroundStyle(.white.opacity(CasinoDesign.Opacity.medium))
|
|
}
|
|
}
|
|
.padding(.top, CasinoDesign.Spacing.large)
|
|
.padding(.bottom, CasinoDesign.Spacing.xLarge)
|
|
}
|
|
.padding(.horizontal, CasinoDesign.Spacing.large)
|
|
}
|
|
},
|
|
onDone: onStartPlaying // Done button = Start Playing
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Feature Row
|
|
|
|
private struct FeatureRow: View {
|
|
let feature: WelcomeFeature
|
|
let iconSize: CGFloat
|
|
let titleSize: CGFloat
|
|
let bodySize: CGFloat
|
|
|
|
var body: some View {
|
|
HStack(spacing: CasinoDesign.Spacing.medium) {
|
|
// Icon
|
|
Image(systemName: feature.icon)
|
|
.font(.system(size: iconSize))
|
|
.foregroundStyle(Color.Sheet.accent)
|
|
.frame(width: 40, alignment: .center)
|
|
|
|
// Text
|
|
VStack(alignment: .leading, spacing: CasinoDesign.Spacing.xxSmall) {
|
|
Text(feature.title)
|
|
.font(.system(size: titleSize, weight: .semibold))
|
|
.foregroundStyle(.white)
|
|
|
|
Text(feature.description)
|
|
.font(.system(size: bodySize))
|
|
.foregroundStyle(.white.opacity(CasinoDesign.Opacity.strong))
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Spacer(minLength: 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Welcome Feature Model
|
|
|
|
public struct WelcomeFeature: Identifiable {
|
|
public let id = UUID()
|
|
let icon: String
|
|
let title: String
|
|
let description: String
|
|
|
|
public init(icon: String, title: String, description: String) {
|
|
self.icon = icon
|
|
self.title = title
|
|
self.description = description
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
|
|
#Preview {
|
|
WelcomeSheet(
|
|
gameName: "Casino Game",
|
|
gameEmoji: "🎰",
|
|
features: [
|
|
WelcomeFeature(
|
|
icon: "target",
|
|
title: "Exciting Gameplay",
|
|
description: "Experience the thrill of the casino"
|
|
),
|
|
WelcomeFeature(
|
|
icon: "lightbulb.fill",
|
|
title: "Learn Strategy",
|
|
description: "Built-in hints show optimal plays"
|
|
),
|
|
WelcomeFeature(
|
|
icon: "dollarsign.circle",
|
|
title: "Practice Free",
|
|
description: "Start with virtual chips and play risk-free"
|
|
)
|
|
],
|
|
onStartTutorial: {},
|
|
onStartPlaying: {}
|
|
)
|
|
}
|
|
|