1import Constants from 'expo-constants'; 2import { CodedError, Platform, SyntheticPlatformEmitter } from 'expo-modules-core'; 3export default async function getDevicePushTokenAsync() { 4 const data = await _subscribeDeviceToPushNotificationsAsync(); 5 SyntheticPlatformEmitter.emit('onDevicePushToken', { devicePushToken: data }); 6 return { type: Platform.OS, data }; 7} 8function guardPermission() { 9 if (!('Notification' in window)) { 10 throw new CodedError('ERR_UNAVAILABLE', 'The Web Notifications API is not available on this device.'); 11 } 12 if (!navigator.serviceWorker) { 13 throw new CodedError('ERR_UNAVAILABLE', 'Notifications cannot be used because the service worker API is not supported on this device. This might also happen because your web page does not support HTTPS.'); 14 } 15 if (Notification.permission !== 'granted') { 16 throw new CodedError('ERR_NOTIFICATIONS_PERMISSION_DENIED', `Cannot use web notifications without permissions granted. Request permissions with "expo-permissions".`); 17 } 18} 19async function _subscribeDeviceToPushNotificationsAsync() { 20 const vapidPublicKey = 21 // @ts-expect-error: TODO: not on the schema 22 Constants.manifest?.notification?.vapidPublicKey ?? 23 // @ts-expect-error: TODO: not on the schema 24 Constants.manifest2?.extra?.expoClient?.notification?.vapidPublicKey; 25 if (!vapidPublicKey) { 26 throw new CodedError('ERR_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG', 'You must provide `notification.vapidPublicKey` in `app.json` to use push notifications on web. Learn more: https://docs.expo.dev/versions/latest/guides/using-vapid/.'); 27 } 28 const serviceWorkerPath = 29 // @ts-expect-error: TODO: not on the schema 30 Constants.manifest?.notification.serviceWorkerPath ?? 31 // @ts-expect-error: TODO: not on the schema 32 Constants.manifest2?.extra?.expoClient?.notification?.serviceWorkerPath; 33 if (!serviceWorkerPath) { 34 throw new CodedError('ERR_NOTIFICATIONS_PUSH_MISSING_CONFIGURATION', 'You must specify `notification.serviceWorkerPath` in `app.json` to use push notifications on the web. Please provide the path to the service worker that will handle notifications.'); 35 } 36 guardPermission(); 37 let registration = null; 38 try { 39 registration = await navigator.serviceWorker.register(serviceWorkerPath); 40 } 41 catch (error) { 42 throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', `Could not register this device for push notifications because the service worker (${serviceWorkerPath}) could not be registered: ${error}`); 43 } 44 await navigator.serviceWorker.ready; 45 if (!registration.active) { 46 throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', 'Could not register this device for push notifications because the service worker is not active.'); 47 } 48 const subscribeOptions = { 49 userVisibleOnly: true, 50 applicationServerKey: _urlBase64ToUint8Array(vapidPublicKey), 51 }; 52 let pushSubscription = null; 53 try { 54 pushSubscription = await registration.pushManager.subscribe(subscribeOptions); 55 } 56 catch (error) { 57 throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with the browser endpoint. (' + 58 error + 59 ')'); 60 } 61 const pushSubscriptionJson = pushSubscription.toJSON(); 62 const subscriptionObject = { 63 endpoint: pushSubscriptionJson.endpoint, 64 keys: { 65 p256dh: pushSubscriptionJson.keys.p256dh, 66 auth: pushSubscriptionJson.keys.auth, 67 }, 68 }; 69 // Store notification icon string in service worker. 70 // This message is received by `/expo-service-worker.js`. 71 // We wrap it with `fromExpoWebClient` to make sure other message 72 // will not override content such as `notificationIcon`. 73 // https://stackoverflow.com/a/35729334/2603230 74 const notificationIcon = (Constants.manifest?.notification ?? 75 Constants.manifest2?.extra?.expoClient?.notification ?? 76 {}).icon; 77 await registration.active.postMessage(JSON.stringify({ fromExpoWebClient: { notificationIcon } })); 78 return subscriptionObject; 79} 80// https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey 81function _urlBase64ToUint8Array(base64String) { 82 const padding = '='.repeat((4 - (base64String.length % 4)) % 4); 83 const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); 84 const rawData = window.atob(base64); 85 const outputArray = new Uint8Array(rawData.length); 86 for (let i = 0; i < rawData.length; ++i) { 87 outputArray[i] = rawData.charCodeAt(i); 88 } 89 return outputArray; 90} 91//# sourceMappingURL=getDevicePushTokenAsync.web.js.map