1 | import { CodedError } from '@unimodules/core';
|
2 | import Constants from 'expo-constants';
|
3 | import { Platform } from 'react-native';
|
4 | export function guardPermission() {
|
5 | if (!('Notification' in window)) {
|
6 | throw new Error('The Notification API is not available on this device.');
|
7 | }
|
8 | if (!navigator.serviceWorker) {
|
9 | 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.');
|
10 | }
|
11 | if (Notification.permission !== 'granted') {
|
12 | throw new Error('Cannot use Notifications without permissions. Please request permissions with `expo-permissions`');
|
13 | }
|
14 | }
|
15 | export async function getExponentPushTokenAsync() {
|
16 | if (!Constants.manifest.owner || !Constants.manifest.slug) {
|
17 | throw new CodedError('E_NOTIFICATIONS_PUSH_WEB_MISSING_CONFIG', 'You must provide `owner` and `slug` in `app.json` to use push notifications on web. Learn more: https://docs.expo.io/versions/latest/guides/using-vapid/.');
|
18 | }
|
19 | const data = await _subscribeUserToPushAsync();
|
20 | const experienceId = `@${Constants.manifest.owner}/${Constants.manifest.slug}`;
|
21 | const tokenArguments = {
|
22 | deviceId: Constants.installationId,
|
23 | experienceId,
|
24 |
|
25 | appId: experienceId,
|
26 | deviceToken: JSON.stringify(data),
|
27 | type: 'web',
|
28 | };
|
29 | const response = await fetch('https://exp.host/--/api/v2/push/getExpoPushToken', {
|
30 | method: 'POST',
|
31 | body: JSON.stringify(tokenArguments),
|
32 | })
|
33 | .then(response => {
|
34 | if (!response.ok) {
|
35 | throw new Error(response.statusText);
|
36 | }
|
37 | return response;
|
38 | })
|
39 | .then(response => response.json())
|
40 | .catch(error => {
|
41 | throw new CodedError('E_NOTIFICATIONS_TOKEN_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with Expo. (' + error + ')');
|
42 | });
|
43 | return response.data.expoPushToken;
|
44 | }
|
45 | export async function getDevicePushTokenAsync() {
|
46 | const data = await _subscribeUserToPushAsync();
|
47 | return { type: Platform.OS, data };
|
48 | }
|
49 | async function _subscribeUserToPushAsync() {
|
50 | if (!Constants.manifest.notification || !Constants.manifest.notification.vapidPublicKey) {
|
51 | 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/.');
|
52 | }
|
53 | guardPermission();
|
54 | const registration = await navigator.serviceWorker.register('/expo-service-worker.js');
|
55 | await navigator.serviceWorker.ready;
|
56 | if (!registration.active) {
|
57 | throw new Error('Notifications might not be working because the service worker API is not active.');
|
58 | }
|
59 | const subscribeOptions = {
|
60 | userVisibleOnly: true,
|
61 | applicationServerKey: _urlBase64ToUint8Array(Constants.manifest.notification.vapidPublicKey),
|
62 | };
|
63 | const pushSubscription = await registration.pushManager
|
64 | .subscribe(subscribeOptions)
|
65 | .catch(error => {
|
66 | throw new CodedError('E_NOTIFICATIONS_PUSH_WEB_TOKEN_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with the browser endpoint. (' +
|
67 | error +
|
68 | ')');
|
69 | });
|
70 | const pushSubscriptionJson = pushSubscription.toJSON();
|
71 | const subscriptionObject = {
|
72 | endpoint: pushSubscriptionJson.endpoint,
|
73 | keys: {
|
74 | p256dh: pushSubscriptionJson.keys.p256dh,
|
75 | auth: pushSubscriptionJson.keys.auth,
|
76 | },
|
77 | };
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | const notificationIcon = (Constants.manifest.notification || {}).icon;
|
84 | await registration.active.postMessage(JSON.stringify({ fromExpoWebClient: { notificationIcon } }));
|
85 | return subscriptionObject;
|
86 | }
|
87 |
|
88 | function _urlBase64ToUint8Array(base64String) {
|
89 | const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
90 | const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
|
91 | const rawData = window.atob(base64);
|
92 | const outputArray = new Uint8Array(rawData.length);
|
93 | for (let i = 0; i < rawData.length; ++i) {
|
94 | outputArray[i] = rawData.charCodeAt(i);
|
95 | }
|
96 | return outputArray;
|
97 | }
|
98 |
|
\ | No newline at end of file |