Complete rename across all bundle IDs, App Groups, CloudKit containers, StoreKit product IDs, data store filenames, URL schemes, logger subsystems, Swift identifiers, user-facing strings (7 languages), file names, directory names, Xcode project, schemes, assets, and documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
4.4 KiB
Swift
125 lines
4.4 KiB
Swift
//
|
|
// DataControllerADD.swift
|
|
// Reflect
|
|
//
|
|
// SwiftData CREATE operations.
|
|
//
|
|
|
|
import SwiftData
|
|
import Foundation
|
|
import os.log
|
|
|
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.88oakapps.reflect", category: "DataControllerADD")
|
|
|
|
extension DataController {
|
|
func add(mood: Mood, forDate date: Date, entryType: EntryType) {
|
|
// Delete ALL existing entries for this date (handles duplicates)
|
|
let existing = getAllEntries(byDate: date)
|
|
for entry in existing {
|
|
cleanupPhotoIfNeeded(for: entry)
|
|
modelContext.delete(entry)
|
|
}
|
|
if !existing.isEmpty {
|
|
do {
|
|
try modelContext.save()
|
|
} catch {
|
|
logger.error("Failed to save after deleting existing entries: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
let entry = MoodEntryModel(
|
|
forDate: date,
|
|
mood: mood,
|
|
entryType: entryType
|
|
)
|
|
|
|
modelContext.insert(entry)
|
|
AnalyticsManager.shared.track(.moodLogged(mood: mood.rawValue, entryType: String(describing: entryType)))
|
|
saveAndRunDataListeners()
|
|
}
|
|
|
|
func fillInMissingDates() {
|
|
let currentOnboarding = UserDefaultsStore.getOnboarding()
|
|
var endDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: currentOnboarding)
|
|
// Since it's for views, take away the last date so vote is enabled
|
|
guard let adjustedEndDate = Calendar.current.date(byAdding: .day, value: -1, to: endDate) else {
|
|
logger.error("Failed to calculate adjusted end date")
|
|
return
|
|
}
|
|
endDate = adjustedEndDate
|
|
|
|
let descriptor = FetchDescriptor<MoodEntryModel>(
|
|
sortBy: [SortDescriptor(\.forDate, order: .reverse)]
|
|
)
|
|
|
|
let entries: [MoodEntryModel]
|
|
do {
|
|
entries = try modelContext.fetch(descriptor)
|
|
} catch {
|
|
logger.error("Failed to fetch entries for fill-in: \(error.localizedDescription)")
|
|
return
|
|
}
|
|
|
|
guard let firstEntry = entries.last else { return }
|
|
|
|
let allDates: [Date] = Date.dates(from: firstEntry.forDate, toDate: endDate, includingToDate: true).compactMap {
|
|
Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: $0)
|
|
}
|
|
|
|
let existingDates: Set<Date> = Set(entries.compactMap {
|
|
Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: $0.forDate)
|
|
})
|
|
|
|
let missing = Array(Set(allDates).subtracting(existingDates)).sorted(by: >)
|
|
|
|
guard !missing.isEmpty else { return }
|
|
|
|
// Batch insert all missing dates without triggering listeners
|
|
for date in missing {
|
|
// Add 12 hours to avoid UTC offset issues
|
|
guard let adjustedDate = Calendar.current.date(byAdding: .hour, value: 12, to: date) else {
|
|
logger.error("Failed to calculate adjusted date for \(date)")
|
|
continue
|
|
}
|
|
let entry = MoodEntryModel(
|
|
forDate: adjustedDate,
|
|
mood: .missing,
|
|
entryType: .filledInMissing
|
|
)
|
|
modelContext.insert(entry)
|
|
}
|
|
|
|
// Single save and listener notification at the end
|
|
saveAndRunDataListeners()
|
|
AnalyticsManager.shared.track(.missingEntriesFilled(count: missing.count))
|
|
}
|
|
|
|
func fixWrongWeekdays() {
|
|
let data = getData(startDate: Date(timeIntervalSince1970: 0), endDate: Date(), includedDays: [])
|
|
for entry in data {
|
|
entry.weekDay = Calendar.current.component(.weekday, from: entry.forDate)
|
|
}
|
|
saveAndRunDataListeners()
|
|
}
|
|
|
|
func removeNoForDates() {
|
|
// Note: With SwiftData's non-optional forDate, this is essentially a no-op
|
|
// Keeping for API compatibility
|
|
}
|
|
|
|
/// Batch insert mood entries without per-entry analytics or listener notifications.
|
|
/// Used for CSV import where side effects should fire once at the end.
|
|
func addBatch(entries: [(mood: Mood, date: Date, entryType: EntryType)]) {
|
|
for (mood, date, entryType) in entries {
|
|
let existing = getAllEntries(byDate: date)
|
|
for entry in existing {
|
|
cleanupPhotoIfNeeded(for: entry)
|
|
modelContext.delete(entry)
|
|
}
|
|
let entry = MoodEntryModel(forDate: date, mood: mood, entryType: entryType)
|
|
modelContext.insert(entry)
|
|
}
|
|
saveAndRunDataListeners()
|
|
}
|
|
}
|