1.2.0: contextual notifications + in-onboarding auto-fill (aha moment)
- New "Week Ready" onboarding step: auto-fills the first week inline right after dish creation, then pitches the Sunday reminder with context before requesting OS notification permission (no more cold prompt on Home) - Notifications section in Settings: weekly reminder toggle, deep-link to system settings when permission is denied - NotificationService: remindersEnabled preference, requestPermission(), authorizationStatus(), cancelAllReminders() - Post-onboarding premium prompt deferred to 2nd session (paywall was just shown as the final onboarding step — 2s-later alert was prompt fatigue) - Back navigation blocked after onboarding commit to avoid duplicate dishes - 10 new strings × 6 languages Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UtWT52pAE91D7mbDda1cAM
This commit is contained in:
@@ -1,15 +1,45 @@
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
import EventKit
|
||||
import UserNotifications
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.modelContext) private var context
|
||||
@Query private var allSettings: [AppSettings]
|
||||
@StateObject private var viewModel = SettingsViewModel()
|
||||
@State private var showResetAllDataAlert = false
|
||||
@State private var notificationAuthStatus: UNAuthorizationStatus = .notDetermined
|
||||
@State private var remindersEnabled = NotificationService.shared.remindersEnabled
|
||||
|
||||
private var settings: AppSettings? { allSettings.first }
|
||||
|
||||
private func refreshNotificationStatus() async {
|
||||
notificationAuthStatus = await NotificationService.shared.authorizationStatus()
|
||||
remindersEnabled = NotificationService.shared.remindersEnabled
|
||||
}
|
||||
|
||||
private func handleReminderToggle(_ enabled: Bool, settings: AppSettings) {
|
||||
Task {
|
||||
if enabled {
|
||||
let granted = await NotificationService.shared.requestPermission()
|
||||
NotificationService.shared.remindersEnabled = true
|
||||
remindersEnabled = true
|
||||
notificationAuthStatus = await NotificationService.shared.authorizationStatus()
|
||||
AnalyticsService.logNotificationsToggled(enabled: granted)
|
||||
if granted {
|
||||
NotificationService.shared.schedulePlanningReminderIfNeeded(
|
||||
nextWeekPlan: nil,
|
||||
language: settings.languageEnum.resolved()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
NotificationService.shared.remindersEnabled = false
|
||||
remindersEnabled = false
|
||||
AnalyticsService.logNotificationsToggled(enabled: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.mealMoodBackground.ignoresSafeArea()
|
||||
@@ -147,6 +177,37 @@ struct SettingsView: View {
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
|
||||
// Notifications section
|
||||
Section {
|
||||
Toggle("settings_weekly_reminder", isOn: Binding(
|
||||
get: { remindersEnabled && notificationAuthStatus != .denied },
|
||||
set: { newValue in handleReminderToggle(newValue, settings: settings) }
|
||||
))
|
||||
.tint(.mealMoodCoral)
|
||||
.disabled(notificationAuthStatus == .denied)
|
||||
|
||||
if notificationAuthStatus == .denied {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("settings_notifications_denied_hint")
|
||||
.font(.mealMoodSmall)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
Button {
|
||||
if let url = URL(string: UIApplication.openSettingsURLString) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
} label: {
|
||||
Text("settings_open_system_settings")
|
||||
.font(.mealMoodSmall)
|
||||
.foregroundColor(.mealMoodCoral)
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Label("settings_notifications_header", systemImage: "bell")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
.task { await refreshNotificationStatus() }
|
||||
|
||||
// Tags section
|
||||
Section {
|
||||
NavigationLink(destination: TagListView()) {
|
||||
|
||||
Reference in New Issue
Block a user