// // TrackingViewModel.swift // HaruDanim // // Drives real-time tracking of Actions: starting/stopping time sessions // and recording count entries. // import Foundation import SwiftData import Observation @Observable final class TrackingViewModel { /// Currently running time sessions (those without an `endTime`). var activeSessions: [TimeSession] = [] // MARK: - Time tracking /// Starts a new `TimeSession` for the action, or ends the running one if it is /// already being tracked. func toggleTimeTracking(for action: Action, context: ModelContext) { if let running = runningSession(for: action, context: context) { running.endTime = .now } else { let session = TimeSession(startTime: .now, action: action) context.insert(session) } save(context) refreshActiveSessions(context: context) } /// Ends a specific running session (used by the stop button in the UI). func stopSession(_ session: TimeSession, context: ModelContext) { guard session.endTime == nil else { return } session.endTime = .now save(context) refreshActiveSessions(context: context) } // MARK: - Count tracking /// Records a `CountEntry` for the action. func incrementCount(for action: Action, amount: Int = 1, context: ModelContext) { let entry = CountEntry(timestamp: .now, amount: amount, action: action) context.insert(entry) save(context) } // MARK: - Active session loading /// Reloads `activeSessions` from the store (sessions with no `endTime`). func refreshActiveSessions(context: ModelContext) { let descriptor = FetchDescriptor( predicate: #Predicate { $0.endTime == nil }, sortBy: [SortDescriptor(\.startTime, order: .forward)] ) activeSessions = (try? context.fetch(descriptor)) ?? [] } /// Returns the running session belonging to `action`, if any. func runningSession(for action: Action, context: ModelContext) -> TimeSession? { let actionID = action.id let descriptor = FetchDescriptor( predicate: #Predicate { $0.endTime == nil && $0.action?.id == actionID } ) return try? context.fetch(descriptor).first } /// Whether the given action currently has a running session. func isTracking(_ action: Action) -> Bool { activeSessions.contains { $0.action?.id == action.id } } // MARK: - Persistence private func save(_ context: ModelContext) { do { try context.save() } catch { // Persistence failures are non-fatal for the in-flight UI; surface in logs. print("TrackingViewModel save failed: \(error)") } } }