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

225 lines
8.7 KiB
Swift

import SwiftUI
import Bedrock
struct SettingsView: View {
@Bindable var store: SettingsStore
var ritualStore: RitualStore?
var categoryStore: CategoryStore?
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: "Reminders"),
subtitle: reminderSubtitle,
isOn: remindersBinding,
accentColor: AppAccent.primary
)
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: "Customization"),
systemImage: "paintbrush",
accentColor: AppAccent.primary
)
SettingsCard(backgroundColor: AppSurface.card, borderColor: AppBorder.subtle) {
if let categoryStore {
SettingsNavigationRow(
title: String(localized: "Categories"),
subtitle: String(localized: "Manage ritual categories"),
backgroundColor: AppSurface.primary
) {
CategoryListView(store: categoryStore)
}
}
}
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 Setup Wizard"),
iconColor: AppStatus.warning
) {
// Reset both the old and new onboarding flags
UserDefaults.standard.removeObject(forKey: "hasCompletedOnboarding")
UserDefaults.standard.removeObject(forKey: "hasCompletedSetupWizard")
}
if let ritualStore {
SettingsRow(
systemImage: "calendar.badge.plus",
title: String(localized: "Preload 6 Months Demo Data"),
iconColor: AppStatus.info
) {
ritualStore.preloadDemoData()
}
SettingsRow(
systemImage: "checkmark.circle.badge.xmark",
title: String(localized: "Complete First Active Arc (Test Renewal)"),
iconColor: AppStatus.success
) {
ritualStore.simulateArcCompletion()
}
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)
}
.onAppear {
store.refresh()
ritualStore?.refresh()
Task {
await ritualStore?.reminderScheduler.refreshStatus()
}
}
.background(AppSurface.primary)
.navigationTitle(String(localized: "Settings"))
.navigationBarTitleDisplayMode(.inline)
}
}
// MARK: - Private Computed Properties
extension SettingsView {
private var reminderSubtitle: String {
guard let ritualStore else {
return String(localized: "Get reminded when it's time for your rituals")
}
let scheduler = ritualStore.reminderScheduler
// Check if notifications are denied at system level
if scheduler.authorizationStatus == .denied {
return String(localized: "Notifications disabled in Settings")
}
// If reminders are enabled, show which time slots are scheduled
if scheduler.remindersEnabled {
let slots = scheduler.scheduledSlots
if slots.isEmpty {
return String(localized: "No active rituals to remind")
}
// Build time string like "7am, 6pm"
let times = slots.sorted { $0.hour < $1.hour }.map { slot in
switch slot {
case .morning: return String(localized: "7am")
case .midday: return String(localized: "12pm")
case .evening: return String(localized: "6pm")
}
}
let timeList = times.joined(separator: ", ")
return String(localized: "Daily at \(timeList)")
}
return String(localized: "Get reminded when it's time for your rituals")
}
private var remindersBinding: Binding<Bool> {
Binding(
get: { ritualStore?.reminderScheduler.remindersEnabled ?? false },
set: { newValue in
ritualStore?.reminderScheduler.remindersEnabled = newValue
}
)
}
}
#Preview {
NavigationStack {
SettingsView(store: SettingsStore.preview, ritualStore: nil)
}
}