Complete analytics overhaul: delete EventLogger.swift, create Analytics.swift with typed event enum (~45 events), screen tracking, super properties (theme, icon pack, voting layout, etc.), session replay with kill switch, autocapture, and network telemetry. Replace all 99 call sites across 38 files with compiler-enforced typed events in object_action naming convention. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
3.0 KiB
Swift
88 lines
3.0 KiB
Swift
//
|
|
// DataControllerADD.swift
|
|
// Feels
|
|
//
|
|
// SwiftData CREATE operations.
|
|
//
|
|
|
|
import SwiftData
|
|
import Foundation
|
|
|
|
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 {
|
|
modelContext.delete(entry)
|
|
}
|
|
if !existing.isEmpty {
|
|
try? modelContext.save()
|
|
}
|
|
|
|
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
|
|
endDate = Calendar.current.date(byAdding: .day, value: -1, to: endDate)!
|
|
|
|
let descriptor = FetchDescriptor<MoodEntryModel>(
|
|
sortBy: [SortDescriptor(\.forDate, order: .reverse)]
|
|
)
|
|
|
|
guard let entries = try? modelContext.fetch(descriptor),
|
|
let firstEntry = entries.last else { return }
|
|
|
|
let allDates: [Date] = Date.dates(from: firstEntry.forDate, toDate: endDate, includingToDate: true).map {
|
|
Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: $0)!
|
|
}
|
|
|
|
let existingDates: Set<Date> = Set(entries.map {
|
|
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
|
|
let adjustedDate = Calendar.current.date(byAdding: .hour, value: 12, to: date)!
|
|
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)
|
|
}
|
|
save()
|
|
}
|
|
|
|
func removeNoForDates() {
|
|
// Note: With SwiftData's non-optional forDate, this is essentially a no-op
|
|
// Keeping for API compatibility
|
|
}
|
|
}
|