Read App Intents Documentation
Nucleic-Session: 56C5F781-4444-466B-91CE-A7734A8E117F Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -1225,6 +1225,59 @@ final class RemoteStore: ObservableObject {
|
||||
func setSessionAutoShip(_ id: SessionID, _ autoShip: Bool) { send(.setSessionAutoShip(id, autoShip)) }
|
||||
func setSessionShipBranch(_ id: SessionID, _ branch: String?) { send(.setSessionShipBranch(id, branch)) }
|
||||
|
||||
// MARK: - App Intents support
|
||||
|
||||
/// Bring networking online and dial the paired host(s) for an App Intent that runs in a *cold*
|
||||
/// background process (Siri / Shortcuts / a widget or Live Activity button), where the SwiftUI
|
||||
/// scene never mounts and `onAppear` never fires. Mirrors the push handler's silent-launch
|
||||
/// bootstrap (`startNetworkingIfNeeded` + `reconnect`). Idempotent; a no-op in demo mode.
|
||||
func bootstrapForIntent() {
|
||||
guard !demoMode else { return }
|
||||
startNetworkingIfNeeded()
|
||||
if isPaired { reconnect() }
|
||||
}
|
||||
|
||||
/// Whether any paired Mac currently has a live channel.
|
||||
var hasLiveConnection: Bool { connections.values.contains { $0.connectivity.isLive } }
|
||||
|
||||
/// Await a live connection to any paired Mac, up to `timeout` seconds — an intent must act over a
|
||||
/// *live* channel or fail clean (UX_IOS §6, §3.1). Brings networking up first if it's cold, then
|
||||
/// polls until a link comes up or the deadline passes. Returns whether a link is live.
|
||||
func awaitLiveConnection(timeout: TimeInterval = 6) async -> Bool {
|
||||
if demoMode || hasLiveConnection { return true }
|
||||
bootstrapForIntent()
|
||||
let deadline = Date().addingTimeInterval(timeout)
|
||||
while Date() < deadline {
|
||||
try? await Task.sleep(for: .milliseconds(200))
|
||||
if hasLiveConnection { return true }
|
||||
}
|
||||
return hasLiveConnection
|
||||
}
|
||||
|
||||
/// Resolve an approval by id from an App Intent with a full `Decision`. Like
|
||||
/// `respondFromNotification`, it prefers the owning Mac, else broadcasts to every live Mac (the
|
||||
/// owner resolves; the rest see an unknown / `alreadyResolved` id and no-op), and queues briefly
|
||||
/// on a dropped link so a decision made just as the socket blips still lands. `sessionID` (when
|
||||
/// the surface knows it) routes directly. Returns whether it went out over a live channel now.
|
||||
@discardableResult
|
||||
func respondToApproval(id: ApprovalID, sessionID: SessionID?, decision: Decision) -> Bool {
|
||||
if demoMode { demoHandle(.approvalRespond(id, decision)); return true }
|
||||
if let sessionID, let conn = connection(owningSession: sessionID), conn.connectivity.isLive {
|
||||
conn.send(.approvalRespond(id, decision))
|
||||
openApprovals.removeAll { $0.id == id }
|
||||
return true
|
||||
}
|
||||
let live = connections.values.filter { $0.connectivity.isLive }
|
||||
if !live.isEmpty {
|
||||
for conn in live { conn.send(.approvalRespond(id, decision)) }
|
||||
openApprovals.removeAll { $0.id == id }
|
||||
return true
|
||||
}
|
||||
pendingNotificationDecision = (id, decision, Date())
|
||||
reconnect()
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - Plumbing
|
||||
|
||||
/// Route an intent to the Mac that owns its target (mesh P3). Sessions/projects/to-dos are shown
|
||||
|
||||
Reference in New Issue
Block a user