// // LiveRunState.swift // Workouts // // Copyright 2025 Rouslan Zenetl. All Rights Reserved. // import Foundation import Observation /// Phone-side holder for the *ephemeral* live-run frames the watch broadcasts while it drives /// an exercise (see `LiveProgress`). The mirror UI observes this; nothing here is persisted. /// /// Phase 1 is watch-drives / phone-mirrors, so this is read-only state fed by the connectivity /// bridge — the phone never sends back, which is why there's no echo loop to guard against yet. @Observable @MainActor final class LiveRunState { /// The latest frame from the driving device, or `nil` when no run is being mirrored. private(set) var current: LiveProgress? /// A log the user manually closed the mirror for; suppressed until that run ends. private var mutedLogID: String? /// The frame to actually present, honoring a manual dismiss. var presentable: LiveProgress? { guard let c = current, c.logID != mutedLogID else { return nil } return c } /// Apply an incoming frame, dropping a stale one for the same run. func apply(_ frame: LiveProgress) { if let c = current, c.logID == frame.logID, frame.version < c.version { return } current = frame } /// The driver left the run (cancel / done / navigated away) — stop mirroring it. func end(logID: String) { if current?.logID == logID { current = nil } if mutedLogID == logID { mutedLogID = nil } } /// The user dismissed the mirror; don't re-present this run until it ends. func mute() { mutedLogID = current?.logID } }