- 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
106 lines
4.3 KiB
JavaScript
106 lines
4.3 KiB
JavaScript
import { CodedError } from 'expo-modules-core';
|
|
import * as React from 'react';
|
|
import { StyleSheet, View } from 'react-native';
|
|
import createElement from 'react-native-web/dist/exports/createElement';
|
|
import CameraManager from './ExpoCameraManager.web';
|
|
import { CameraType, } from './legacy/Camera.types';
|
|
import { capture } from './web/WebCameraUtils';
|
|
import { PictureSizes } from './web/WebConstants';
|
|
import { useWebCameraStream } from './web/useWebCameraStream';
|
|
import { useWebQRScanner } from './web/useWebQRScanner';
|
|
const ExponentCamera = React.forwardRef(({ type, poster, ...props }, ref) => {
|
|
const video = React.useRef(null);
|
|
const native = useWebCameraStream(video, type, props, {
|
|
onCameraReady() {
|
|
if (props.onCameraReady) {
|
|
props.onCameraReady();
|
|
}
|
|
},
|
|
onMountError: props.onMountError,
|
|
});
|
|
const isQRScannerEnabled = React.useMemo(() => {
|
|
return !!(props.barCodeScannerSettings?.barCodeTypes?.includes('qr') && !!props.onBarCodeScanned);
|
|
}, [props.barCodeScannerSettings?.barCodeTypes, props.onBarCodeScanned]);
|
|
useWebQRScanner(video, {
|
|
interval: props.barCodeScannerSettings?.interval,
|
|
isEnabled: isQRScannerEnabled,
|
|
captureOptions: { scale: 1, isImageMirror: native.type === CameraType.front },
|
|
onScanned(event) {
|
|
if (props.onBarCodeScanned) {
|
|
props.onBarCodeScanned(event);
|
|
}
|
|
},
|
|
// onError: props.onMountError,
|
|
});
|
|
// const [pause, setPaused]
|
|
React.useImperativeHandle(ref, () => ({
|
|
async getAvailablePictureSizes(ratio) {
|
|
return PictureSizes;
|
|
},
|
|
async takePicture(options) {
|
|
if (!video.current || video.current?.readyState !== video.current?.HAVE_ENOUGH_DATA) {
|
|
throw new CodedError('ERR_CAMERA_NOT_READY', 'HTMLVideoElement does not have enough camera data to construct an image yet.');
|
|
}
|
|
const settings = native.mediaTrackSettings;
|
|
if (!settings) {
|
|
throw new CodedError('ERR_CAMERA_NOT_READY', 'MediaStream is not ready yet.');
|
|
}
|
|
return capture(video.current, settings, {
|
|
...options,
|
|
// This will always be defined, the option gets added to a queue in the upper-level. We should replace the original so it isn't called twice.
|
|
onPictureSaved(picture) {
|
|
if (options.onPictureSaved) {
|
|
options.onPictureSaved(picture);
|
|
}
|
|
if (props.onPictureSaved) {
|
|
props.onPictureSaved({ nativeEvent: { data: picture, id: -1 } });
|
|
}
|
|
},
|
|
});
|
|
},
|
|
async resumePreview() {
|
|
if (video.current) {
|
|
video.current.play();
|
|
}
|
|
},
|
|
async pausePreview() {
|
|
if (video.current) {
|
|
video.current.pause();
|
|
}
|
|
},
|
|
}), [native.mediaTrackSettings, props.onPictureSaved]);
|
|
// TODO(Bacon): Create a universal prop, on native the microphone is only used when recording videos.
|
|
// Because we don't support recording video in the browser we don't need the user to give microphone permissions.
|
|
const isMuted = true;
|
|
const style = React.useMemo(() => {
|
|
const isFrontFacingCamera = native.type === CameraManager.Type.front;
|
|
return [
|
|
StyleSheet.absoluteFill,
|
|
styles.video,
|
|
{
|
|
// Flip the camera
|
|
transform: isFrontFacingCamera ? [{ scaleX: -1 }] : undefined,
|
|
},
|
|
];
|
|
}, [native.type]);
|
|
return (<View pointerEvents="box-none" style={[styles.videoWrapper, props.style]}>
|
|
<Video autoPlay playsInline muted={isMuted} poster={poster}
|
|
// webkitPlaysinline
|
|
pointerEvents={props.pointerEvents} ref={video} style={style}/>
|
|
{props.children}
|
|
</View>);
|
|
});
|
|
export default ExponentCamera;
|
|
const Video = React.forwardRef((props, ref) => createElement('video', { ...props, ref }));
|
|
const styles = StyleSheet.create({
|
|
videoWrapper: {
|
|
flex: 1,
|
|
alignItems: 'stretch',
|
|
},
|
|
video: {
|
|
width: '100%',
|
|
height: '100%',
|
|
objectFit: 'cover',
|
|
},
|
|
});
|
|
//# sourceMappingURL=ExpoCamera.web.js.map
|