217 lines
8.3 KiB
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: {})
|
|
}
|
|
}
|