diff --git a/NucleicRemote/NucleicRemote/LiveActivityManager.swift b/NucleicRemote/NucleicRemote/LiveActivityManager.swift index fd39e6d..194eacf 100644 --- a/NucleicRemote/NucleicRemote/LiveActivityManager.swift +++ b/NucleicRemote/NucleicRemote/LiveActivityManager.swift @@ -209,9 +209,35 @@ final class LiveActivityManager { Date().timeIntervalSince($0) >= Self.churnRefreshInterval } ?? true guard signatureChanged || (state != lastState && churnRefreshDue) else { return } + // When a session newly needs the user while we're backgrounded, this update carries the alert + // (sound/haptic) — there's no banner then, we prefer the glance. Foreground, the app UI / a + // banner announces it, so the glance updates silently. Computed against the *previous* state + // before `lastState` is overwritten. + let alert = foreground ? nil : Self.alert(from: lastState, to: state) lastState = state lastPushAt = Date() - enqueue(state) + enqueue(state, alert: alert) + } + + /// The alert an update should carry when backgrounded: a session newly needs the user. An approval + /// (its Allow/Deny live on the glance) outranks a needs-input for the wording; a rise in neither ⇒ + /// nil (silent update). Reuses the same localized keys as the host's pushed alert so a local and a + /// pushed alert read identically. Approvals/inputs that merely persist (or resolve) don't re-alert. + private static func alert( + from previous: NucleicSessionAttributes.ContentState?, + to next: NucleicSessionAttributes.ContentState + ) -> AlertConfiguration? { + if next.approvalCount > (previous?.approvalCount ?? 0) { + return AlertConfiguration( + title: LocalizedStringResource("approval.title"), + body: LocalizedStringResource("approval.pending"), sound: .default) + } + if next.needsYouCount > (previous?.needsYouCount ?? 0) { + return AlertConfiguration( + title: LocalizedStringResource("input.title"), + body: LocalizedStringResource("input.pending"), sound: .default) + } + return nil } /// Start the process-lived observers that make push-to-start work: the device's push-to-start