225 lines
8.7 KiB
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)
|
|
}
|
|
}
|