huecos sin planificar y exclusiones en la lista de la compra
- MealSlot.isSkipped: opcion "No planificar esta comida" en el picker de hueco vacio — cuenta como resuelto (semana completa, notificaciones, stats) y el autocompletado lo respeta; vista gris con guion, tap para desmarcar; viaja en undo, snapshot KV (junto con isEatingOut, que faltaba en el payload) y exports (— en imagen, omitido en texto) - ShoppingItem.isDismissed: "Ya lo tengo" por ingrediente (swipe izquierdo) y "Ya esta hecho" por plato entero (boton en la cabecera de su seccion); van a la seccion "Ya en casa" con restauracion de un tap, y quedan fuera del export de texto. No se borran porque reconcile los recrearia - Esquema CloudKit Development: CD_isSkipped, CD_isDismissed y el record type CD_ShoppingItem entero, que no existia — la lista de la compra no estaba sincronizando entre dispositivos (mismo bug que photoData) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
This commit is contained in:
@@ -14,6 +14,7 @@ final class HomeViewModel: ObservableObject {
|
||||
let secondaryDishId: UUID?
|
||||
let isRuleOverridden: Bool
|
||||
let isEatingOut: Bool
|
||||
let isSkipped: Bool
|
||||
}
|
||||
|
||||
@Published var currentWeekStart: Date
|
||||
@@ -150,7 +151,7 @@ final class HomeViewModel: ObservableObject {
|
||||
.sorted { $0.weekStartDate > $1.weekStartDate }
|
||||
.prefix(12)
|
||||
|
||||
let emptySlots = plan.slotList.filter { $0.dishId == nil && !$0.isEatingOut }
|
||||
let emptySlots = plan.slotList.filter { $0.dishId == nil && !$0.isEatingOut && !$0.isSkipped }
|
||||
let result = AutocompleteEngine.autocomplete(
|
||||
emptySlots: emptySlots,
|
||||
currentPlan: plan,
|
||||
@@ -207,6 +208,7 @@ final class HomeViewModel: ObservableObject {
|
||||
slot.secondaryDishId = nil
|
||||
slot.isRuleOverridden = false
|
||||
slot.isEatingOut = false
|
||||
slot.isSkipped = false
|
||||
}
|
||||
plan.updatedAt = Date()
|
||||
AnalyticsService.logWeekReset()
|
||||
@@ -356,11 +358,13 @@ final class HomeViewModel: ObservableObject {
|
||||
slot.secondaryDishId = snap.secondaryDishId
|
||||
slot.isRuleOverridden = snap.isRuleOverridden
|
||||
slot.isEatingOut = snap.isEatingOut
|
||||
slot.isSkipped = snap.isSkipped
|
||||
} else {
|
||||
slot.dishId = nil
|
||||
slot.secondaryDishId = nil
|
||||
slot.isRuleOverridden = false
|
||||
slot.isEatingOut = false
|
||||
slot.isSkipped = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +388,7 @@ final class HomeViewModel: ObservableObject {
|
||||
guard !isApplyingUndo else { return }
|
||||
lastWeekStartSnapshot = plan.weekStartDate
|
||||
lastSlotsSnapshot = Dictionary(plan.slotList.map { slot in
|
||||
(slot.id, SlotSnapshot(dishId: slot.dishId, secondaryDishId: slot.secondaryDishId, isRuleOverridden: slot.isRuleOverridden, isEatingOut: slot.isEatingOut))
|
||||
(slot.id, SlotSnapshot(dishId: slot.dishId, secondaryDishId: slot.secondaryDishId, isRuleOverridden: slot.isRuleOverridden, isEatingOut: slot.isEatingOut, isSkipped: slot.isSkipped))
|
||||
}, uniquingKeysWith: { first, _ in first })
|
||||
hasUndoSnapshot = true
|
||||
}
|
||||
@@ -397,7 +401,7 @@ final class HomeViewModel: ObservableObject {
|
||||
|
||||
private func firstFreeSlot(in plan: WeekPlan) -> MealSlot? {
|
||||
plan.slotList
|
||||
.filter { $0.dishId == nil && !$0.isEatingOut }
|
||||
.filter { $0.dishId == nil && !$0.isEatingOut && !$0.isSkipped }
|
||||
.sorted { lhs, rhs in
|
||||
if lhs.dayOfWeek != rhs.dayOfWeek {
|
||||
return lhs.dayOfWeek < rhs.dayOfWeek
|
||||
@@ -416,7 +420,7 @@ final class HomeViewModel: ObservableObject {
|
||||
|
||||
switch settings.syncModeEnum {
|
||||
case .weekComplete:
|
||||
if plan.slotList.allSatisfy({ $0.dishId != nil || $0.isEatingOut }) {
|
||||
if plan.slotList.allSatisfy({ $0.dishId != nil || $0.isEatingOut || $0.isSkipped }) {
|
||||
let descriptor = FetchDescriptor<Dish>()
|
||||
let dishes = (try? plan.modelContext?.fetch(descriptor)) ?? []
|
||||
syncAllAssignedSlotsToCalendar(plan: plan, dishes: dishes, settings: settings)
|
||||
@@ -497,6 +501,7 @@ final class HomeViewModel: ObservableObject {
|
||||
slot.secondaryDishId = nil
|
||||
slot.isRuleOverridden = false
|
||||
slot.isEatingOut = true
|
||||
slot.isSkipped = false
|
||||
plan.updatedAt = Date()
|
||||
applyCalendarSyncPolicy(plan: plan, settings: settings, shouldNotify: false)
|
||||
HapticManager.shared.impact(style: .medium)
|
||||
@@ -510,6 +515,33 @@ final class HomeViewModel: ObservableObject {
|
||||
HapticManager.shared.impact(style: .light)
|
||||
}
|
||||
|
||||
/// "Don't plan this meal": resolves the slot without a dish, so the week
|
||||
/// can complete and autocomplete leaves it alone.
|
||||
func markSkipped(slot: MealSlot, plan: WeekPlan, settings: AppSettings) {
|
||||
captureUndoSnapshot(plan: plan)
|
||||
if let eventId = slot.calendarEventId {
|
||||
CalendarService.shared.deleteEvent(eventId: eventId)
|
||||
slot.calendarEventId = nil
|
||||
}
|
||||
slot.dishId = nil
|
||||
slot.secondaryDishId = nil
|
||||
slot.isRuleOverridden = false
|
||||
slot.isEatingOut = false
|
||||
slot.isSkipped = true
|
||||
plan.updatedAt = Date()
|
||||
applyCalendarSyncPolicy(plan: plan, settings: settings, shouldNotify: false)
|
||||
AnalyticsService.logEvent("meal_skipped")
|
||||
HapticManager.shared.impact(style: .medium)
|
||||
showToastMessage(localizedString("toast_meal_skipped", language: settings.languageEnum.resolved()))
|
||||
}
|
||||
|
||||
func unmarkSkipped(slot: MealSlot, plan: WeekPlan, settings: AppSettings) {
|
||||
captureUndoSnapshot(plan: plan)
|
||||
slot.isSkipped = false
|
||||
plan.updatedAt = Date()
|
||||
HapticManager.shared.impact(style: .light)
|
||||
}
|
||||
|
||||
func acknowledgeViolation(slot: MealSlot, plan: WeekPlan) {
|
||||
slot.isRuleOverridden = false
|
||||
plan.updatedAt = Date()
|
||||
|
||||
Reference in New Issue
Block a user