Make live-run mirroring symmetric: phone-driven runs reach the watch
Two-way driving only worked watch->phone: the watch's navigated driver broadcast and the phone auto-presented a follower cover. The reverse failed on both ends — the phone's in-list ExerciseProgressView never broadcast (only its cover did), and the watch had no surface to present an incoming run. - Wire the live channel into the phone's in-list driver (broadcast + follow) via a progressView(logID:) helper in WorkoutLogListView. - Add a watch follower cover (LiveRunCoverView, mirroring the phone's), presented from ContentView when the phone drives a run the watch isn't already in; the watch bridge gains presentable / muteLive. - Add a navigatedRunID guard on both sides so a device already in the run follows it inline rather than stacking a cover over itself. Now starting or driving on either device surfaces the run on the other — as a follower cover when idle, or inline when already in that run — and either side can take over. Claude-Session: https://claude.ai/code/session_01SCv7zvGFcKy47KSTnTLxRe
This commit is contained in:
@@ -33,6 +33,24 @@ final class WatchConnectivityBridge: NSObject {
|
||||
/// this to follow a phone-driven transition; it's never persisted.
|
||||
private(set) var liveIncoming: LiveProgress?
|
||||
|
||||
/// The run currently open in the watch's navigated driver. When the incoming frame is for
|
||||
/// it, the watch follows inline there and suppresses the follower cover (so it never stacks
|
||||
/// on top of a run the user already has open).
|
||||
var navigatedRunID: String?
|
||||
|
||||
/// A run the user dismissed the follower cover for; suppressed until that run ends.
|
||||
private var mutedLogID: String?
|
||||
|
||||
/// The frame to present as a follower cover when the phone drives a run the watch isn't
|
||||
/// already showing: the latest, unless the user dismissed it or has that run open inline.
|
||||
var presentable: LiveProgress? {
|
||||
guard let f = liveIncoming, f.logID != mutedLogID, f.logID != navigatedRunID else { return nil }
|
||||
return f
|
||||
}
|
||||
|
||||
/// The user dismissed the follower cover; don't re-present this run until it ends.
|
||||
func muteLive() { mutedLogID = liveIncoming?.logID }
|
||||
|
||||
private var context: ModelContext { container.mainContext }
|
||||
|
||||
init(container: ModelContainer) {
|
||||
@@ -96,9 +114,10 @@ final class WatchConnectivityBridge: NSObject {
|
||||
liveIncoming = frame
|
||||
}
|
||||
|
||||
/// The phone left the run — stop following it.
|
||||
/// The phone left the run — stop following it (and clear any dismiss for it).
|
||||
private func endIncomingLive(logID: String) {
|
||||
if liveIncoming?.logID == logID { liveIncoming = nil }
|
||||
if mutedLogID == logID { mutedLogID = nil }
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
Reference in New Issue
Block a user