175 lines
6.7 KiB
Swift
175 lines
6.7 KiB
Swift
import SwiftUI
|
|
import Bedrock
|
|
|
|
struct TodayEmptyStateView: View {
|
|
@Bindable var store: RitualStore
|
|
@Bindable var categoryStore: CategoryStore
|
|
@State private var showingPresetLibrary = false
|
|
@State private var showingCreateRitual = false
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: Design.Spacing.large) {
|
|
SectionHeaderView(
|
|
title: String(localized: "No Active Rituals"),
|
|
subtitle: String(localized: "Start building better habits")
|
|
)
|
|
|
|
VStack(spacing: Design.Spacing.large) {
|
|
// Icon
|
|
SymbolIcon("sparkles", size: .hero, color: AppAccent.primary)
|
|
.padding(.top, Design.Spacing.large)
|
|
|
|
Text(String(localized: "Rituals help you build consistent habits through focused, time-bound journeys."))
|
|
.typography(.subheading)
|
|
.foregroundStyle(AppTextColors.secondary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, Design.Spacing.large)
|
|
|
|
// Quick start goal cards
|
|
quickStartSection
|
|
|
|
// Divider
|
|
HStack {
|
|
Rectangle()
|
|
.fill(AppBorder.subtle)
|
|
.frame(height: 1)
|
|
Text(String(localized: "or"))
|
|
.typography(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
Rectangle()
|
|
.fill(AppBorder.subtle)
|
|
.frame(height: 1)
|
|
}
|
|
.padding(.horizontal, Design.Spacing.medium)
|
|
|
|
// Action buttons
|
|
VStack(spacing: Design.Spacing.medium) {
|
|
Button {
|
|
showingCreateRitual = true
|
|
} label: {
|
|
HStack {
|
|
Image(systemName: "plus.circle.fill")
|
|
Text(String(localized: "Create Custom Ritual"))
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
.buttonStyle(.bordered)
|
|
|
|
Button {
|
|
showingPresetLibrary = true
|
|
} label: {
|
|
HStack {
|
|
Image(systemName: "sparkles.rectangle.stack")
|
|
Text(String(localized: "Browse All Presets"))
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
.buttonStyle(.bordered)
|
|
}
|
|
.padding(.horizontal, Design.Spacing.medium)
|
|
|
|
// Past rituals hint
|
|
if !store.pastRituals.isEmpty {
|
|
Text(String(localized: "You can also restart a past ritual from the Rituals tab."))
|
|
.typography(.caption)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.top, Design.Spacing.small)
|
|
}
|
|
}
|
|
.padding(Design.Spacing.large)
|
|
.frame(maxWidth: .infinity)
|
|
.background(AppSurface.card)
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
|
|
}
|
|
.sheet(isPresented: $showingPresetLibrary) {
|
|
PresetLibrarySheet(store: store)
|
|
}
|
|
.sheet(isPresented: $showingCreateRitual) {
|
|
RitualEditSheet(store: store, categoryStore: categoryStore, ritual: nil)
|
|
}
|
|
}
|
|
|
|
// MARK: - Quick Start Section
|
|
|
|
@State private var quickStartButtonHeight: CGFloat?
|
|
|
|
private var quickStartSection: some View {
|
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
|
Text(String(localized: "Quick Start"))
|
|
.typography(.captionEmphasis)
|
|
.foregroundStyle(AppTextColors.tertiary)
|
|
.padding(.horizontal, Design.Spacing.medium)
|
|
|
|
LazyVGrid(
|
|
columns: [
|
|
GridItem(.flexible(), spacing: Design.Spacing.small),
|
|
GridItem(.flexible(), spacing: Design.Spacing.small)
|
|
],
|
|
spacing: Design.Spacing.small
|
|
) {
|
|
ForEach(OnboardingGoal.allCases) { goal in
|
|
QuickStartButton(goal: goal, uniformHeight: quickStartButtonHeight) {
|
|
startQuickRitual(for: goal)
|
|
}
|
|
.onGeometryChange(for: CGFloat.self) { proxy in
|
|
proxy.size.height
|
|
} action: { height in
|
|
if let current = quickStartButtonHeight {
|
|
if height > current {
|
|
quickStartButtonHeight = height
|
|
}
|
|
} else {
|
|
quickStartButtonHeight = height
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, Design.Spacing.small)
|
|
}
|
|
}
|
|
|
|
private func startQuickRitual(for goal: OnboardingGoal) {
|
|
// Get the first morning preset for this goal, or any preset
|
|
if let preset = OnboardingPresetRecommender.recommendedPreset(for: goal, time: .morning) {
|
|
store.createRitual(from: preset)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A compact button for quick-starting a ritual from a goal category.
|
|
private struct QuickStartButton: View {
|
|
let goal: OnboardingGoal
|
|
let uniformHeight: CGFloat?
|
|
let action: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
HStack(spacing: Design.Spacing.small) {
|
|
SymbolIcon(goal.symbolName, size: .row, color: AppAccent.primary)
|
|
|
|
Text(goal.displayName)
|
|
.typography(.subheading)
|
|
.foregroundStyle(AppTextColors.primary)
|
|
.lineLimit(2)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
|
|
Spacer()
|
|
}
|
|
.padding(.horizontal, Design.Spacing.medium)
|
|
.padding(.vertical, Design.Spacing.small)
|
|
.frame(height: uniformHeight)
|
|
.background(AppSurface.tertiary)
|
|
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
|
|
}
|
|
.buttonStyle(.plain)
|
|
.accessibilityLabel(String(localized: "Start \(goal.displayName) ritual"))
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
TodayEmptyStateView(store: RitualStore.preview, categoryStore: CategoryStore.preview)
|
|
.padding()
|
|
.background(AppSurface.primary)
|
|
}
|