From 10227f1c35b9459364651888bede6f330424e787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Thu, 16 Jan 2020 14:28:30 +0100 Subject: [PATCH] [expo-notifications] Post-review fixes https://github.com/expo/expo/pull/6577#pullrequestreview-343656373 --- .../build/TokenEmitter.d.ts | 8 ++-- .../expo-notifications/build/TokenEmitter.js | 6 +-- .../build/TokenEmitter.js.map | 2 +- .../build/getDevicePushTokenAsync.web.js | 24 ++++++------ .../build/getDevicePushTokenAsync.web.js.map | 2 +- .../expo-notifications/src/TokenEmitter.ts | 8 ++-- .../src/getDevicePushTokenAsync.web.ts | 39 +++++++++++-------- 7 files changed, 48 insertions(+), 41 deletions(-) diff --git a/packages/expo-notifications/build/TokenEmitter.d.ts b/packages/expo-notifications/build/TokenEmitter.d.ts index cf71b2a43cecf..bd89fa3dae624 100644 --- a/packages/expo-notifications/build/TokenEmitter.d.ts +++ b/packages/expo-notifications/build/TokenEmitter.d.ts @@ -1,6 +1,6 @@ import { Subscription } from '@unimodules/core'; import { DevicePushToken } from './getDevicePushTokenAsync'; -export declare type TokenListener = (token: DevicePushToken) => void; -export declare function addTokenListener(listener: TokenListener): Subscription; -export declare function removeTokenSubscription(subscription: Subscription): void; -export declare function removeAllTokenListeners(): void; +export declare type PushTokenListener = (token: DevicePushToken) => void; +export declare function addPushTokenListener(listener: PushTokenListener): Subscription; +export declare function removePushTokenSubscription(subscription: Subscription): void; +export declare function removeAllPushTokenListeners(): void; diff --git a/packages/expo-notifications/build/TokenEmitter.js b/packages/expo-notifications/build/TokenEmitter.js index 6765d45a7967e..dde8a7600f351 100644 --- a/packages/expo-notifications/build/TokenEmitter.js +++ b/packages/expo-notifications/build/TokenEmitter.js @@ -3,16 +3,16 @@ import PushTokenManager from './PushTokenManager'; // Web uses SyntheticEventEmitter const tokenEmitter = new EventEmitter(PushTokenManager); const newTokenEventName = 'onDevicePushToken'; -export function addTokenListener(listener) { +export function addPushTokenListener(listener) { const wrappingListener = ({ devicePushToken }) => // @ts-ignore: TS can't decide what Platform.OS is. listener({ data: devicePushToken, type: Platform.OS }); return tokenEmitter.addListener(newTokenEventName, wrappingListener); } -export function removeTokenSubscription(subscription) { +export function removePushTokenSubscription(subscription) { tokenEmitter.removeSubscription(subscription); } -export function removeAllTokenListeners() { +export function removeAllPushTokenListeners() { tokenEmitter.removeAllListeners(newTokenEventName); } //# sourceMappingURL=TokenEmitter.js.map \ No newline at end of file diff --git a/packages/expo-notifications/build/TokenEmitter.js.map b/packages/expo-notifications/build/TokenEmitter.js.map index 8b812c8579290..d396c9da90600 100644 --- a/packages/expo-notifications/build/TokenEmitter.js.map +++ b/packages/expo-notifications/build/TokenEmitter.js.map @@ -1 +1 @@ -{"version":3,"file":"TokenEmitter.js","sourceRoot":"","sources":["../src/TokenEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAIlD,iCAAiC;AACjC,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,gBAAgB,CAAC,CAAC;AACxD,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAE9C,MAAM,UAAU,gBAAgB,CAAC,QAAuB;IACtD,MAAM,gBAAgB,GAAG,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE;IAC/C,mDAAmD;IACnD,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO,YAAY,CAAC,WAAW,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,YAA0B;IAChE,YAAY,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,YAAY,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import { EventEmitter, Subscription, Platform } from '@unimodules/core';\nimport { DevicePushToken } from './getDevicePushTokenAsync';\nimport PushTokenManager from './PushTokenManager';\n\nexport type TokenListener = (token: DevicePushToken) => void;\n\n// Web uses SyntheticEventEmitter\nconst tokenEmitter = new EventEmitter(PushTokenManager);\nconst newTokenEventName = 'onDevicePushToken';\n\nexport function addTokenListener(listener: TokenListener): Subscription {\n const wrappingListener = ({ devicePushToken }) =>\n // @ts-ignore: TS can't decide what Platform.OS is.\n listener({ data: devicePushToken, type: Platform.OS });\n return tokenEmitter.addListener(newTokenEventName, wrappingListener);\n}\n\nexport function removeTokenSubscription(subscription: Subscription) {\n tokenEmitter.removeSubscription(subscription);\n}\n\nexport function removeAllTokenListeners() {\n tokenEmitter.removeAllListeners(newTokenEventName);\n}\n"]} \ No newline at end of file +{"version":3,"file":"TokenEmitter.js","sourceRoot":"","sources":["../src/TokenEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAIlD,iCAAiC;AACjC,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,gBAAgB,CAAC,CAAC;AACxD,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAE9C,MAAM,UAAU,oBAAoB,CAAC,QAA2B;IAC9D,MAAM,gBAAgB,GAAG,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE;IAC/C,mDAAmD;IACnD,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO,YAAY,CAAC,WAAW,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,YAA0B;IACpE,YAAY,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,YAAY,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import { EventEmitter, Subscription, Platform } from '@unimodules/core';\nimport { DevicePushToken } from './getDevicePushTokenAsync';\nimport PushTokenManager from './PushTokenManager';\n\nexport type PushTokenListener = (token: DevicePushToken) => void;\n\n// Web uses SyntheticEventEmitter\nconst tokenEmitter = new EventEmitter(PushTokenManager);\nconst newTokenEventName = 'onDevicePushToken';\n\nexport function addPushTokenListener(listener: PushTokenListener): Subscription {\n const wrappingListener = ({ devicePushToken }) =>\n // @ts-ignore: TS can't decide what Platform.OS is.\n listener({ data: devicePushToken, type: Platform.OS });\n return tokenEmitter.addListener(newTokenEventName, wrappingListener);\n}\n\nexport function removePushTokenSubscription(subscription: Subscription) {\n tokenEmitter.removeSubscription(subscription);\n}\n\nexport function removeAllPushTokenListeners() {\n tokenEmitter.removeAllListeners(newTokenEventName);\n}\n"]} \ No newline at end of file diff --git a/packages/expo-notifications/build/getDevicePushTokenAsync.web.js b/packages/expo-notifications/build/getDevicePushTokenAsync.web.js index d2fb49ac698d0..7b092e085935d 100644 --- a/packages/expo-notifications/build/getDevicePushTokenAsync.web.js +++ b/packages/expo-notifications/build/getDevicePushTokenAsync.web.js @@ -1,27 +1,27 @@ import { CodedError, Platform, SyntheticPlatformEmitter } from '@unimodules/core'; import Constants from 'expo-constants'; export default async function getDevicePushTokenAsync() { - const data = await _subscribeUserToPushAsync(); + const data = await _subscribeDeviceToPushNotificationsAsync(); SyntheticPlatformEmitter.emit('onDevicePushToken', { devicePushToken: data }); return { type: Platform.OS, data }; } function guardPermission() { if (!('Notification' in window)) { - throw new Error('The Notification API is not available on this device.'); + throw new CodedError('ERR_UNAVAILABLE', 'The Web Notifications API is not available on this device.'); } if (!navigator.serviceWorker) { - throw new Error('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.'); + 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.'); } if (Notification.permission !== 'granted') { - throw new Error('Cannot use Notifications without permissions. Please request permissions with `expo-permissions`'); + throw new CodedError('ERR_NOTIFICATIONS_PERMISSION_DENIED', `Cannot use web notifications without permissions granted. Request permissions with "expo-permissions".`); } } -async function _subscribeUserToPushAsync() { - if (!Constants.manifest.notification || !Constants.manifest.notification.vapidPublicKey) { - throw new CodedError('E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG', 'You must provide `notification.vapidPublicKey` in `app.json` to use push notifications on web. Learn more: https://docs.expo.io/versions/latest/guides/using-vapid/.'); +async function _subscribeDeviceToPushNotificationsAsync() { + if (!Constants.manifest.notification?.vapidPublicKey) { + 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.io/versions/latest/guides/using-vapid/.'); } - if (!Constants.manifest.notification || !Constants.manifest.notification.serviceWorkerPath) { - throw new CodedError('E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG', 'You must provide `notification.serviceWorkerPath` in `app.json` to use push notifications on web. Please provide path to the service worker that will handle notifications.'); + if (!Constants.manifest.notification?.serviceWorkerPath) { + 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.'); } guardPermission(); let registration = null; @@ -29,11 +29,11 @@ async function _subscribeUserToPushAsync() { registration = await navigator.serviceWorker.register(Constants.manifest.notification.serviceWorkerPath); } catch (error) { - throw new Error(`Notifications might not be working because the service worker (${Constants.manifest.notification.serviceWorkerPath}) couldn't be registered: ${error}`); + throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', `Could not register this device for push notifications because the service worker (${Constants.manifest.notification.serviceWorkerPath}) could not be registered: ${error}`); } await navigator.serviceWorker.ready; if (!registration.active) { - throw new Error('Notifications might not be working because the service worker API is not active.'); + throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', 'Could not register this device for push notifications because the service worker is not active.'); } const subscribeOptions = { userVisibleOnly: true, @@ -44,7 +44,7 @@ async function _subscribeUserToPushAsync() { pushSubscription = await registration.pushManager.subscribe(subscribeOptions); } catch (error) { - throw new CodedError('E_NOTIFICATIONS_PUSH_WEB_TOKEN_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with the browser endpoint. (' + + throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with the browser endpoint. (' + error + ')'); } diff --git a/packages/expo-notifications/build/getDevicePushTokenAsync.web.js.map b/packages/expo-notifications/build/getDevicePushTokenAsync.web.js.map index f9f7ca0e7ef53..821336c74e941 100644 --- a/packages/expo-notifications/build/getDevicePushTokenAsync.web.js.map +++ b/packages/expo-notifications/build/getDevicePushTokenAsync.web.js.map @@ -1 +1 @@ -{"version":3,"file":"getDevicePushTokenAsync.web.js","sourceRoot":"","sources":["../src/getDevicePushTokenAsync.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { CodedError, Platform, SyntheticPlatformEmitter } from '@unimodules/core';\nimport Constants from 'expo-constants';\n\nimport { DevicePushToken } from './getDevicePushTokenAsync';\n\nexport default async function getDevicePushTokenAsync(): Promise {\n const data = await _subscribeUserToPushAsync();\n SyntheticPlatformEmitter.emit('onDevicePushToken', { devicePushToken: data });\n return { type: Platform.OS, data };\n}\n\nfunction guardPermission() {\n if (!('Notification' in window)) {\n throw new Error('The Notification API is not available on this device.');\n }\n if (!navigator.serviceWorker) {\n throw new Error(\n '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.'\n );\n }\n if (Notification.permission !== 'granted') {\n throw new Error(\n 'Cannot use Notifications without permissions. Please request permissions with `expo-permissions`'\n );\n }\n}\n\nasync function _subscribeUserToPushAsync(): Promise {\n if (!Constants.manifest.notification || !Constants.manifest.notification.vapidPublicKey) {\n throw new CodedError(\n 'E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG',\n 'You must provide `notification.vapidPublicKey` in `app.json` to use push notifications on web. Learn more: https://docs.expo.io/versions/latest/guides/using-vapid/.'\n );\n }\n if (!Constants.manifest.notification || !Constants.manifest.notification.serviceWorkerPath) {\n throw new CodedError(\n 'E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG',\n 'You must provide `notification.serviceWorkerPath` in `app.json` to use push notifications on web. Please provide path to the service worker that will handle notifications.'\n );\n }\n guardPermission();\n\n let registration: ServiceWorkerRegistration | null = null;\n try {\n registration = await navigator.serviceWorker.register(\n Constants.manifest.notification.serviceWorkerPath\n );\n } catch (error) {\n throw new Error(\n `Notifications might not be working because the service worker (${Constants.manifest.notification.serviceWorkerPath}) couldn't be registered: ${error}`\n );\n }\n await navigator.serviceWorker.ready;\n\n if (!registration.active) {\n throw new Error(\n 'Notifications might not be working because the service worker API is not active.'\n );\n }\n\n const subscribeOptions = {\n userVisibleOnly: true,\n applicationServerKey: _urlBase64ToUint8Array(Constants.manifest.notification.vapidPublicKey),\n };\n let pushSubscription: PushSubscription | null = null;\n try {\n pushSubscription = await registration.pushManager.subscribe(subscribeOptions);\n } catch (error) {\n throw new CodedError(\n 'E_NOTIFICATIONS_PUSH_WEB_TOKEN_REGISTRATION_FAILED',\n 'The device was unable to register for remote notifications with the browser endpoint. (' +\n error +\n ')'\n );\n }\n const pushSubscriptionJson = pushSubscription.toJSON();\n\n const subscriptionObject = {\n endpoint: pushSubscriptionJson.endpoint,\n keys: {\n p256dh: pushSubscriptionJson.keys!.p256dh,\n auth: pushSubscriptionJson.keys!.auth,\n },\n };\n\n // Store notification icon string in service worker.\n // This message is received by `/expo-service-worker.js`.\n // We wrap it with `fromExpoWebClient` to make sure other message\n // will not override content such as `notificationIcon`.\n // https://stackoverflow.com/a/35729334/2603230\n let notificationIcon = (Constants.manifest.notification || {}).icon;\n await registration.active.postMessage(\n JSON.stringify({ fromExpoWebClient: { notificationIcon } })\n );\n\n return subscriptionObject;\n}\n\n// https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey\nfunction _urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n\n const rawData = window.atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n"]} \ No newline at end of file +{"version":3,"file":"getDevicePushTokenAsync.web.js","sourceRoot":"","sources":["../src/getDevicePushTokenAsync.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { CodedError, Platform, SyntheticPlatformEmitter } from '@unimodules/core';\nimport Constants from 'expo-constants';\n\nimport { DevicePushToken } from './getDevicePushTokenAsync';\n\nexport default async function getDevicePushTokenAsync(): Promise {\n const data = await _subscribeDeviceToPushNotificationsAsync();\n SyntheticPlatformEmitter.emit('onDevicePushToken', { devicePushToken: data });\n return { type: Platform.OS, data };\n}\n\nfunction guardPermission() {\n if (!('Notification' in window)) {\n throw new CodedError(\n 'ERR_UNAVAILABLE',\n 'The Web Notifications API is not available on this device.'\n );\n }\n if (!navigator.serviceWorker) {\n throw new CodedError(\n 'ERR_UNAVAILABLE',\n '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.'\n );\n }\n if (Notification.permission !== 'granted') {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_PERMISSION_DENIED',\n `Cannot use web notifications without permissions granted. Request permissions with \"expo-permissions\".`\n );\n }\n}\n\nasync function _subscribeDeviceToPushNotificationsAsync(): Promise {\n if (!Constants.manifest.notification?.vapidPublicKey) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG',\n 'You must provide `notification.vapidPublicKey` in `app.json` to use push notifications on web. Learn more: https://docs.expo.io/versions/latest/guides/using-vapid/.'\n );\n }\n if (!Constants.manifest.notification?.serviceWorkerPath) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_PUSH_MISSING_CONFIGURATION',\n '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.'\n );\n }\n guardPermission();\n\n let registration: ServiceWorkerRegistration | null = null;\n try {\n registration = await navigator.serviceWorker.register(\n Constants.manifest.notification.serviceWorkerPath\n );\n } catch (error) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED',\n `Could not register this device for push notifications because the service worker (${Constants.manifest.notification.serviceWorkerPath}) could not be registered: ${error}`\n );\n }\n await navigator.serviceWorker.ready;\n\n if (!registration.active) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED',\n 'Could not register this device for push notifications because the service worker is not active.'\n );\n }\n\n const subscribeOptions = {\n userVisibleOnly: true,\n applicationServerKey: _urlBase64ToUint8Array(Constants.manifest.notification.vapidPublicKey),\n };\n let pushSubscription: PushSubscription | null = null;\n try {\n pushSubscription = await registration.pushManager.subscribe(subscribeOptions);\n } catch (error) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED',\n 'The device was unable to register for remote notifications with the browser endpoint. (' +\n error +\n ')'\n );\n }\n const pushSubscriptionJson = pushSubscription.toJSON();\n\n const subscriptionObject = {\n endpoint: pushSubscriptionJson.endpoint,\n keys: {\n p256dh: pushSubscriptionJson.keys!.p256dh,\n auth: pushSubscriptionJson.keys!.auth,\n },\n };\n\n // Store notification icon string in service worker.\n // This message is received by `/expo-service-worker.js`.\n // We wrap it with `fromExpoWebClient` to make sure other message\n // will not override content such as `notificationIcon`.\n // https://stackoverflow.com/a/35729334/2603230\n let notificationIcon = (Constants.manifest.notification || {}).icon;\n await registration.active.postMessage(\n JSON.stringify({ fromExpoWebClient: { notificationIcon } })\n );\n\n return subscriptionObject;\n}\n\n// https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey\nfunction _urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n\n const rawData = window.atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n"]} \ No newline at end of file diff --git a/packages/expo-notifications/src/TokenEmitter.ts b/packages/expo-notifications/src/TokenEmitter.ts index 7a2aeb9a6f5fa..fde55a8c42332 100644 --- a/packages/expo-notifications/src/TokenEmitter.ts +++ b/packages/expo-notifications/src/TokenEmitter.ts @@ -2,23 +2,23 @@ import { EventEmitter, Subscription, Platform } from '@unimodules/core'; import { DevicePushToken } from './getDevicePushTokenAsync'; import PushTokenManager from './PushTokenManager'; -export type TokenListener = (token: DevicePushToken) => void; +export type PushTokenListener = (token: DevicePushToken) => void; // Web uses SyntheticEventEmitter const tokenEmitter = new EventEmitter(PushTokenManager); const newTokenEventName = 'onDevicePushToken'; -export function addTokenListener(listener: TokenListener): Subscription { +export function addPushTokenListener(listener: PushTokenListener): Subscription { const wrappingListener = ({ devicePushToken }) => // @ts-ignore: TS can't decide what Platform.OS is. listener({ data: devicePushToken, type: Platform.OS }); return tokenEmitter.addListener(newTokenEventName, wrappingListener); } -export function removeTokenSubscription(subscription: Subscription) { +export function removePushTokenSubscription(subscription: Subscription) { tokenEmitter.removeSubscription(subscription); } -export function removeAllTokenListeners() { +export function removeAllPushTokenListeners() { tokenEmitter.removeAllListeners(newTokenEventName); } diff --git a/packages/expo-notifications/src/getDevicePushTokenAsync.web.ts b/packages/expo-notifications/src/getDevicePushTokenAsync.web.ts index b5014531adf8b..482fa371c36df 100644 --- a/packages/expo-notifications/src/getDevicePushTokenAsync.web.ts +++ b/packages/expo-notifications/src/getDevicePushTokenAsync.web.ts @@ -4,38 +4,43 @@ import Constants from 'expo-constants'; import { DevicePushToken } from './getDevicePushTokenAsync'; export default async function getDevicePushTokenAsync(): Promise { - const data = await _subscribeUserToPushAsync(); + const data = await _subscribeDeviceToPushNotificationsAsync(); SyntheticPlatformEmitter.emit('onDevicePushToken', { devicePushToken: data }); return { type: Platform.OS, data }; } function guardPermission() { if (!('Notification' in window)) { - throw new Error('The Notification API is not available on this device.'); + throw new CodedError( + 'ERR_UNAVAILABLE', + 'The Web Notifications API is not available on this device.' + ); } if (!navigator.serviceWorker) { - throw new Error( + 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.' ); } if (Notification.permission !== 'granted') { - throw new Error( - 'Cannot use Notifications without permissions. Please request permissions with `expo-permissions`' + throw new CodedError( + 'ERR_NOTIFICATIONS_PERMISSION_DENIED', + `Cannot use web notifications without permissions granted. Request permissions with "expo-permissions".` ); } } -async function _subscribeUserToPushAsync(): Promise { - if (!Constants.manifest.notification || !Constants.manifest.notification.vapidPublicKey) { +async function _subscribeDeviceToPushNotificationsAsync(): Promise { + if (!Constants.manifest.notification?.vapidPublicKey) { throw new CodedError( - 'E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG', + '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.io/versions/latest/guides/using-vapid/.' ); } - if (!Constants.manifest.notification || !Constants.manifest.notification.serviceWorkerPath) { + if (!Constants.manifest.notification?.serviceWorkerPath) { throw new CodedError( - 'E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG', - 'You must provide `notification.serviceWorkerPath` in `app.json` to use push notifications on web. Please provide path to the service worker that will handle notifications.' + '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.' ); } guardPermission(); @@ -46,15 +51,17 @@ async function _subscribeUserToPushAsync(): Promise { Constants.manifest.notification.serviceWorkerPath ); } catch (error) { - throw new Error( - `Notifications might not be working because the service worker (${Constants.manifest.notification.serviceWorkerPath}) couldn't be registered: ${error}` + throw new CodedError( + 'ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', + `Could not register this device for push notifications because the service worker (${Constants.manifest.notification.serviceWorkerPath}) could not be registered: ${error}` ); } await navigator.serviceWorker.ready; if (!registration.active) { - throw new Error( - 'Notifications might not be working because the service worker API is not active.' + throw new CodedError( + 'ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', + 'Could not register this device for push notifications because the service worker is not active.' ); } @@ -67,7 +74,7 @@ async function _subscribeUserToPushAsync(): Promise { pushSubscription = await registration.pushManager.subscribe(subscribeOptions); } catch (error) { throw new CodedError( - 'E_NOTIFICATIONS_PUSH_WEB_TOKEN_REGISTRATION_FAILED', + 'ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with the browser endpoint. (' + error + ')'