UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

78 lines (69 loc) • 2.79 kB
import type { Notification, NotificationBehavior, NotificationHandlerInput, } from './types' // expo shows an arriving foreground notification until the app says // otherwise, and keeps showing it when the handler stalls, so a slow // handler never drops one. the null handler is the only quiet mode. export const showAllBehavior: NotificationBehavior = { shouldShowBanner: true, shouldShowList: true, shouldPlaySound: true, shouldSetBadge: true, } export const suppressBehavior: NotificationBehavior = { shouldShowBanner: false, shouldShowList: false, shouldPlaySound: false, shouldSetBadge: false, } export function normalizeBehavior(input: unknown): NotificationBehavior { if (!input || typeof input !== 'object') return { ...suppressBehavior } return { shouldShowBanner: 'shouldShowBanner' in input && input.shouldShowBanner === true, shouldShowList: 'shouldShowList' in input && input.shouldShowList === true, shouldPlaySound: 'shouldPlaySound' in input && input.shouldPlaySound === true, shouldSetBadge: 'shouldSetBadge' in input && input.shouldSetBadge === true, } } // pairs foreground arrivals with their presentation answers by request id. // each id settles exactly once; duplicates of an in-flight id are ignored. // native shows everything after 3s on its own, so js never times out: a // stalled handler resolves natively, and a late js answer finds no pending // record and stays a no-op. no react-native imports: unit-tested. export class ForegroundHandler { private handler: NotificationHandlerInput | null = null private nulled = false private pending = new Set<string>() constructor( private present: (requestId: string, behavior: NotificationBehavior) => void ) {} setHandler(handler: NotificationHandlerInput | null) { this.handler = handler this.nulled = handler === null } // returns false for a duplicate of an in-flight id; the caller skips // listener delivery for those. receive(requestId: string, notification: Notification): boolean { if (this.pending.has(requestId)) return false if (!this.handler) { // never set: show, like expo. explicitly nulled: stay quiet. this.present(requestId, { ...(this.nulled ? suppressBehavior : showAllBehavior) }) return true } const handler = this.handler this.pending.add(requestId) Promise.resolve() .then(() => handler.handleNotification(notification)) .then( (behavior) => this.settle(requestId, normalizeBehavior(behavior)), () => this.settle(requestId, showAllBehavior) ) return true } private settle(requestId: string, behavior: NotificationBehavior) { if (!this.pending.delete(requestId)) return this.present(requestId, { ...behavior }) } }