// // SharedMoodIntent.swift // Feels // // Single VoteMoodIntent for all targets. // Main app uses ForegroundContinuableIntent to run widget intents in app process. // // Add this file to ALL targets: Feels (iOS), FeelsWidgetExtension, Feels Watch App // import AppIntents import SwiftUI import SwiftData import WidgetKit import os.log // MARK: - Vote Mood Intent struct VoteMoodIntent: AppIntent { static var title: LocalizedStringResource = "Vote Mood" static var description = IntentDescription("Record your mood for today") static var openAppWhenRun: Bool = false @Parameter(title: "Mood") var moodValue: Int init() { self.moodValue = 2 } init(mood: Mood) { self.moodValue = mood.rawValue } @MainActor func perform() async throws -> some IntentResult { let mood = Mood(rawValue: moodValue) ?? .average #if os(watchOS) // Watch: Send to iPhone via WatchConnectivity let date = Date() _ = WatchConnectivityManager.shared.sendMoodToPhone(mood: moodValue, date: date) WidgetCenter.shared.reloadAllTimelines() #elseif WIDGET_EXTENSION // Widget: Save to shared container, main app handles side effects let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding()) WidgetMoodSaver.save(mood: mood, date: votingDate) WidgetCenter.shared.reloadAllTimelines() #else // Main app: Full logging with all side effects let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding()) MoodLogger.shared.logMood(mood, for: votingDate, entryType: .widget) let dateString = ISO8601DateFormatter().string(from: Calendar.current.startOfDay(for: votingDate)) GroupUserDefaults.groupDefaults.set(dateString, forKey: UserDefaultsStore.Keys.lastVotedDate.rawValue) #endif return .result() } } // MARK: - Main App: Run widget intents in app process #if !WIDGET_EXTENSION && !os(watchOS) extension VoteMoodIntent: ForegroundContinuableIntent {} #endif // MARK: - Widget: Save to shared container #if WIDGET_EXTENSION enum WidgetMoodSaver { private static let logger = Logger(subsystem: "com.tt.feels.widget", category: "WidgetMoodSaver") @MainActor static func save(mood: Mood, date: Date) { let schema = Schema([MoodEntryModel.self]) let appGroupID = Constants.currentGroupShareId guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) else { logger.error("App Group not available") return } #if DEBUG let storeURL = containerURL.appendingPathComponent("Feels-Debug.store") #else let storeURL = containerURL.appendingPathComponent("Feels.store") #endif do { let config = ModelConfiguration(schema: schema, url: storeURL, cloudKitDatabase: .none) let container = try ModelContainer(for: schema, configurations: [config]) let context = container.mainContext // Delete existing entry for this date let startDate = Calendar.current.startOfDay(for: date) let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)! var descriptor = FetchDescriptor( predicate: #Predicate { entry in entry.forDate >= startDate && entry.forDate <= endDate } ) descriptor.fetchLimit = 1 if let existing = try? context.fetch(descriptor).first { context.delete(existing) try? context.save() } // Create new entry let entry = MoodEntryModel(forDate: date, mood: mood, entryType: .widget) context.insert(entry) try context.save() logger.info("Saved mood \(mood.rawValue) from widget") } catch { logger.error("Failed to save mood: \(error.localizedDescription)") } } } #endif