UNPKG

expo-notifications

Version:

Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications.

122 lines (107 loc) 4.23 kB
import 'abort-controller/polyfill'; import { UnavailabilityError } from 'expo-modules-core'; import ServerRegistrationModule from './ServerRegistrationModule'; import { addPushTokenListener } from './TokenEmitter'; import { DevicePushToken } from './Tokens.types'; import getDevicePushTokenAsync from './getDevicePushTokenAsync'; import { updateDevicePushTokenAsync as updateDevicePushTokenAsyncWithSignal } from './utils/updateDevicePushTokenAsync'; let lastAbortController: AbortController | null = null; async function updatePushTokenAsync(token: DevicePushToken) { // Abort current update process lastAbortController?.abort(); lastAbortController = new AbortController(); return await updateDevicePushTokenAsyncWithSignal(lastAbortController.signal, token); } /** * Encapsulates device server registration data */ export type DevicePushTokenRegistration = { isEnabled: boolean; }; /** * @hidden - the comment is misleading and the purpose of the function needs to be reevaluated * * Sets the registration information so that the device push token gets pushed * to the given registration endpoint * @param enabled */ export async function setAutoServerRegistrationEnabledAsync(enabled: boolean) { // We are overwriting registration, so we shouldn't let // any pending request complete. lastAbortController?.abort(); if (!ServerRegistrationModule.setRegistrationInfoAsync) { throw new UnavailabilityError('ServerRegistrationModule', 'setRegistrationInfoAsync'); } await ServerRegistrationModule.setRegistrationInfoAsync( enabled ? JSON.stringify({ isEnabled: enabled }) : null ); } // note(Chmiela): This function is exported only for testing purposes. export async function __handlePersistedRegistrationInfoAsync( registrationInfo: string | null | undefined ) { if (!registrationInfo) { // No registration info, nothing to do return; } let registration: DevicePushTokenRegistration | null = null; try { registration = JSON.parse(registrationInfo); } catch (e) { console.warn( '[expo-notifications] Error encountered while fetching registration information for auto token updates.', e ); } if (!registration?.isEnabled) { // Registration is invalid or not enabled, nothing more to do return; } try { // Since the registration is enabled, fetching a "new" device token // shouldn't be a problem. const latestDevicePushToken = await getDevicePushTokenAsync(); await updatePushTokenAsync(latestDevicePushToken); } catch (e) { console.warn( '[expo-notifications] Error encountered while updating server registration with latest device push token.', e ); } } if (ServerRegistrationModule.getRegistrationInfoAsync) { // A global scope (to get all the updates) device push token // subscription, never cleared. addPushTokenListener(async (token) => { try { // Before updating the push token on server we always check if we should // Since modules can't change their method availability while running, we // can assert it's defined. const registrationInfo = await ServerRegistrationModule.getRegistrationInfoAsync!(); if (!registrationInfo) { // Registration is not enabled return; } const registration: DevicePushTokenRegistration | null = JSON.parse(registrationInfo); if (registration?.isEnabled) { // Dispatch an abortable task to update // registration with new token. await updatePushTokenAsync(token); } } catch (e) { console.warn( '[expo-notifications] Error encountered while updating server registration with latest device push token.', e ); } }); // Verify if persisted registration // has successfully uploaded last known // device push token. If not, retry. ServerRegistrationModule.getRegistrationInfoAsync().then(__handlePersistedRegistrationInfoAsync); } else { console.warn( `[expo-notifications] Error encountered while fetching auto-registration state, new tokens will not be automatically registered on server.`, new UnavailabilityError('ServerRegistrationModule', 'getRegistrationInfoAsync') ); }