1import * as Notifications from "expo-notifications";
2
3import { setAndroidThreadOnScreen } from "./androidNotifications";
4import { foregroundNotificationBehavior } from "./foregroundNotificationBehavior";
5import { routeAgentNotificationResponseOnce, threadDeepLinkOnScreen } from "./notificationPayload";
6import { consumeLastAgentNotificationResponse } from "./notificationResponseConsumer";
7
8let deepLinkOnScreen: string | null = null;
9
10/** Called with the workspace pathname on every root navigation state change. */
11export function setAgentNotificationPathname(pathname: string): void {
12 deepLinkOnScreen = threadDeepLinkOnScreen(pathname);
13 setAndroidThreadOnScreen(deepLinkOnScreen);
14}
15
16/**
17 * App-lifetime notification routing: foreground presentation suppresses the
18 * thread already on screen, and taps (including the cold-start tap) open the
19 * notification's deep link through `navigate`.
20 */
21export function installAgentNotificationNavigation(navigate: (href: string) => void): () => void {
22 const handledResponseIds = new Set<string>();
23 Notifications.setNotificationHandler({
24 handleNotification: (notification) =>
25 Promise.resolve(foregroundNotificationBehavior(notification, deepLinkOnScreen)),
26 });
27
28 const handleResponse = (response: Notifications.NotificationResponse): void => {
29 routeAgentNotificationResponseOnce({ handledResponseIds, response, navigate });
30 };
31 const subscription = Notifications.addNotificationResponseReceivedListener(handleResponse);
32 void consumeLastAgentNotificationResponse({
33 getLastResponse: () => Notifications.getLastNotificationResponseAsync(),
34 clearLastResponse: () => Notifications.clearLastNotificationResponseAsync(),
35 handleResponse,
36 });
37
38 return () => {
39 subscription.remove();
40 Notifications.setNotificationHandler(null);
41 };
42}