| ... | ... | @@ -2,6 +2,7 @@ import { addPushToStartTokenListener, type LiveActivity } from "expo-widgets"; |
| 2 | 2 | import Constants from "expo-constants"; |
| 3 | 3 | import * as Notifications from "expo-notifications"; |
| 4 | 4 | import * as Effect from "effect/Effect"; |
| 5 | import * as Schema from "effect/Schema"; |
| 5 | 6 | import { Platform } from "react-native"; |
| 6 | 7 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 7 | 8 | import { |
| ... | ... | @@ -28,6 +29,33 @@ import { resolveCloudPublicConfig } from "../cloud/publicConfig"; |
| 28 | 29 | import { makeRelayDeviceRegistrationRequest } from "./registrationPayload"; |
| 29 | 30 | |
| 30 | 31 | const REMOTE_ACTIVITY_REGISTRATION_RETRY_MS = 15_000; |
| 32 | |
| 33 | const AgentAwarenessOperation = Schema.Literals([ |
| 34 | "read-notification-permissions", |
| 35 | "read-native-push-token", |
| 36 | "read-device-registration-relay-token", |
| 37 | "read-device-unregistration-relay-token", |
| 38 | "read-live-activity-registration-relay-token", |
| 39 | "load-device-registration-identifier", |
| 40 | "load-device-registration-preferences", |
| 41 | "load-device-unregistration-identifier", |
| 42 | "read-live-activity-push-token", |
| 43 | "load-live-activity-registration-identifier", |
| 44 | "list-active-live-activities", |
| 45 | ]); |
| 46 | |
| 47 | export class AgentAwarenessOperationError extends Schema.TaggedErrorClass<AgentAwarenessOperationError>()( |
| 48 | "AgentAwarenessOperationError", |
| 49 | { |
| 50 | operation: AgentAwarenessOperation, |
| 51 | cause: Schema.Defect(), |
| 52 | }, |
| 53 | ) { |
| 54 | override get message(): string { |
| 55 | return `Agent awareness operation ${this.operation} failed.`; |
| 56 | } |
| 57 | } |
| 58 | |
| 31 | 59 | const environmentConnections = new Map<EnvironmentId, SavedRemoteConnection>(); |
| 32 | 60 | const activityPushTokenListeners = new WeakSet<LiveActivity<AgentActivityProps>>(); |
| 33 | 61 | let pushToStartSubscription: { remove: () => void } | null = null; |
| ... | ... | @@ -137,14 +165,22 @@ function nativePushTokenRegistration(observedPushToken?: string) { |
| 137 | 165 | } |
| 138 | 166 | const permissions = yield* Effect.tryPromise({ |
| 139 | 167 | try: () => Notifications.getPermissionsAsync(), |
| 140 | | catch: (error) => error, |
| 168 | catch: (cause) => |
| 169 | new AgentAwarenessOperationError({ |
| 170 | operation: "read-notification-permissions", |
| 171 | cause, |
| 172 | }), |
| 141 | 173 | }); |
| 142 | 174 | if (!permissions.granted) { |
| 143 | 175 | return { notificationsEnabled: false, pushToken: null }; |
| 144 | 176 | } |
| 145 | 177 | const token = yield* Effect.tryPromise({ |
| 146 | 178 | try: () => Notifications.getDevicePushTokenAsync(), |
| 147 | | catch: (error) => error, |
| 179 | catch: (cause) => |
| 180 | new AgentAwarenessOperationError({ |
| 181 | operation: "read-native-push-token", |
| 182 | cause, |
| 183 | }), |
| 148 | 184 | }).pipe( |
| 149 | 185 | Effect.tapError((error) => |
| 150 | 186 | Effect.sync(() => { |
| ... | ... | @@ -161,16 +197,19 @@ function nativePushTokenRegistration(observedPushToken?: string) { |
| 161 | 197 | }); |
| 162 | 198 | } |
| 163 | 199 | |
| 164 | | const relayToken = Effect.gen(function* () { |
| 165 | | const provider = relayTokenProvider; |
| 166 | | if (!provider) { |
| 167 | | return null; |
| 168 | | } |
| 169 | | return yield* Effect.tryPromise({ |
| 170 | | try: provider, |
| 171 | | catch: (error) => error, |
| 200 | const relayToken = ( |
| 201 | operation: "read-device-registration-relay-token" | "read-live-activity-registration-relay-token", |
| 202 | ) => |
| 203 | Effect.gen(function* () { |
| 204 | const provider = relayTokenProvider; |
| 205 | if (!provider) { |
| 206 | return null; |
| 207 | } |
| 208 | return yield* Effect.tryPromise({ |
| 209 | try: provider, |
| 210 | catch: (cause) => new AgentAwarenessOperationError({ operation, cause }), |
| 211 | }); |
| 172 | 212 | }); |
| 173 | | }); |
| 174 | 213 | |
| 175 | 214 | function registerDeviceWithRelay( |
| 176 | 215 | body: RelayDeviceRegistrationRequest, |
| ... | ... | @@ -185,7 +224,7 @@ function registerDeviceWithRelay( |
| 185 | 224 | return; |
| 186 | 225 | } |
| 187 | 226 | if (!readRelayConfig()) return; |
| 188 | | const token = yield* relayToken; |
| 227 | const token = yield* relayToken("read-device-registration-relay-token"); |
| 189 | 228 | if (expectedGeneration !== deviceRegistrationGeneration) { |
| 190 | 229 | logRegistrationDebug("device registration cancelled after auth lookup", { |
| 191 | 230 | expectedGeneration, |
| ... | ... | @@ -220,7 +259,11 @@ function unregisterDeviceWithRelay(input: { |
| 220 | 259 | if (!readRelayConfig()) return; |
| 221 | 260 | const token = yield* Effect.tryPromise({ |
| 222 | 261 | try: input.tokenProvider, |
| 223 | | catch: (error) => error, |
| 262 | catch: (cause) => |
| 263 | new AgentAwarenessOperationError({ |
| 264 | operation: "read-device-unregistration-relay-token", |
| 265 | cause, |
| 266 | }), |
| 224 | 267 | }); |
| 225 | 268 | if (!token) { |
| 226 | 269 | logRegistrationDebug("relay device unregistration skipped; user is not signed in"); |
| ... | ... | @@ -240,7 +283,7 @@ function registerLiveActivityWithRelay( |
| 240 | 283 | ): Effect.Effect<boolean, unknown, ManagedRelay.ManagedRelayClient> { |
| 241 | 284 | return Effect.gen(function* () { |
| 242 | 285 | if (!readRelayConfig()) return false; |
| 243 | | const token = yield* relayToken; |
| 286 | const token = yield* relayToken("read-live-activity-registration-relay-token"); |
| 244 | 287 | if (!token) { |
| 245 | 288 | logRegistrationDebug("relay live activity registration skipped; user is not signed in"); |
| 246 | 289 | return false; |
| ... | ... | @@ -381,11 +424,19 @@ function registerDevice( |
| 381 | 424 | const [deviceId, preferences] = yield* Effect.all([ |
| 382 | 425 | Effect.tryPromise({ |
| 383 | 426 | try: () => loadOrCreateAgentAwarenessDeviceId(), |
| 384 | | catch: (error) => error, |
| 427 | catch: (cause) => |
| 428 | new AgentAwarenessOperationError({ |
| 429 | operation: "load-device-registration-identifier", |
| 430 | cause, |
| 431 | }), |
| 385 | 432 | }), |
| 386 | 433 | Effect.tryPromise({ |
| 387 | 434 | try: () => loadPreferences(), |
| 388 | | catch: (error) => error, |
| 435 | catch: (cause) => |
| 436 | new AgentAwarenessOperationError({ |
| 437 | operation: "load-device-registration-preferences", |
| 438 | cause, |
| 439 | }), |
| 389 | 440 | }), |
| 390 | 441 | ]); |
| 391 | 442 | const pushTokenRegistration = yield* nativePushTokenRegistration(input?.observedPushToken); |
| ... | ... | @@ -519,7 +570,11 @@ export function unregisterAgentAwarenessDeviceForCurrentUser( |
| 519 | 570 | return Effect.gen(function* () { |
| 520 | 571 | const deviceId = yield* Effect.tryPromise({ |
| 521 | 572 | try: () => loadAgentAwarenessDeviceId(), |
| 522 | | catch: (error) => error, |
| 573 | catch: (cause) => |
| 574 | new AgentAwarenessOperationError({ |
| 575 | operation: "load-device-unregistration-identifier", |
| 576 | cause, |
| 577 | }), |
| 523 | 578 | }); |
| 524 | 579 | if (!deviceId) { |
| 525 | 580 | return; |
| ... | ... | @@ -544,7 +599,11 @@ export function registerLiveActivityPushToken(input: { |
| 544 | 599 | |
| 545 | 600 | const activityPushToken = yield* Effect.tryPromise({ |
| 546 | 601 | try: () => input.activity.getPushToken(), |
| 547 | | catch: (error) => error, |
| 602 | catch: (cause) => |
| 603 | new AgentAwarenessOperationError({ |
| 604 | operation: "read-live-activity-push-token", |
| 605 | cause, |
| 606 | }), |
| 548 | 607 | }); |
| 549 | 608 | if (!activityPushToken) { |
| 550 | 609 | if (activityPushTokenListeners.has(input.activity)) { |
| ... | ... | @@ -592,7 +651,11 @@ function registerLiveActivityPushTokenValue(input: { |
| 592 | 651 | return Effect.gen(function* () { |
| 593 | 652 | const deviceId = yield* Effect.tryPromise({ |
| 594 | 653 | try: () => loadOrCreateAgentAwarenessDeviceId(), |
| 595 | | catch: (error) => error, |
| 654 | catch: (cause) => |
| 655 | new AgentAwarenessOperationError({ |
| 656 | operation: "load-live-activity-registration-identifier", |
| 657 | cause, |
| 658 | }), |
| 596 | 659 | }); |
| 597 | 660 | const registered = yield* registerLiveActivityWithRelay({ |
| 598 | 661 | deviceId, |
| ... | ... | @@ -633,7 +696,11 @@ export function refreshActiveLiveActivityRemoteRegistration(): Effect.Effect< |
| 633 | 696 | |
| 634 | 697 | const activities = yield* Effect.try({ |
| 635 | 698 | try: () => AgentActivity.getInstances(), |
| 636 | | catch: (error) => error, |
| 699 | catch: (cause) => |
| 700 | new AgentAwarenessOperationError({ |
| 701 | operation: "list-active-live-activities", |
| 702 | cause, |
| 703 | }), |
| 637 | 704 | }).pipe( |
| 638 | 705 | Effect.catch((error) => |
| 639 | 706 | Effect.sync(() => { |