diff --git a/assets/images/camera-flip.svg b/assets/images/camera-flip.svg deleted file mode 100644 index 6d05251e0c77..000000000000 --- a/assets/images/camera-flip.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/jest/setup.ts b/jest/setup.ts index ea2a0d44086e..ee2b1702ba1e 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -375,10 +375,3 @@ jest.mock('@src/hooks/useDomainDocumentTitle', () => ({ __esModule: true, default: jest.fn(), })); - -jest.mock('react-native-vision-camera', () => ({ - Camera: 'Camera', - useCameraDevice: jest.fn(() => null), - useCameraFormat: jest.fn(() => null), - useCameraPermission: jest.fn(() => ({hasPermission: false, requestPermission: jest.fn()})), -})); diff --git a/src/CONST/index.ts b/src/CONST/index.ts index e13ba87a9aa9..4a9087ed3c21 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -1271,7 +1271,6 @@ const CONST = { SHUTTER_SIZE: 90, MAX_REPORT_PREVIEW_RECEIPTS: 3, FLASH_DELAY_MS: 2000, - PHOTO_ASPECT_RATIO: 4 / 3, }, RECEIPT_PREVIEW_TOP_BOTTOM_MARGIN: 120, REPORT: { diff --git a/src/components/AttachmentPicker/AttachmentCamera.tsx b/src/components/AttachmentPicker/AttachmentCamera.tsx deleted file mode 100644 index 080407d17691..000000000000 --- a/src/components/AttachmentPicker/AttachmentCamera.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import React, {useCallback, useEffect, useRef, useState} from 'react'; -import {Alert, Modal, View} from 'react-native'; -import {RESULTS} from 'react-native-permissions'; -import type {Camera, PhotoFile} from 'react-native-vision-camera'; -import {useCameraDevice, useCameraFormat, Camera as VisionCamera} from 'react-native-vision-camera'; -import ActivityIndicator from '@components/ActivityIndicator'; -import Button from '@components/Button'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Icon from '@components/Icon'; -import ImageSVG from '@components/ImageSVG'; -import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; -import Text from '@components/Text'; -import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; -import useLocalize from '@hooks/useLocalize'; -import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; -import {showCameraPermissionsAlert} from '@libs/fileDownload/FileUtils'; -import getPhotoSource from '@libs/fileDownload/getPhotoSource'; -import Log from '@libs/Log'; -import CameraPermission from '@pages/iou/request/step/IOURequestStepScan/CameraPermission'; -import CONST from '@src/CONST'; - -type CapturedPhoto = { - uri: string; - fileName: string; - type: string; - width: number; - height: number; -}; - -type AttachmentCameraProps = { - /** Whether the camera modal is visible */ - isVisible: boolean; - - /** Callback when a photo is captured */ - onCapture: (photos: CapturedPhoto[]) => void; - - /** Callback when the camera is closed without capturing */ - onClose: () => void; -}; - -function AttachmentCamera({isVisible, onCapture, onClose}: AttachmentCameraProps) { - const theme = useTheme(); - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - const {translate} = useLocalize(); - const insets = useSafeAreaInsets(); - - const lazyIcons = useMemoizedLazyExpensifyIcons(['Bolt', 'boltSlash', 'CameraFlip']); - const lazyIllustrations = useMemoizedLazyIllustrations(['Shutter', 'Hand']); - - const camera = useRef(null); - const [cameraPermissionStatus, setCameraPermissionStatus] = useState(null); - const isCapturing = useRef(false); - const [cameraPosition, setCameraPosition] = useState<'back' | 'front'>('back'); - - const device = useCameraDevice(cameraPosition, { - physicalDevices: ['wide-angle-camera', 'ultra-wide-angle-camera'], - }); - const format = useCameraFormat(device, [{photoAspectRatio: CONST.RECEIPT.PHOTO_ASPECT_RATIO}, {photoResolution: 'max'}]); - const cameraAspectRatio = format ? format.photoHeight / format.photoWidth : undefined; - const hasFlash = !!device?.hasFlash; - - // Request camera permissions when modal opens - useEffect(() => { - if (!isVisible) { - return; - } - - CameraPermission.getCameraPermissionStatus?.() - .then((status) => { - if (status === RESULTS.DENIED) { - return CameraPermission.requestCameraPermission?.().then(setCameraPermissionStatus); - } - setCameraPermissionStatus(status); - }) - .catch(() => setCameraPermissionStatus(RESULTS.UNAVAILABLE)); - }, [isVisible]); - - const [flash, setFlash] = useState(false); - - const askForPermissions = useCallback(() => { - CameraPermission.requestCameraPermission?.() - .then((status: string) => { - setCameraPermissionStatus(status); - if (status === RESULTS.BLOCKED) { - showCameraPermissionsAlert(translate); - } - }) - .catch(() => setCameraPermissionStatus(RESULTS.UNAVAILABLE)); - }, [translate]); - - const capturePhoto = useCallback(() => { - if (!camera.current || isCapturing.current) { - return; - } - - if (cameraPermissionStatus === RESULTS.DENIED || cameraPermissionStatus === RESULTS.BLOCKED) { - askForPermissions(); - return; - } - - isCapturing.current = true; - - camera.current - .takePhoto({ - flash: flash && hasFlash ? 'on' : 'off', - }) - .then((photo: PhotoFile) => { - const uri = getPhotoSource(photo.path); - const fileName = photo.path.split('/').pop() ?? `photo_${Date.now()}.jpg`; - - onCapture([ - { - uri, - fileName, - type: 'image/jpeg', - width: photo.width, - height: photo.height, - }, - ]); - }) - .catch((error: unknown) => { - Log.warn('AttachmentCamera: Error taking photo', {error}); - Alert.alert(translate('receipt.cameraErrorTitle'), translate('receipt.cameraErrorMessage')); - }) - .finally(() => { - isCapturing.current = false; - }); - }, [cameraPermissionStatus, flash, hasFlash, onCapture, translate, askForPermissions]); - - return ( - - - - {/* Camera viewfinder area */} - - {cameraPermissionStatus !== RESULTS.GRANTED && ( - - - {translate('receipt.takePhoto')} - {translate('receipt.cameraAccess')} -