- 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
120 lines
4.6 KiB
JavaScript
120 lines
4.6 KiB
JavaScript
/* eslint-env browser */
|
|
/**
|
|
* A web-only module for ponyfilling the UserMedia API.
|
|
*/
|
|
import { Platform } from 'expo-modules-core';
|
|
export const userMediaRequested = false;
|
|
export const mountedInstances = [];
|
|
async function requestLegacyUserMediaAsync(props) {
|
|
const optionalSource = (id) => ({ optional: [{ sourceId: id }] });
|
|
const constraintToSourceId = (constraint) => {
|
|
const { deviceId } = constraint;
|
|
if (typeof deviceId === 'string') {
|
|
return deviceId;
|
|
}
|
|
if (Array.isArray(deviceId) && deviceId.length > 0) {
|
|
return deviceId[0];
|
|
}
|
|
if (typeof deviceId === 'object' && deviceId.ideal) {
|
|
return deviceId.ideal;
|
|
}
|
|
return null;
|
|
};
|
|
const sources = await new Promise((resolve) =>
|
|
// @ts-ignore: https://caniuse.com/#search=getSources Chrome for Android (78) & Samsung Internet (10.1) use this
|
|
MediaStreamTrack.getSources((sources) => resolve(sources)));
|
|
let audioSource = null;
|
|
let videoSource = null;
|
|
sources.forEach((source) => {
|
|
if (source.kind === 'audio') {
|
|
audioSource = source.id;
|
|
}
|
|
else if (source.kind === 'video') {
|
|
videoSource = source.id;
|
|
}
|
|
});
|
|
const audioSourceId = constraintToSourceId(props.audioConstraints);
|
|
if (audioSourceId) {
|
|
audioSource = audioSourceId;
|
|
}
|
|
const videoSourceId = constraintToSourceId(props.videoConstraints);
|
|
if (videoSourceId) {
|
|
videoSource = videoSourceId;
|
|
}
|
|
return [optionalSource(audioSource), optionalSource(videoSource)];
|
|
}
|
|
async function sourceSelectedAsync(isMuted, audioConstraints, videoConstraints) {
|
|
const constraints = {
|
|
video: typeof videoConstraints !== 'undefined' ? videoConstraints : true,
|
|
};
|
|
if (!isMuted) {
|
|
constraints.audio = typeof audioConstraints !== 'undefined' ? audioConstraints : true;
|
|
}
|
|
return await getAnyUserMediaAsync(constraints);
|
|
}
|
|
export async function requestUserMediaAsync(props, isMuted = true) {
|
|
if (canGetUserMedia()) {
|
|
return await sourceSelectedAsync(isMuted, props.audio, props.video);
|
|
}
|
|
const [audio, video] = await requestLegacyUserMediaAsync(props);
|
|
return await sourceSelectedAsync(isMuted, audio, video);
|
|
}
|
|
export async function getAnyUserMediaAsync(constraints, ignoreConstraints = false) {
|
|
try {
|
|
return await getUserMediaAsync({
|
|
...constraints,
|
|
video: ignoreConstraints || constraints.video,
|
|
});
|
|
}
|
|
catch (error) {
|
|
if (!ignoreConstraints && error.name === 'ConstraintNotSatisfiedError') {
|
|
return await getAnyUserMediaAsync(constraints, true);
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
export async function getUserMediaAsync(constraints) {
|
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
return navigator.mediaDevices.getUserMedia(constraints);
|
|
}
|
|
const _getUserMedia = navigator['mozGetUserMedia'] || navigator['webkitGetUserMedia'] || navigator['msGetUserMedia'];
|
|
return new Promise((resolve, reject) => _getUserMedia.call(navigator, constraints, resolve, reject));
|
|
}
|
|
export function canGetUserMedia() {
|
|
return (
|
|
// SSR
|
|
Platform.isDOMAvailable &&
|
|
// Has any form of media API
|
|
!!((navigator.mediaDevices && navigator.mediaDevices.getUserMedia) ||
|
|
navigator['mozGetUserMedia'] ||
|
|
navigator['webkitGetUserMedia'] ||
|
|
navigator['msGetUserMedia']));
|
|
}
|
|
export async function isFrontCameraAvailableAsync(devices) {
|
|
return await supportsCameraType(['front', 'user', 'facetime'], 'user', devices);
|
|
}
|
|
export async function isBackCameraAvailableAsync(devices) {
|
|
return await supportsCameraType(['back', 'rear'], 'environment', devices);
|
|
}
|
|
async function supportsCameraType(labels, type, devices) {
|
|
if (!devices) {
|
|
if (!navigator.mediaDevices.enumerateDevices) {
|
|
return null;
|
|
}
|
|
devices = await navigator.mediaDevices.enumerateDevices();
|
|
}
|
|
const cameras = devices.filter((t) => t.kind === 'videoinput');
|
|
const [hasCamera] = cameras.filter((camera) => labels.some((label) => camera.label.toLowerCase().includes(label)));
|
|
const [isCapable] = cameras.filter((camera) => {
|
|
if (!('getCapabilities' in camera)) {
|
|
return null;
|
|
}
|
|
const capabilities = camera.getCapabilities();
|
|
if (!capabilities.facingMode) {
|
|
return null;
|
|
}
|
|
return capabilities.facingMode.find((_) => type);
|
|
});
|
|
return isCapable?.deviceId || hasCamera?.deviceId || null;
|
|
}
|
|
//# sourceMappingURL=WebUserMediaManager.js.map
|