Fix 7 data mutation layer risks identified in audit

- save()/saveAndRunDataListeners() now return @discardableResult Bool;
  listeners only fire on successful save
- MoodLogger.updateMood() now recalculates streak, updates Live Activity,
  and notifies watch (was missing these side effects)
- CSV import uses new addBatch()/importMoods() for O(1) side effects
  instead of O(n) per-row widget reloads and streak calcs
- Foreground task ordering: fillInMissingDates() now runs before
  removeDuplicates() so backfill-created duplicates are caught same cycle
- WidgetMoodSaver deletes ALL entries for date (was fetchLimit=1, leaving
  CloudKit sync duplicates behind)
- cleanupPhotoIfNeeded logs warning on failed photo deletion instead of
  silently orphaning files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-14 23:09:11 -06:00
parent 3023475f66
commit f1cd81c395
8 changed files with 306 additions and 55 deletions

View File

@@ -775,7 +775,7 @@ struct SettingsContentView: View {
ZStack {
theme.currentTheme.secondaryBGColor
Button {
DataController.shared.clearDB()
MoodLogger.shared.deleteAllData()
} label: {
HStack(spacing: 12) {
Image(systemName: "trash")
@@ -1347,6 +1347,7 @@ struct SettingsView: View {
var rows = input.components(separatedBy: "\n")
guard !rows.isEmpty else { return }
rows.removeFirst()
var importEntries: [(mood: Mood, date: Date, entryType: EntryType)] = []
for row in rows {
let stripped = row.replacingOccurrences(of: " +0000", with: "")
let columns = stripped.components(separatedBy: ",")
@@ -1359,10 +1360,9 @@ struct SettingsView: View {
}
let mood = Mood(rawValue: moodValue) ?? .missing
let entryType = EntryType(rawValue: Int(columns[2]) ?? 0) ?? .listView
DataController.shared.add(mood: mood, forDate: forDate, entryType: entryType)
importEntries.append((mood: mood, date: forDate, entryType: entryType))
}
DataController.shared.saveAndRunDataListeners()
MoodLogger.shared.importMoods(importEntries)
AnalyticsManager.shared.track(.importSucceeded)
} else {
AnalyticsManager.shared.track(.importFailed(error: nil))
@@ -1743,7 +1743,7 @@ struct SettingsView: View {
ZStack {
theme.currentTheme.secondaryBGColor
Button(action: {
DataController.shared.clearDB()
MoodLogger.shared.deleteAllData()
}, label: {
Text("Clear DB")
.foregroundColor(textColor)