Release 1.0.0 baseline
This commit is contained in:
@@ -68,13 +68,16 @@ final class HomeViewModel: ObservableObject {
|
||||
)
|
||||
|
||||
if let existing = try? context.fetch(descriptor).first {
|
||||
syncSlotsIfNeeded(plan: existing, settings: settings, context: context)
|
||||
return existing
|
||||
}
|
||||
|
||||
return DefaultDataService.createWeekPlan(for: currentWeekStart, settings: settings, context: context)
|
||||
}
|
||||
|
||||
func reconcileSlotsIfNeeded(plan: WeekPlan, settings: AppSettings, context: ModelContext) {
|
||||
syncSlotsIfNeeded(plan: plan, settings: settings, context: context)
|
||||
}
|
||||
|
||||
func assignDish(_ dish: Dish, to slot: MealSlot, plan: WeekPlan, settings: AppSettings, isOverride: Bool = false) {
|
||||
captureUndoSnapshot(plan: plan)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ final class OnboardingViewModel: ObservableObject {
|
||||
@Published var currentStep: Int = 0
|
||||
@Published var selectedMealWindows: MealWindows = .dinnerOnly
|
||||
@Published var includeWeekends: Bool = true
|
||||
@Published var syncICloud: Bool = true
|
||||
@Published var syncCalendar: Bool = false
|
||||
@Published var selectedCalendarId: String?
|
||||
@Published var lunchTime: Date = {
|
||||
@@ -21,6 +22,7 @@ final class OnboardingViewModel: ObservableObject {
|
||||
@Published var newDishName: String = ""
|
||||
@Published var newDishTags: Set<UUID> = []
|
||||
@Published var addedDishes: [(name: String, tagIds: [UUID])] = []
|
||||
let maxOnboardingDishes = 10
|
||||
|
||||
let totalSteps = 5
|
||||
|
||||
@@ -36,10 +38,15 @@ final class OnboardingViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
var canAddDish: Bool {
|
||||
!newDishName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
!newDishName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && canAddMoreDishes
|
||||
}
|
||||
|
||||
var canAddMoreDishes: Bool {
|
||||
addedDishes.count < maxOnboardingDishes
|
||||
}
|
||||
|
||||
func addDish(defaultTagId: UUID? = nil) {
|
||||
guard canAddMoreDishes else { return }
|
||||
guard canAddDish else { return }
|
||||
let normalizedName = newDishName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !addedDishes.contains(where: { $0.name.lowercased() == normalizedName.lowercased() }) else { return }
|
||||
@@ -64,6 +71,7 @@ final class OnboardingViewModel: ObservableObject {
|
||||
}
|
||||
|
||||
func addSuggestedDish(name: String, preferredTagNames: [String], availableTags: [Tag]) {
|
||||
guard canAddMoreDishes else { return }
|
||||
let normalizedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !normalizedName.isEmpty else { return }
|
||||
guard !addedDishes.contains(where: { $0.name.lowercased() == normalizedName.lowercased() }) else { return }
|
||||
@@ -115,6 +123,7 @@ final class OnboardingViewModel: ObservableObject {
|
||||
|
||||
settings.mealWindowsEnum = selectedMealWindows
|
||||
settings.includeWeekends = includeWeekends
|
||||
settings.iCloudSyncEnabled = syncICloud
|
||||
settings.syncEnabled = syncCalendar
|
||||
settings.calendarId = selectedCalendarId
|
||||
settings.lunchTime = lunchTime
|
||||
|
||||
@@ -6,11 +6,33 @@ import EventKit
|
||||
final class SettingsViewModel: ObservableObject {
|
||||
@Published var availableCalendars: [EKCalendar] = []
|
||||
@Published var showCalendarPermissionAlert: Bool = false
|
||||
@Published var showToast: Bool = false
|
||||
@Published var toastMessage: String = ""
|
||||
|
||||
func loadCalendars() {
|
||||
availableCalendars = CalendarService.shared.availableCalendars()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func ensureValidCalendarSelection(settings: AppSettings) -> Bool {
|
||||
loadCalendars()
|
||||
|
||||
guard !availableCalendars.isEmpty else {
|
||||
settings.calendarId = nil
|
||||
settings.syncEnabled = false
|
||||
showToastMessage(String(localized: "toast_calendar_unavailable"))
|
||||
return false
|
||||
}
|
||||
|
||||
if let selected = settings.calendarId,
|
||||
availableCalendars.contains(where: { $0.calendarIdentifier == selected }) {
|
||||
return true
|
||||
}
|
||||
|
||||
settings.calendarId = availableCalendars.first?.calendarIdentifier
|
||||
return true
|
||||
}
|
||||
|
||||
func requestCalendarAccess() async -> Bool {
|
||||
let granted = await CalendarService.shared.requestAccess()
|
||||
if !granted {
|
||||
@@ -42,10 +64,19 @@ final class SettingsViewModel: ObservableObject {
|
||||
try deleteAll(of: WeekPlan.self, in: context)
|
||||
try deleteAll(of: Dish.self, in: context)
|
||||
try deleteAll(of: Tag.self, in: context)
|
||||
try deleteAll(of: AppSettings.self, in: context)
|
||||
|
||||
DefaultDataService.createDefaultSettings(context: context)
|
||||
DefaultDataService.createDefaultTags(context: context)
|
||||
|
||||
let settingsDescriptor = FetchDescriptor<AppSettings>()
|
||||
if let existingSettings = try context.fetch(settingsDescriptor).first {
|
||||
resetSettings(existingSettings)
|
||||
} else {
|
||||
DefaultDataService.createDefaultSettings(context: context)
|
||||
if let createdSettings = try context.fetch(settingsDescriptor).first {
|
||||
resetSettings(createdSettings)
|
||||
}
|
||||
}
|
||||
|
||||
try context.save()
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("Failed to reset all data: \(error)")
|
||||
@@ -61,4 +92,28 @@ final class SettingsViewModel: ObservableObject {
|
||||
}
|
||||
try context.save()
|
||||
}
|
||||
|
||||
private func showToastMessage(_ message: String) {
|
||||
toastMessage = message
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
showToast = true
|
||||
}
|
||||
}
|
||||
|
||||
private func resetSettings(_ settings: AppSettings) {
|
||||
let defaults = AppSettings()
|
||||
settings.mealWindows = defaults.mealWindows
|
||||
settings.includeWeekends = defaults.includeWeekends
|
||||
settings.language = defaults.language
|
||||
settings.calendarId = defaults.calendarId
|
||||
settings.syncEnabled = defaults.syncEnabled
|
||||
settings.syncMode = defaults.syncMode
|
||||
settings.lunchTime = defaults.lunchTime
|
||||
settings.dinnerTime = defaults.dinnerTime
|
||||
settings.eventDuration = defaults.eventDuration
|
||||
settings.eventPrefix = defaults.eventPrefix
|
||||
settings.reminderMinutesBefore = defaults.reminderMinutesBefore
|
||||
settings.iCloudSyncEnabled = false
|
||||
settings.onboardingCompleted = false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user