Andromida/Andromida/App/Views/Settings/SettingsView.swift

222 lines
9.0 KiB
Swift

import SwiftUI
import Bedrock
import UserNotifications
struct SettingsView: View {
@Bindable var store: SettingsStore
var ritualStore: RitualStore?
private let focusOptions: [(String, FocusStyle)] = FocusStyle.allCases.map { ($0.title, $0) }
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading, spacing: Design.Spacing.large) {
SettingsSectionHeader(
title: String(localized: "Preferences"),
systemImage: "gearshape",
accentColor: AppAccent.primary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsToggle(
title: String(localized: "Daily reminders"),
subtitle: reminderSubtitle,
isOn: $store.remindersEnabled,
accentColor: AppAccent.primary
)
if store.remindersEnabled {
HStack {
Text(String(localized: "Reminder time"))
.foregroundStyle(AppTextColors.primary)
Spacer()
DatePicker(
"",
selection: $store.reminderTime,
displayedComponents: .hourAndMinute
)
.labelsHidden()
.tint(AppAccent.primary)
}
.padding(.vertical, Design.Spacing.small)
}
SettingsToggle(
title: String(localized: "Haptics"),
subtitle: String(localized: "Vibrate when completing habits"),
isOn: $store.hapticsEnabled,
accentColor: AppAccent.primary
)
SettingsToggle(
title: String(localized: "Sound"),
subtitle: String(localized: "Play subtle completion sounds"),
isOn: $store.soundEnabled,
accentColor: AppAccent.primary
)
}
SettingsSectionHeader(
title: String(localized: "Rituals Pro"),
systemImage: "crown.fill",
accentColor: AppAccent.secondary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsNavigationRow(
title: String(localized: "Upgrade to Pro"),
subtitle: String(localized: "Unlock unlimited rituals and more"),
backgroundColor: AppSurface.primary
) {
ProUpgradeView()
}
}
SettingsSectionHeader(
title: String(localized: "Ritual pacing"),
systemImage: "timer",
accentColor: AppAccent.primary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsSegmentedPicker(
title: String(localized: "Focus style"),
subtitle: String(localized: "Choose the intensity of your arc"),
options: focusOptions,
selection: $store.focusStyle,
accentColor: AppAccent.primary
)
SettingsSlider(
title: String(localized: "Ritual length"),
subtitle: String(localized: "Adjust arc duration"),
value: $store.ritualLengthDays,
in: AppMetrics.RitualLength.minimumDays...AppMetrics.RitualLength.maximumDays,
step: AppMetrics.RitualLength.stepDays,
format: SliderFormat.integer(unit: String(localized: "days")),
accentColor: AppAccent.primary,
leadingIcon: Image(systemName: "calendar"),
trailingIcon: Image(systemName: "calendar.circle.fill")
)
}
SettingsSectionHeader(
title: String(localized: "iCloud Sync"),
systemImage: "icloud",
accentColor: AppAccent.primary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
iCloudSyncSettingsView(
viewModel: store,
accentColor: AppAccent.primary,
successColor: AppStatus.success,
warningColor: AppStatus.warning
)
}
SettingsSectionHeader(
title: String(localized: "About"),
systemImage: "info.circle",
accentColor: AppAccent.primary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsNavigationRow(
title: String(localized: "Rituals mission"),
subtitle: String(localized: "Why arcs keep habits grounded"),
backgroundColor: AppSurface.primary
) {
SettingsAboutView()
}
}
#if DEBUG
SettingsSectionHeader(
title: String(localized: "Debug"),
systemImage: "ant.fill",
accentColor: AppStatus.error
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
SettingsNavigationRow(
title: String(localized: "Icon Generator"),
subtitle: String(localized: "Generate the app icon"),
backgroundColor: AppSurface.primary
) {
IconGeneratorView(config: .rituals, appName: "Rituals")
}
SettingsNavigationRow(
title: String(localized: "Branding Preview"),
subtitle: String(localized: "Preview launch and icon"),
backgroundColor: AppSurface.primary
) {
BrandingPreviewView(
iconConfig: .rituals,
launchConfig: .rituals,
appName: "Rituals"
)
}
SettingsRow(
systemImage: "arrow.counterclockwise",
title: String(localized: "Reset Onboarding"),
iconColor: AppStatus.warning
) {
UserDefaults.standard.removeObject(forKey: "hasCompletedOnboarding")
}
if let ritualStore {
SettingsRow(
systemImage: "calendar.badge.plus",
title: String(localized: "Preload 6 Months Demo Data"),
iconColor: AppStatus.info
) {
ritualStore.preloadDemoData()
}
SettingsRow(
systemImage: "trash",
title: String(localized: "Clear All Completions"),
iconColor: AppStatus.error
) {
ritualStore.clearAllCompletions()
}
}
}
#endif
Spacer(minLength: Design.Spacing.xxxLarge)
}
.padding(.horizontal, Design.Spacing.large)
}
.background(AppSurface.primary)
.navigationTitle(String(localized: "Settings"))
.navigationBarTitleDisplayMode(.inline)
}
}
// MARK: - Private Computed Properties
extension SettingsView {
private var reminderSubtitle: String {
switch store.notificationAuthStatus {
case .denied:
return String(localized: "Notifications disabled in Settings")
case .notDetermined:
return String(localized: "Get a gentle check-in each morning")
case .authorized, .provisional, .ephemeral:
return String(localized: "Get a gentle check-in each morning")
@unknown default:
return String(localized: "Get a gentle check-in each morning")
}
}
}
#Preview {
NavigationStack {
SettingsView(store: SettingsStore.preview, ritualStore: nil)
}
}