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
text/typescript
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')
);
}