- App.tsx: full navigation (Auth stack + Main tabs with 5 screens) - Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen - HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors - MapScreen: interactive map with layer toggles (6 layers) - MarketplaceScreen: categories (6), products (5), search - ChatScreen: AI chat with quick prompts (4), bot responses - ProfileScreen: user info, stats, menu (9 items), logout - AlertsScreen: alert list with severity, acknowledge - SensorsScreen: sensor list with type filters (6 types), search - ZonesScreen: zone cards with stats - SettingsScreen: language picker (FR/EN/ES/DE), privacy, about - Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n - Hooks: useSensors, useAlerts, useNotifications, useLocation - Components: Card, Button, LoadingSpinner, ErrorBoundary, Header - Services: iotService, notificationService (with axios API client) - Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN) - Theme: colors.ts with full design system (Blue Ocean palette) - Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
115 lines
4.6 KiB
JavaScript
115 lines
4.6 KiB
JavaScript
import { computeNextBackoffInterval } from '@ide/backoff';
|
|
import * as Application from 'expo-application';
|
|
import { CodedError, Platform, UnavailabilityError } from 'expo-modules-core';
|
|
import ServerRegistrationModule from '../ServerRegistrationModule';
|
|
const updateDevicePushTokenUrl = 'https://exp.host/--/api/v2/push/updateDeviceToken';
|
|
export async function updateDevicePushTokenAsync(signal, token) {
|
|
const doUpdateDevicePushTokenAsync = async (retry) => {
|
|
const [development, deviceId] = await Promise.all([
|
|
shouldUseDevelopmentNotificationService(),
|
|
getDeviceIdAsync(),
|
|
]);
|
|
const body = {
|
|
deviceId: deviceId.toLowerCase(),
|
|
development,
|
|
deviceToken: token.data,
|
|
appId: Application.applicationId,
|
|
type: getTypeOfToken(token),
|
|
};
|
|
try {
|
|
const response = await fetch(updateDevicePushTokenUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
body: JSON.stringify(body),
|
|
signal,
|
|
});
|
|
// Help debug erroring servers
|
|
if (!response.ok) {
|
|
console.debug('[expo-notifications] Error encountered while updating the device push token with the server:', await response.text());
|
|
}
|
|
// Retry if request failed
|
|
if (!response.ok) {
|
|
retry();
|
|
}
|
|
}
|
|
catch (e) {
|
|
// Error returned if the request is aborted should be an 'AbortError'. In
|
|
// React Native fetch is polyfilled using `whatwg-fetch` which:
|
|
// - creates `AbortError`s like this
|
|
// https://github.com/github/fetch/blob/75d9455d380f365701151f3ac85c5bda4bbbde76/fetch.js#L505
|
|
// - which creates exceptions like
|
|
// https://github.com/github/fetch/blob/75d9455d380f365701151f3ac85c5bda4bbbde76/fetch.js#L490-L494
|
|
if (e.name === 'AbortError') {
|
|
// We don't consider AbortError a failure, it's a sign somewhere else the
|
|
// request is expected to succeed and we don't need this one, so let's
|
|
// just return.
|
|
return;
|
|
}
|
|
console.warn('[expo-notifications] Error thrown while updating the device push token with the server:', e);
|
|
retry();
|
|
}
|
|
};
|
|
let shouldTry = true;
|
|
const retry = () => {
|
|
shouldTry = true;
|
|
};
|
|
let retriesCount = 0;
|
|
const initialBackoff = 500; // 0.5 s
|
|
const backoffOptions = {
|
|
maxBackoff: 2 * 60 * 1000, // 2 minutes
|
|
};
|
|
let nextBackoffInterval = computeNextBackoffInterval(initialBackoff, retriesCount, backoffOptions);
|
|
while (shouldTry && !signal.aborted) {
|
|
// Will be set to true by `retry` if it's called
|
|
shouldTry = false;
|
|
await doUpdateDevicePushTokenAsync(retry);
|
|
// Do not wait if we won't retry
|
|
if (shouldTry && !signal.aborted) {
|
|
nextBackoffInterval = computeNextBackoffInterval(initialBackoff, retriesCount, backoffOptions);
|
|
retriesCount += 1;
|
|
await new Promise((resolve) => setTimeout(resolve, nextBackoffInterval));
|
|
}
|
|
}
|
|
}
|
|
// Same as in getExpoPushTokenAsync
|
|
async function getDeviceIdAsync() {
|
|
try {
|
|
if (!ServerRegistrationModule.getInstallationIdAsync) {
|
|
throw new UnavailabilityError('ExpoServerRegistrationModule', 'getInstallationIdAsync');
|
|
}
|
|
return await ServerRegistrationModule.getInstallationIdAsync();
|
|
}
|
|
catch (e) {
|
|
throw new CodedError('ERR_NOTIFICATIONS_DEVICE_ID', `Could not fetch the installation ID of the application: ${e}.`);
|
|
}
|
|
}
|
|
// Same as in getExpoPushTokenAsync
|
|
function getTypeOfToken(devicePushToken) {
|
|
switch (devicePushToken.type) {
|
|
case 'ios':
|
|
return 'apns';
|
|
case 'android':
|
|
return 'fcm';
|
|
// This probably will error on server, but let's make this function future-safe.
|
|
default:
|
|
return devicePushToken.type;
|
|
}
|
|
}
|
|
// Same as in getExpoPushTokenAsync
|
|
async function shouldUseDevelopmentNotificationService() {
|
|
if (Platform.OS === 'ios') {
|
|
try {
|
|
const notificationServiceEnvironment = await Application.getIosPushNotificationServiceEnvironmentAsync();
|
|
if (notificationServiceEnvironment === 'development') {
|
|
return true;
|
|
}
|
|
}
|
|
catch {
|
|
// We can't do anything here, we'll fallback to false then.
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
//# sourceMappingURL=updateDevicePushTokenAsync.js.map
|