- 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
83 lines
4.3 KiB
JavaScript
83 lines
4.3 KiB
JavaScript
import Constants from 'expo-constants';
|
|
import { CodedError, DeviceEventEmitter, Platform } from 'expo-modules-core';
|
|
export default async function getDevicePushTokenAsync() {
|
|
const data = await _subscribeDeviceToPushNotificationsAsync();
|
|
DeviceEventEmitter.emit('onDevicePushToken', { devicePushToken: data });
|
|
return { type: Platform.OS, data };
|
|
}
|
|
function guardPermission() {
|
|
if (!('Notification' in window)) {
|
|
throw new CodedError('ERR_UNAVAILABLE', 'The Web Notifications API is not available on this device.');
|
|
}
|
|
if (!navigator.serviceWorker) {
|
|
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 CodedError('ERR_NOTIFICATIONS_PERMISSION_DENIED', `Cannot use web notifications without permissions granted. Request permissions with "expo-permissions".`);
|
|
}
|
|
}
|
|
async function _subscribeDeviceToPushNotificationsAsync() {
|
|
// @ts-expect-error: TODO: not on the schema
|
|
const vapidPublicKey = Constants.expoConfig?.notification?.vapidPublicKey;
|
|
if (!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.dev/versions/latest/guides/using-vapid/.');
|
|
}
|
|
// @ts-expect-error: TODO: not on the schema
|
|
const serviceWorkerPath = Constants.expoConfig?.notification?.serviceWorkerPath;
|
|
if (!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;
|
|
try {
|
|
registration = await navigator.serviceWorker.register(serviceWorkerPath);
|
|
}
|
|
catch (error) {
|
|
throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', `Could not register this device for push notifications because the service worker (${serviceWorkerPath}) could not be registered: ${error}`);
|
|
}
|
|
await navigator.serviceWorker.ready;
|
|
if (!registration.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,
|
|
applicationServerKey: _urlBase64ToUint8Array(vapidPublicKey),
|
|
};
|
|
let pushSubscription = null;
|
|
try {
|
|
pushSubscription = await registration.pushManager.subscribe(subscribeOptions);
|
|
}
|
|
catch (error) {
|
|
throw new CodedError('ERR_NOTIFICATIONS_PUSH_REGISTRATION_FAILED', 'The device was unable to register for remote notifications with the browser endpoint. (' +
|
|
error +
|
|
')');
|
|
}
|
|
const pushSubscriptionJson = pushSubscription.toJSON();
|
|
const subscriptionObject = {
|
|
endpoint: pushSubscriptionJson.endpoint,
|
|
keys: {
|
|
p256dh: pushSubscriptionJson.keys.p256dh,
|
|
auth: pushSubscriptionJson.keys.auth,
|
|
},
|
|
};
|
|
// Store notification icon string in service worker.
|
|
// This message is received by `/expo-service-worker.js`.
|
|
// We wrap it with `fromExpoWebClient` to make sure other message
|
|
// will not override content such as `notificationIcon`.
|
|
// https://stackoverflow.com/a/35729334/2603230
|
|
const notificationIcon = (Constants.expoConfig?.notification ?? {}).icon;
|
|
await registration.active.postMessage(JSON.stringify({ fromExpoWebClient: { notificationIcon } }));
|
|
return subscriptionObject;
|
|
}
|
|
// https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey
|
|
function _urlBase64ToUint8Array(base64String) {
|
|
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
|
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
|
|
const rawData = window.atob(base64);
|
|
const outputArray = new Uint8Array(rawData.length);
|
|
for (let i = 0; i < rawData.length; ++i) {
|
|
outputArray[i] = rawData.charCodeAt(i);
|
|
}
|
|
return outputArray;
|
|
}
|
|
//# sourceMappingURL=getDevicePushTokenAsync.web.js.map
|