139 lines
5.0 KiB
Swift
139 lines
5.0 KiB
Swift
import SwiftUI
|
|
import Bedrock
|
|
|
|
/// Educational screen shown at the end of the wizard explaining the app's main features.
|
|
struct WhatsNextStepView: View {
|
|
let onComplete: () -> Void
|
|
|
|
@State private var animateContent = false
|
|
|
|
var body: some View {
|
|
VStack(spacing: Design.Spacing.xxLarge) {
|
|
Spacer()
|
|
|
|
// Header
|
|
VStack(spacing: Design.Spacing.medium) {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.font(.system(size: Design.IconSize.display))
|
|
.foregroundStyle(AppStatus.success)
|
|
|
|
Text(String(localized: "You're all set!"))
|
|
.font(.largeTitle)
|
|
.fontWeight(.bold)
|
|
.foregroundStyle(AppTextColors.primary)
|
|
|
|
Text(String(localized: "Here's how to get the most from Rituals"))
|
|
.font(.subheadline)
|
|
.foregroundStyle(AppTextColors.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
.opacity(animateContent ? 1 : 0)
|
|
.offset(y: animateContent ? 0 : 20)
|
|
|
|
// Feature cards
|
|
VStack(spacing: Design.Spacing.medium) {
|
|
FeatureHelpCard(
|
|
icon: "sun.max.fill",
|
|
title: String(localized: "Today"),
|
|
description: String(localized: "Shows rituals for the current time of day. Check in here daily.")
|
|
)
|
|
.opacity(animateContent ? 1 : 0)
|
|
.offset(y: animateContent ? 0 : 20)
|
|
.animation(.easeOut(duration: 0.4).delay(0.1), value: animateContent)
|
|
|
|
FeatureHelpCard(
|
|
icon: "sparkles",
|
|
title: String(localized: "Rituals"),
|
|
description: String(localized: "View and manage all your rituals, regardless of time.")
|
|
)
|
|
.opacity(animateContent ? 1 : 0)
|
|
.offset(y: animateContent ? 0 : 20)
|
|
.animation(.easeOut(duration: 0.4).delay(0.2), value: animateContent)
|
|
|
|
FeatureHelpCard(
|
|
icon: "chart.bar.fill",
|
|
title: String(localized: "Insights"),
|
|
description: String(localized: "Track your streaks, progress, and trends over time.")
|
|
)
|
|
.opacity(animateContent ? 1 : 0)
|
|
.offset(y: animateContent ? 0 : 20)
|
|
.animation(.easeOut(duration: 0.4).delay(0.3), value: animateContent)
|
|
}
|
|
.padding(.horizontal, Design.Spacing.large)
|
|
|
|
Spacer()
|
|
|
|
// CTA button
|
|
Button(action: onComplete) {
|
|
Text(String(localized: "Let's Go"))
|
|
.font(.headline)
|
|
.foregroundStyle(AppTextColors.inverse)
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: AppMetrics.Size.buttonHeight)
|
|
.background(AppAccent.primary)
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
|
}
|
|
.padding(.horizontal, Design.Spacing.xxLarge)
|
|
.opacity(animateContent ? 1 : 0)
|
|
.animation(.easeOut(duration: 0.4).delay(0.4), value: animateContent)
|
|
|
|
Spacer()
|
|
.frame(height: Design.Spacing.xxLarge)
|
|
}
|
|
.onAppear {
|
|
withAnimation(.easeOut(duration: 0.5)) {
|
|
animateContent = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A card explaining a feature of the app.
|
|
private struct FeatureHelpCard: View {
|
|
let icon: String
|
|
let title: String
|
|
let description: String
|
|
|
|
var body: some View {
|
|
HStack(spacing: Design.Spacing.medium) {
|
|
// Icon
|
|
Image(systemName: icon)
|
|
.font(.title2)
|
|
.foregroundStyle(AppAccent.primary)
|
|
.frame(width: 44, height: 44)
|
|
.background(AppAccent.primary.opacity(0.15))
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
|
|
|
// Text
|
|
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
|
|
Text(title)
|
|
.font(.headline)
|
|
.foregroundStyle(AppTextColors.primary)
|
|
|
|
Text(description)
|
|
.font(.caption)
|
|
.foregroundStyle(AppTextColors.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding(Design.Spacing.medium)
|
|
.background(AppSurface.card)
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
ZStack {
|
|
LinearGradient(
|
|
colors: [AppSurface.primary, AppSurface.secondary],
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
.ignoresSafeArea()
|
|
|
|
WhatsNextStepView(onComplete: {})
|
|
}
|
|
}
|