Add Apple Reminders integration for grocery list with local mapping persistence
Introduce a GroceryListManager facade that delegates to either the existing in-app GroceryList or a new RemindersGroceryStore backed by EventKit. Users choose the mode in Settings; when Reminders mode is active the Grocery List tab is hidden. Recipe-to-reminder grouping uses a local mapping file (reminder_mappings.data) instead of polluting the reminder's notes field, with automatic pruning when reminders are deleted externally. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
// Created by Vincent Meilinger on 15.09.23.
|
||||
//
|
||||
|
||||
import EventKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
@@ -13,8 +14,11 @@ import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var groceryListManager: GroceryListManager
|
||||
@ObservedObject var userSettings = UserSettings.shared
|
||||
@StateObject var viewModel = ViewModel()
|
||||
@State private var reminderLists: [EKCalendar] = []
|
||||
@State private var remindersPermission: EKAuthorizationStatus = .notDetermined
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
@@ -54,6 +58,50 @@ struct SettingsView: View {
|
||||
} footer: {
|
||||
Text("The selected cookbook will open on app launch by default.")
|
||||
}
|
||||
|
||||
Section {
|
||||
Picker("Grocery list storage", selection: $userSettings.groceryListMode) {
|
||||
ForEach(GroceryListMode.allValues, id: \.self) { mode in
|
||||
Text(mode.descriptor()).tag(mode.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
if userSettings.groceryListMode == GroceryListMode.appleReminders.rawValue {
|
||||
if remindersPermission == .notDetermined {
|
||||
Button("Grant Reminders Access") {
|
||||
Task {
|
||||
let granted = await groceryListManager.requestRemindersAccess()
|
||||
remindersPermission = groceryListManager.remindersPermissionStatus
|
||||
if granted {
|
||||
reminderLists = groceryListManager.availableReminderLists()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if remindersPermission == .denied || remindersPermission == .restricted {
|
||||
Text("Reminders access was denied. Please enable it in System Settings to use this feature.")
|
||||
.foregroundStyle(.secondary)
|
||||
Button("Open Settings") {
|
||||
if let url = URL(string: UIApplication.openSettingsURLString) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
} else if remindersPermission == .fullAccess {
|
||||
Picker("Reminders list", selection: $userSettings.remindersListIdentifier) {
|
||||
ForEach(reminderLists, id: \.calendarIdentifier) { list in
|
||||
Text(list.title).tag(list.calendarIdentifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Grocery List")
|
||||
} footer: {
|
||||
if userSettings.groceryListMode == GroceryListMode.appleReminders.rawValue {
|
||||
Text("Grocery items will be saved to Apple Reminders. The Grocery List tab will be hidden since you can manage items directly in the Reminders app.")
|
||||
} else {
|
||||
Text("Grocery items are stored locally on this device.")
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Toggle(isOn: $userSettings.expandNutritionSection) {
|
||||
@@ -187,6 +235,16 @@ struct SettingsView: View {
|
||||
}
|
||||
.task {
|
||||
await viewModel.getUserData()
|
||||
remindersPermission = groceryListManager.remindersPermissionStatus
|
||||
if remindersPermission == .fullAccess {
|
||||
reminderLists = groceryListManager.availableReminderLists()
|
||||
}
|
||||
}
|
||||
.onChange(of: userSettings.groceryListMode) { _, _ in
|
||||
remindersPermission = groceryListManager.remindersPermissionStatus
|
||||
if remindersPermission == .fullAccess {
|
||||
reminderLists = groceryListManager.availableReminderLists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user