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