Andromida/Andromida/App/Views/Onboarding/WhatsNextStepView.swift

217 lines
8.3 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
@State private var isShowingWidgetHelp = false
var body: some View {
VStack(spacing: Design.Spacing.xxLarge) {
Spacer()
// Header
VStack(spacing: Design.Spacing.medium) {
SymbolIcon("checkmark.circle.fill", size: .section, color: AppStatus.success)
Text(String(localized: "You're all set!"))
.typography(.heroBold)
.foregroundStyle(AppTextColors.primary)
Text(String(localized: "Here's how to get the most from Rituals"))
.typography(.subheading)
.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)
WidgetDiscoveryCard(onLearnMore: { isShowingWidgetHelp = true })
.opacity(animateContent ? 1 : 0)
.offset(y: animateContent ? 0 : 20)
.animation(.easeOut(duration: 0.4).delay(0.4), value: animateContent)
}
.padding(.horizontal, Design.Spacing.large)
Spacer()
// CTA button
Button(action: onComplete) {
Text(String(localized: "Let's Go")).styled(.heading, emphasis: .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
}
}
.sheet(isPresented: $isShowingWidgetHelp) {
WidgetSetupSheet()
}
}
}
/// 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
SymbolIcon(icon, size: .row, color: 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).styled(.heading, emphasis: .primary)
Text(description).styled(.caption, emphasis: .secondary)
}
Spacer()
}
.padding(Design.Spacing.medium)
.background(AppSurface.card)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
}
/// A feature card with a CTA to learn how to add widgets.
private struct WidgetDiscoveryCard: View {
let onLearnMore: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) {
HStack(spacing: Design.Spacing.medium) {
SymbolIcon("square.grid.2x2.fill", size: .row, color: AppAccent.primary)
.frame(width: 44, height: 44)
.background(AppAccent.primary.opacity(0.15))
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
Text(String(localized: "Widgets")).styled(.heading, emphasis: .primary)
Text(String(localized: "Add Andromida to your Home Screen for quick check-ins."))
.styled(.caption, emphasis: .secondary)
}
Spacer()
}
Button(action: onLearnMore) {
Text(String(localized: "How to add")).styled(.caption, emphasis: .primary)
.padding(.vertical, Design.Spacing.xSmall)
.padding(.horizontal, Design.Spacing.small)
.background(AppAccent.primary.opacity(0.15))
.clipShape(.capsule)
}
.buttonStyle(.plain)
}
.padding(Design.Spacing.medium)
.background(AppSurface.card)
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
}
}
private struct WidgetSetupSheet: View {
var body: some View {
VStack(spacing: Design.Spacing.large) {
VStack(spacing: Design.Spacing.small) {
Text(String(localized: "Add the widget")).styled(.title2Bold, emphasis: .primary)
Text(String(localized: "Keep your rituals visible at a glance."))
.styled(.subheading, emphasis: .secondary)
.multilineTextAlignment(.center)
}
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
WidgetStepRow(number: "1", text: String(localized: "Touch and hold your Home Screen."))
WidgetStepRow(number: "2", text: String(localized: "Tap Edit, then Add Widget."))
WidgetStepRow(number: "3", text: String(localized: "Search for Andromida and pick a size."))
}
.padding(.horizontal, Design.Spacing.large)
Spacer()
}
.padding(.top, Design.Spacing.xxLarge)
.presentationDetents([.medium])
}
}
private struct WidgetStepRow: View {
let number: String
let text: String
var body: some View {
HStack(alignment: .top, spacing: Design.Spacing.medium) {
Text(number).styled(.caption, emphasis: .inverse)
.frame(width: 28, height: 28)
.background(AppAccent.primary)
.clipShape(.circle)
Text(text).styled(.body, emphasis: .primary)
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: {})
}
}