- Add HealthKit State of Mind sync for mood entries - Add Live Activity with streak display and rating time window - Add App Shortcuts/Siri integration for voice mood logging - Add TipKit hints for feature discovery - Add centralized MoodLogger for consistent side effects - Add reminder time setting in Settings with time picker - Fix duplicate notifications when changing reminder time - Fix Live Activity streak showing 0 when not yet rated today - Fix slow tap response in entry detail mood selection - Update widget timeline to refresh at rating time - Sync widgets when reminder time changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
90 lines
3.1 KiB
Swift
90 lines
3.1 KiB
Swift
//
|
|
// MoodLogger.swift
|
|
// Feels
|
|
//
|
|
// Centralized mood logging service that handles all side effects
|
|
//
|
|
|
|
import Foundation
|
|
import WidgetKit
|
|
|
|
/// Centralized service for logging moods with all associated side effects.
|
|
/// All mood entry points should use this service to ensure consistent behavior.
|
|
@MainActor
|
|
final class MoodLogger {
|
|
static let shared = MoodLogger()
|
|
|
|
private init() {}
|
|
|
|
/// Log a mood entry with all associated side effects.
|
|
/// This is the single source of truth for mood logging in the app.
|
|
///
|
|
/// - Parameters:
|
|
/// - mood: The mood to log
|
|
/// - date: The date for the mood entry
|
|
/// - entryType: The source of the mood entry (header, widget, siri, etc.)
|
|
/// - syncHealthKit: Whether to sync to HealthKit (default true, but widget can't access HealthKit)
|
|
/// - updateTips: Whether to update TipKit parameters (default true, but widget can't access TipKit)
|
|
func logMood(
|
|
_ mood: Mood,
|
|
for date: Date,
|
|
entryType: EntryType,
|
|
syncHealthKit: Bool = true,
|
|
updateTips: Bool = true
|
|
) {
|
|
// 1. Add mood entry to data store
|
|
DataController.shared.add(mood: mood, forDate: date, entryType: entryType)
|
|
|
|
// Skip side effects for placeholder/missing moods
|
|
guard mood != .missing && mood != .placeholder else { return }
|
|
|
|
// 2. Sync to HealthKit if enabled and requested
|
|
if syncHealthKit {
|
|
let healthKitEnabled = GroupUserDefaults.groupDefaults.bool(forKey: UserDefaultsStore.Keys.healthKitEnabled.rawValue)
|
|
if healthKitEnabled {
|
|
Task {
|
|
try? await HealthKitManager.shared.saveMood(mood, for: date)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Calculate current streak for Live Activity and TipKit
|
|
let streak = calculateCurrentStreak()
|
|
|
|
// 4. Update Live Activity
|
|
LiveActivityManager.shared.updateActivity(streak: streak, mood: mood)
|
|
LiveActivityScheduler.shared.scheduleForNextDay()
|
|
|
|
// 5. Update TipKit parameters if requested
|
|
if updateTips {
|
|
TipsManager.shared.onMoodLogged()
|
|
TipsManager.shared.updateStreak(streak)
|
|
}
|
|
|
|
// 6. Reload widgets
|
|
WidgetCenter.shared.reloadAllTimelines()
|
|
}
|
|
|
|
/// Calculate the current mood streak
|
|
private func calculateCurrentStreak() -> Int {
|
|
var streak = 0
|
|
var checkDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
|
|
|
|
while true {
|
|
let dayStart = Calendar.current.startOfDay(for: checkDate)
|
|
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
|
|
|
|
let entry = DataController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
|
|
|
|
if let entry = entry, entry.mood != .missing && entry.mood != .placeholder {
|
|
streak += 1
|
|
checkDate = Calendar.current.date(byAdding: .day, value: -1, to: checkDate)!
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return streak
|
|
}
|
|
}
|