1import { computeNextBackoffInterval } from '@ide/backoff'; 2import * as Application from 'expo-application'; 3import { CodedError, Platform, UnavailabilityError } from 'expo-modules-core'; 4import ServerRegistrationModule from '../ServerRegistrationModule'; 5const updateDevicePushTokenUrl = 'https://exp.host/--/api/v2/push/updateDeviceToken'; 6export async function updateDevicePushTokenAsync(signal, token) { 7 const doUpdateDevicePushTokenAsync = async (retry) => { 8 const [development, deviceId] = await Promise.all([ 9 shouldUseDevelopmentNotificationService(), 10 getDeviceIdAsync(), 11 ]); 12 const body = { 13 deviceId: deviceId.toLowerCase(), 14 development, 15 deviceToken: token.data, 16 appId: Application.applicationId, 17 type: getTypeOfToken(token), 18 }; 19 try { 20 const response = await fetch(updateDevicePushTokenUrl, { 21 method: 'POST', 22 headers: { 23 'content-type': 'application/json', 24 }, 25 body: JSON.stringify(body), 26 signal, 27 }); 28 // Help debug erroring servers 29 if (!response.ok) { 30 console.debug('[expo-notifications] Error encountered while updating the device push token with the server:', await response.text()); 31 } 32 // Retry if request failed 33 if (!response.ok) { 34 retry(); 35 } 36 } 37 catch (e) { 38 // Error returned if the request is aborted should be an 'AbortError'. In 39 // React Native fetch is polyfilled using `whatwg-fetch` which: 40 // - creates `AbortError`s like this 41 // https://github.com/github/fetch/blob/75d9455d380f365701151f3ac85c5bda4bbbde76/fetch.js#L505 42 // - which creates exceptions like 43 // https://github.com/github/fetch/blob/75d9455d380f365701151f3ac85c5bda4bbbde76/fetch.js#L490-L494 44 if (e.name === 'AbortError') { 45 // We don't consider AbortError a failure, it's a sign somewhere else the 46 // request is expected to succeed and we don't need this one, so let's 47 // just return. 48 return; 49 } 50 console.warn('[expo-notifications] Error thrown while updating the device push token with the server:', e); 51 retry(); 52 } 53 }; 54 let shouldTry = true; 55 const retry = () => { 56 shouldTry = true; 57 }; 58 let retriesCount = 0; 59 const initialBackoff = 500; // 0.5 s 60 const backoffOptions = { 61 maxBackoff: 2 * 60 * 1000, // 2 minutes 62 }; 63 let nextBackoffInterval = computeNextBackoffInterval(initialBackoff, retriesCount, backoffOptions); 64 while (shouldTry && !signal.aborted) { 65 // Will be set to true by `retry` if it's called 66 shouldTry = false; 67 await doUpdateDevicePushTokenAsync(retry); 68 // Do not wait if we won't retry 69 if (shouldTry && !signal.aborted) { 70 nextBackoffInterval = computeNextBackoffInterval(initialBackoff, retriesCount, backoffOptions); 71 retriesCount += 1; 72 await new Promise((resolve) => setTimeout(resolve, nextBackoffInterval)); 73 } 74 } 75} 76// Same as in getExpoPushTokenAsync 77async function getDeviceIdAsync() { 78 try { 79 if (!ServerRegistrationModule.getInstallationIdAsync) { 80 throw new UnavailabilityError('ExpoServerRegistrationModule', 'getInstallationIdAsync'); 81 } 82 return await ServerRegistrationModule.getInstallationIdAsync(); 83 } 84 catch (e) { 85 throw new CodedError('ERR_NOTIFICATIONS_DEVICE_ID', `Could not fetch the installation ID of the application: ${e}.`); 86 } 87} 88// Same as in getExpoPushTokenAsync 89function getTypeOfToken(devicePushToken) { 90 switch (devicePushToken.type) { 91 case 'ios': 92 return 'apns'; 93 case 'android': 94 return 'fcm'; 95 // This probably will error on server, but let's make this function future-safe. 96 default: 97 return devicePushToken.type; 98 } 99} 100// Same as in getExpoPushTokenAsync 101async function shouldUseDevelopmentNotificationService() { 102 if (Platform.OS === 'ios') { 103 try { 104 const notificationServiceEnvironment = await Application.getIosPushNotificationServiceEnvironmentAsync(); 105 if (notificationServiceEnvironment === 'development') { 106 return true; 107 } 108 } 109 catch { 110 // We can't do anything here, we'll fallback to false then. 111 } 112 } 113 return false; 114} 115//# sourceMappingURL=updateDevicePushTokenAsync.js.map