1import { EventEmitter, Subscription, CodedError, UnavailabilityError } from 'expo-modules-core'; 2 3import { Notification, NotificationBehavior } from './Notifications.types'; 4import NotificationsHandlerModule from './NotificationsHandlerModule'; 5 6/** 7 * @hidden 8 */ 9export class NotificationTimeoutError extends CodedError { 10 info: { notification: Notification; id: string }; 11 constructor(notificationId: string, notification: Notification) { 12 super('ERR_NOTIFICATION_TIMEOUT', `Notification handling timed out for ID ${notificationId}.`); 13 this.info = { id: notificationId, notification }; 14 } 15} 16 17// @docsMissing 18export type NotificationHandlingError = NotificationTimeoutError | Error; 19 20export interface NotificationHandler { 21 /** 22 * A function accepting an incoming notification returning a `Promise` resolving to a behavior ([`NotificationBehavior`](#notificationbehavior)) 23 * applicable to the notification 24 * @param notification An object representing the notification. 25 */ 26 handleNotification: (notification: Notification) => Promise<NotificationBehavior>; 27 /** 28 * A function called whenever an incoming notification is handled successfully. 29 * @param notificationId Identifier of the notification. 30 */ 31 handleSuccess?: (notificationId: string) => void; 32 /** 33 * A function called whenever handling of an incoming notification fails. 34 * @param notificationId Identifier of the notification. 35 * @param error An error which occurred in form of `NotificationHandlingError` object. 36 */ 37 handleError?: (notificationId: string, error: NotificationHandlingError) => void; 38} 39 40type HandleNotificationEvent = { 41 id: string; 42 notification: Notification; 43}; 44 45type HandleNotificationTimeoutEvent = HandleNotificationEvent; 46 47// Web uses SyntheticEventEmitter 48const notificationEmitter = new EventEmitter(NotificationsHandlerModule); 49 50const handleNotificationEventName = 'onHandleNotification'; 51const handleNotificationTimeoutEventName = 'onHandleNotificationTimeout'; 52 53let handleSubscription: Subscription | null = null; 54let handleTimeoutSubscription: Subscription | null = null; 55 56/** 57 * When a notification is received while the app is running, using this function you can set a callback that will decide 58 * whether the notification should be shown to the user or not. 59 * 60 * When a notification is received, `handleNotification` is called with the incoming notification as an argument. 61 * The function should respond with a behavior object within 3 seconds, otherwise, the notification will be discarded. 62 * If the notification is handled successfully, `handleSuccess` is called with the identifier of the notification, 63 * otherwise (or on timeout) `handleError` will be called. 64 * 65 * The default behavior when the handler is not set or does not respond in time is not to show the notification. 66 * @param handler A single parameter which should be either `null` (if you want to clear the handler) or a [`NotificationHandler`](#notificationhandler) object. 67 * 68 * @example Implementing a notification handler that always shows the notification when it is received. 69 * ```jsx 70 * import * as Notifications from 'expo-notifications'; 71 * 72 * Notifications.setNotificationHandler({ 73 * handleNotification: async () => ({ 74 * shouldShowAlert: true, 75 * shouldPlaySound: false, 76 * shouldSetBadge: false, 77 * }), 78 * }); 79 * ``` 80 * @header inForeground 81 */ 82export function setNotificationHandler(handler: NotificationHandler | null): void { 83 if (handleSubscription) { 84 handleSubscription.remove(); 85 handleSubscription = null; 86 } 87 if (handleTimeoutSubscription) { 88 handleTimeoutSubscription.remove(); 89 handleTimeoutSubscription = null; 90 } 91 92 if (handler) { 93 handleSubscription = notificationEmitter.addListener<HandleNotificationEvent>( 94 handleNotificationEventName, 95 async ({ id, notification }) => { 96 if (!NotificationsHandlerModule.handleNotificationAsync) { 97 handler.handleError?.( 98 id, 99 new UnavailabilityError('Notifications', 'handleNotificationAsync') 100 ); 101 return; 102 } 103 104 try { 105 const behavior = await handler.handleNotification(notification); 106 await NotificationsHandlerModule.handleNotificationAsync(id, behavior); 107 handler.handleSuccess?.(id); 108 } catch (error) { 109 handler.handleError?.(id, error); 110 } 111 } 112 ); 113 114 handleTimeoutSubscription = notificationEmitter.addListener<HandleNotificationTimeoutEvent>( 115 handleNotificationTimeoutEventName, 116 ({ id, notification }) => 117 handler.handleError?.(id, new NotificationTimeoutError(id, notification)) 118 ); 119 } 120} 121