nvrsion: Add: Fix SyncHost.swift timer/sleep to pause refresh at a slow cadence mid-turn without blocking iOS Live Activity alerts.
Nucleic-Promote: 1 Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -29,6 +29,15 @@ final class LiveActivityManager {
|
|||||||
/// connectivity, pong, diff ticks…), most of which leave the aggregate identical. Burning the
|
/// connectivity, pong, diff ticks…), most of which leave the aggregate identical. Burning the
|
||||||
/// budget on those is exactly what makes a *real* change land late and the glance read stale.
|
/// budget on those is exactly what makes a *real* change land late and the glance read stale.
|
||||||
private var lastState: NucleicSessionAttributes.ContentState?
|
private var lastState: NucleicSessionAttributes.ContentState?
|
||||||
|
/// When the last update was applied — the clock for the mid-turn churn refresh. A status change
|
||||||
|
/// (attention-signature shift) updates immediately regardless; a churn-only change (diff totals
|
||||||
|
/// ticking as the agent edits) only re-applies once `churnRefreshInterval` has elapsed, so a busy
|
||||||
|
/// turn refreshes the numbers slowly instead of on every transcript delta. A passive timestamp
|
||||||
|
/// check on the normal path — no timer sits in front of a status update.
|
||||||
|
private var lastPushAt: Date?
|
||||||
|
/// How slowly the mid-turn diff totals refresh while nothing else about a session changes —
|
||||||
|
/// mirrors the host's `SyncHost.churnRefreshInterval` so the local and pushed glances agree.
|
||||||
|
private static let churnRefreshInterval: TimeInterval = 45
|
||||||
/// The newest state waiting to be applied, and the single task draining it. Coalescing to the
|
/// The newest state waiting to be applied, and the single task draining it. Coalescing to the
|
||||||
/// latest through one serial task means the newest data always wins — firing an unstructured
|
/// latest through one serial task means the newest data always wins — firing an unstructured
|
||||||
/// `Task` per `sync` let a later update lose a race to an earlier one and freeze the glance.
|
/// `Task` per `sync` let a later update lose a race to an earlier one and freeze the glance.
|
||||||
@@ -127,8 +136,18 @@ final class LiveActivityManager {
|
|||||||
/// totals on every transcript edit but no status) doesn't spend ActivityKit's update budget. The
|
/// totals on every transcript edit but no status) doesn't spend ActivityKit's update budget. The
|
||||||
/// fresh churn still rides along on the next status-driven push.
|
/// fresh churn still rides along on the next status-driven push.
|
||||||
private func push(_ state: NucleicSessionAttributes.ContentState, hostName: String) {
|
private func push(_ state: NucleicSessionAttributes.ContentState, hostName: String) {
|
||||||
guard state.attentionSignature != lastState?.attentionSignature else { return }
|
// Apply immediately when the attention signature shifts (start/finish/approval/input — the
|
||||||
|
// alert-worthy changes). Otherwise the only difference is mid-turn churn (diff totals); let
|
||||||
|
// that refresh on a slow cadence so it stays roughly current without spending ActivityKit's
|
||||||
|
// budget on every delta. Elapsed-time gate, not a sleeping timer — a status change is never
|
||||||
|
// held behind it.
|
||||||
|
let signatureChanged = state.attentionSignature != lastState?.attentionSignature
|
||||||
|
let churnRefreshDue = lastPushAt.map {
|
||||||
|
Date().timeIntervalSince($0) >= Self.churnRefreshInterval
|
||||||
|
} ?? true
|
||||||
|
guard signatureChanged || (state != lastState && churnRefreshDue) else { return }
|
||||||
lastState = state
|
lastState = state
|
||||||
|
lastPushAt = Date()
|
||||||
|
|
||||||
guard let activity else {
|
guard let activity else {
|
||||||
// Recover an Activity that survived an app relaunch before starting a new one; the
|
// Recover an Activity that survived an app relaunch before starting a new one; the
|
||||||
@@ -193,6 +212,7 @@ final class LiveActivityManager {
|
|||||||
dismissTask = nil
|
dismissTask = nil
|
||||||
pendingState = nil
|
pendingState = nil
|
||||||
lastState = nil
|
lastState = nil
|
||||||
|
lastPushAt = nil
|
||||||
let tracked = activity
|
let tracked = activity
|
||||||
self.activity = nil
|
self.activity = nil
|
||||||
if let tracked { onActivityEnded?(tracked.id) } // let the Macs stop pushing to this token
|
if let tracked { onActivityEnded?(tracked.id) } // let the Macs stop pushing to this token
|
||||||
|
|||||||
Reference in New Issue
Block a user