Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-27 15:06:33 -06:00
parent 69e548b13c
commit ee347bb402
2 changed files with 28 additions and 9 deletions

View File

@ -65,7 +65,7 @@ enum RitualPresetLibrary {
title: String(localized: "Morning Hydration"), title: String(localized: "Morning Hydration"),
theme: String(localized: "Start your day refreshed"), theme: String(localized: "Start your day refreshed"),
notes: String(localized: "Build the habit of hydrating first thing in the morning."), notes: String(localized: "Build the habit of hydrating first thing in the morning."),
durationDays: 21, durationDays: 28,
timeOfDay: .morning, timeOfDay: .morning,
iconName: "drop.fill", iconName: "drop.fill",
category: PresetCategory.health.rawValue, category: PresetCategory.health.rawValue,
@ -113,7 +113,7 @@ enum RitualPresetLibrary {
title: String(localized: "Deep Work Prep"), title: String(localized: "Deep Work Prep"),
theme: String(localized: "Set up for focus"), theme: String(localized: "Set up for focus"),
notes: String(localized: "Create the conditions for uninterrupted deep work."), notes: String(localized: "Create the conditions for uninterrupted deep work."),
durationDays: 21, durationDays: 28,
timeOfDay: .morning, timeOfDay: .morning,
iconName: "brain", iconName: "brain",
category: PresetCategory.productivity.rawValue, category: PresetCategory.productivity.rawValue,
@ -143,7 +143,7 @@ enum RitualPresetLibrary {
title: String(localized: "Focus Reset"), title: String(localized: "Focus Reset"),
theme: String(localized: "Regain clarity"), theme: String(localized: "Regain clarity"),
notes: String(localized: "When you feel scattered, use this to refocus."), notes: String(localized: "When you feel scattered, use this to refocus."),
durationDays: 14, durationDays: 28,
timeOfDay: .anytime, timeOfDay: .anytime,
iconName: "target", iconName: "target",
category: PresetCategory.productivity.rawValue, category: PresetCategory.productivity.rawValue,
@ -162,7 +162,7 @@ enum RitualPresetLibrary {
title: String(localized: "Morning Meditation"), title: String(localized: "Morning Meditation"),
theme: String(localized: "Start with stillness"), theme: String(localized: "Start with stillness"),
notes: String(localized: "A calm mind sets the tone for a calm day."), notes: String(localized: "A calm mind sets the tone for a calm day."),
durationDays: 30, durationDays: 28,
timeOfDay: .morning, timeOfDay: .morning,
iconName: "figure.mind.and.body", iconName: "figure.mind.and.body",
category: PresetCategory.mindfulness.rawValue, category: PresetCategory.mindfulness.rawValue,
@ -176,7 +176,7 @@ enum RitualPresetLibrary {
title: String(localized: "Gratitude Practice"), title: String(localized: "Gratitude Practice"),
theme: String(localized: "Find the good"), theme: String(localized: "Find the good"),
notes: String(localized: "Shift your focus to what's going well."), notes: String(localized: "Shift your focus to what's going well."),
durationDays: 21, durationDays: 28,
timeOfDay: .evening, timeOfDay: .evening,
iconName: "heart.text.square.fill", iconName: "heart.text.square.fill",
category: PresetCategory.mindfulness.rawValue, category: PresetCategory.mindfulness.rawValue,
@ -190,7 +190,7 @@ enum RitualPresetLibrary {
title: String(localized: "Breathwork"), title: String(localized: "Breathwork"),
theme: String(localized: "Calm your nervous system"), theme: String(localized: "Calm your nervous system"),
notes: String(localized: "Use breath to reduce stress and increase focus."), notes: String(localized: "Use breath to reduce stress and increase focus."),
durationDays: 14, durationDays: 28,
timeOfDay: .anytime, timeOfDay: .anytime,
iconName: "wind", iconName: "wind",
category: PresetCategory.mindfulness.rawValue, category: PresetCategory.mindfulness.rawValue,
@ -238,7 +238,7 @@ enum RitualPresetLibrary {
title: String(localized: "Digital Detox"), title: String(localized: "Digital Detox"),
theme: String(localized: "Disconnect to reconnect"), theme: String(localized: "Disconnect to reconnect"),
notes: String(localized: "Give your mind a break from screens."), notes: String(localized: "Give your mind a break from screens."),
durationDays: 21, durationDays: 28,
timeOfDay: .evening, timeOfDay: .evening,
iconName: "iphone.slash", iconName: "iphone.slash",
category: PresetCategory.selfCare.rawValue, category: PresetCategory.selfCare.rawValue,
@ -267,7 +267,7 @@ enum RitualPresetLibrary {
title: String(localized: "Weekly Reset"), title: String(localized: "Weekly Reset"),
theme: String(localized: "Prepare for a fresh week"), theme: String(localized: "Prepare for a fresh week"),
notes: String(localized: "Sunday evening ritual to start Monday strong."), notes: String(localized: "Sunday evening ritual to start Monday strong."),
durationDays: 12, durationDays: 28,
timeOfDay: .evening, timeOfDay: .evening,
iconName: "arrow.counterclockwise.circle.fill", iconName: "arrow.counterclockwise.circle.fill",
category: PresetCategory.selfCare.rawValue, category: PresetCategory.selfCare.rawValue,

View File

@ -31,6 +31,9 @@ final class RitualStore: RitualStoreProviding {
/// Ritual that needs renewal prompt (arc just completed) /// Ritual that needs renewal prompt (arc just completed)
var ritualNeedingRenewal: Ritual? var ritualNeedingRenewal: Ritual?
/// Rituals that have been dismissed for renewal this session
private var dismissedRenewalRituals: Set<PersistentIdentifier> = []
init( init(
modelContext: ModelContext, modelContext: ModelContext,
@ -213,7 +216,7 @@ final class RitualStore: RitualStoreProviding {
/// Checks for rituals that need renewal and triggers the prompt. /// Checks for rituals that need renewal and triggers the prompt.
func checkForCompletedArcs() { func checkForCompletedArcs() {
for ritual in currentRituals { for ritual in currentRituals {
if isArcCompleted(ritual) { if isArcCompleted(ritual) && !dismissedRenewalRituals.contains(ritual.persistentModelID) {
ritualNeedingRenewal = ritual ritualNeedingRenewal = ritual
break break
} }
@ -226,6 +229,9 @@ final class RitualStore: RitualStoreProviding {
/// - durationDays: Duration for the new arc (defaults to ritual's default) /// - durationDays: Duration for the new arc (defaults to ritual's default)
/// - copyHabits: Whether to copy habits from the previous arc /// - copyHabits: Whether to copy habits from the previous arc
func renewArc(for ritual: Ritual, durationDays: Int? = nil, copyHabits: Bool = true) { func renewArc(for ritual: Ritual, durationDays: Int? = nil, copyHabits: Bool = true) {
// Clear dismissed status since user is taking action
dismissedRenewalRituals.remove(ritual.persistentModelID)
// Mark current arc as inactive // Mark current arc as inactive
if let currentArc = ritual.currentArc { if let currentArc = ritual.currentArc {
currentArc.isActive = false currentArc.isActive = false
@ -266,6 +272,9 @@ final class RitualStore: RitualStoreProviding {
/// Ends a ritual without renewal (marks it as having no active arc). /// Ends a ritual without renewal (marks it as having no active arc).
func endArc(for ritual: Ritual) { func endArc(for ritual: Ritual) {
// Clear dismissed status since user is taking action
dismissedRenewalRituals.remove(ritual.persistentModelID)
if let currentArc = ritual.currentArc { if let currentArc = ritual.currentArc {
currentArc.isActive = false currentArc.isActive = false
saveContext() saveContext()
@ -274,6 +283,9 @@ final class RitualStore: RitualStoreProviding {
/// Dismisses the renewal prompt without taking action. /// Dismisses the renewal prompt without taking action.
func dismissRenewalPrompt() { func dismissRenewalPrompt() {
if let ritual = ritualNeedingRenewal {
dismissedRenewalRituals.insert(ritual.persistentModelID)
}
ritualNeedingRenewal = nil ritualNeedingRenewal = nil
} }
@ -760,6 +772,13 @@ final class RitualStore: RitualStoreProviding {
ritual.timeOfDay = timeOfDay ritual.timeOfDay = timeOfDay
ritual.iconName = iconName ritual.iconName = iconName
ritual.category = category ritual.category = category
// Also update the current arc's end date if duration changed
if let currentArc = ritual.currentArc, currentArc.durationDays != durationDays {
let newEndDate = calendar.date(byAdding: .day, value: durationDays - 1, to: currentArc.startDate) ?? currentArc.endDate
currentArc.endDate = newEndDate
}
saveContext() saveContext()
} }