Andromida/Andromida/App/Views/Onboarding/WhatsNextStepView.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: {})
}
}