From 91f94c7054ec73fa4f75a93165042756865710e5 Mon Sep 17 00:00:00 2001 From: codinggeek2023 Date: Mon, 12 Feb 2024 14:12:35 +0530 Subject: [PATCH 01/54] Migrate to Typescript --- ...eiptPage.js => EditRequestReceiptPage.tsx} | 23 ++-- .../{ReceiptDropUI.js => ReceiptDropUI.tsx} | 23 ++-- .../{index.android.js => index.android.tsx} | 0 .../{index.ios.js => index.ios.tsx} | 0 .../CameraPermission/{index.js => index.tsx} | 0 .../IOURequestStepScanProps.tsx | 9 ++ .../{index.native.js => index.native.tsx} | 14 +-- .../{index.js => index.tsx} | 37 ++----- .../{index.native.js => index.native.tsx} | 88 ++++++--------- .../{index.js => index.tsx} | 103 +++++++----------- 10 files changed, 118 insertions(+), 179 deletions(-) rename src/pages/{EditRequestReceiptPage.js => EditRequestReceiptPage.tsx} (75%) rename src/pages/iou/{ReceiptDropUI.js => ReceiptDropUI.tsx} (73%) rename src/pages/iou/request/step/IOURequestStepScan/CameraPermission/{index.android.js => index.android.tsx} (100%) rename src/pages/iou/request/step/IOURequestStepScan/CameraPermission/{index.ios.js => index.ios.tsx} (100%) rename src/pages/iou/request/step/IOURequestStepScan/CameraPermission/{index.js => index.tsx} (100%) create mode 100644 src/pages/iou/request/step/IOURequestStepScan/IOURequestStepScanProps.tsx rename src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/{index.native.js => index.native.tsx} (66%) rename src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/{index.js => index.tsx} (56%) rename src/pages/iou/request/step/IOURequestStepScan/{index.native.js => index.native.tsx} (84%) rename src/pages/iou/request/step/IOURequestStepScan/{index.js => index.tsx} (82%) diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.tsx similarity index 75% rename from src/pages/EditRequestReceiptPage.js rename to src/pages/EditRequestReceiptPage.tsx index 40fe64da7eed..3458b8577206 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React, {useState} from 'react'; import {View} from 'react-native'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; @@ -7,23 +6,16 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import type {StackScreenProps} from '@react-navigation/stack'; +import type SCREENS from '@src/SCREENS'; +import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; import IOURequestStepScan from './iou/request/step/IOURequestStepScan'; -const propTypes = { - /** React Navigation route */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, +type EditRequestReceiptPageProps = StackScreenProps - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, -}; - -function EditRequestReceiptPage({route}) { +function EditRequestReceiptPage({ + route, +}: EditRequestReceiptPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [isDraggingOver, setIsDraggingOver] = useState(false); @@ -50,7 +42,6 @@ function EditRequestReceiptPage({route}) { ); } -EditRequestReceiptPage.propTypes = propTypes; EditRequestReceiptPage.displayName = 'EditRequestReceiptPage'; export default EditRequestReceiptPage; diff --git a/src/pages/iou/ReceiptDropUI.js b/src/pages/iou/ReceiptDropUI.tsx similarity index 73% rename from src/pages/iou/ReceiptDropUI.js rename to src/pages/iou/ReceiptDropUI.tsx index 0f7226668a80..043458dad113 100644 --- a/src/pages/iou/ReceiptDropUI.js +++ b/src/pages/iou/ReceiptDropUI.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import ReceiptUpload from '@assets/images/receipt-upload.svg'; @@ -9,19 +8,15 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; -const propTypes = { - /** Callback to execute when a file is dropped. */ - onDrop: PropTypes.func.isRequired, - - /** Pixels the receipt image should be shifted down to match the non-drag view UI */ - receiptImageTopPosition: PropTypes.number, -}; - -const defaultProps = { - receiptImageTopPosition: 0, -}; +type ReceiptDropUIProps = { + onDrop: () => void; + receiptImageTopPosition: number; +} -function ReceiptDropUI({onDrop, receiptImageTopPosition}) { +function ReceiptDropUI({ + onDrop, + receiptImageTopPosition=0, +}: ReceiptDropUIProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); return ( @@ -43,7 +38,5 @@ function ReceiptDropUI({onDrop, receiptImageTopPosition}) { } ReceiptDropUI.displayName = 'ReceiptDropUI'; -ReceiptDropUI.propTypes = propTypes; -ReceiptDropUI.defaultProps = defaultProps; export default ReceiptDropUI; diff --git a/src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.android.js b/src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.android.tsx similarity index 100% rename from src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.android.js rename to src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.android.tsx diff --git a/src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.ios.js b/src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.ios.tsx similarity index 100% rename from src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.ios.js rename to src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.ios.tsx diff --git a/src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.js b/src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.tsx similarity index 100% rename from src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.js rename to src/pages/iou/request/step/IOURequestStepScan/CameraPermission/index.tsx diff --git a/src/pages/iou/request/step/IOURequestStepScan/IOURequestStepScanProps.tsx b/src/pages/iou/request/step/IOURequestStepScan/IOURequestStepScanProps.tsx new file mode 100644 index 000000000000..cb30251cc84c --- /dev/null +++ b/src/pages/iou/request/step/IOURequestStepScan/IOURequestStepScanProps.tsx @@ -0,0 +1,9 @@ +import type * as OnyxTypes from '@src/types/onyx'; + +type IOURequestStepPropTypes = { + report: OnyxTypes.Report; + + transaction: OnyxTypes.Transaction; +}; + +export default IOURequestStepPropTypes; diff --git a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.tsx similarity index 66% rename from src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js rename to src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.tsx index 65c17d3cb7ab..f132fe80d709 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.tsx @@ -1,15 +1,16 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import type {FC} from 'react'; import {Camera} from 'react-native-vision-camera'; +import type {CameraDevice} from 'react-native-vision-camera'; import useTabNavigatorFocus from '@hooks/useTabNavigatorFocus'; -const propTypes = { - /* The index of the tab that contains this camera */ - cameraTabIndex: PropTypes.number.isRequired, +type NavigationAwareCameraProps = { + cameraTabIndex: number; + device: CameraDevice; }; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, ...props}, ref) => { +const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, ...props}: NavigationAwareCameraProps, ref: React.Ref) => { const isCameraActive = useTabNavigatorFocus({tabIndex: cameraTabIndex}); return ( @@ -22,7 +23,6 @@ const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, ...props}, ref) ); }); -NavigationAwareCamera.propTypes = propTypes; -NavigationAwareCamera.displayName = 'NavigationAwareCamera'; +(NavigationAwareCamera as FC).displayName = 'NavigationAwareCamera'; export default NavigationAwareCamera; diff --git a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.js b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.tsx similarity index 56% rename from src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.js rename to src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.tsx index 10b16da13b6e..8f9945243a50 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.js +++ b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.tsx @@ -1,37 +1,24 @@ -import PropTypes from 'prop-types'; -import React, {useEffect, useRef} from 'react'; +import React, {forwardRef, useEffect, useRef} from 'react'; +import type {FC} from 'react'; import {View} from 'react-native'; import Webcam from 'react-webcam'; import useTabNavigatorFocus from '@hooks/useTabNavigatorFocus'; -const propTypes = { - /** Flag to turn on/off the torch/flashlight - if available */ - torchOn: PropTypes.bool, - - /** The index of the tab that contains this camera */ - cameraTabIndex: PropTypes.number.isRequired, - - /** Callback function when media stream becomes available - user granted camera permissions and camera starts to work */ - onUserMedia: PropTypes.func, - - /** Callback function passing torch/flashlight capability as bool param of the browser */ - onTorchAvailability: PropTypes.func, -}; - -const defaultProps = { - onUserMedia: undefined, - onTorchAvailability: undefined, - torchOn: false, +type NavigationAwareCameraProps = { + torchOn: boolean; + onTorchAvailability?: (torchAvailable: boolean) => void; + cameraTabIndex: number; + onUserMedia?: (stream: MediaStream) => void; }; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, cameraTabIndex, ...props}, ref) => { - const trackRef = useRef(null); +const NavigationAwareCamera = forwardRef(({torchOn, onTorchAvailability, cameraTabIndex, ...props}: NavigationAwareCameraProps, ref: React.Ref) => { + const trackRef = useRef(null); const shouldShowCamera = useTabNavigatorFocus({ tabIndex: cameraTabIndex, }); - const handleOnUserMedia = (stream) => { + const handleOnUserMedia = (stream: MediaStream) => { if (props.onUserMedia) { props.onUserMedia(stream); } @@ -73,8 +60,6 @@ const NavigationAwareCamera = React.forwardRef(({torchOn, onTorchAvailability, c ); }); -NavigationAwareCamera.propTypes = propTypes; -NavigationAwareCamera.displayName = 'NavigationAwareCamera'; -NavigationAwareCamera.defaultProps = defaultProps; +(NavigationAwareCamera as FC).displayName = 'NavigationAwareCamera'; export default NavigationAwareCamera; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx similarity index 84% rename from src/pages/iou/request/step/IOURequestStepScan/index.native.js rename to src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index a1a3ed946967..bd6dc55f946d 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -1,4 +1,4 @@ -import lodashGet from 'lodash/get'; +import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ActivityIndicator, Alert, AppState, View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; @@ -14,7 +14,6 @@ import * as Expensicons from '@components/Icon/Expensicons'; import ImageSVG from '@components/ImageSVG'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Text from '@components/Text'; -import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -22,49 +21,32 @@ import compose from '@libs/compose'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import IOURequestStepRoutePropTypes from '@pages/iou/request/step/IOURequestStepRoutePropTypes'; +import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import * as CameraPermission from './CameraPermission'; +import type IOURequestStepPropTypes from './IOURequestStepScanProps'; import NavigationAwareCamera from './NavigationAwareCamera'; -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: IOURequestStepRoutePropTypes.isRequired, - - /* Onyx Props */ - /** The report that the transaction belongs to */ - report: reportPropTypes, - - /** The transaction (or draft transaction) being changed */ - transaction: transactionPropTypes, -}; - -const defaultProps = { - report: {}, - transaction: {}, -}; - -function IOURequestStepScan({ - report, - route: { - params: {action, iouType, reportID, transactionID, backTo}, - }, - transaction: {isFromGlobalCreate}, -}) { +type IOURequestStepScanProps = IOURequestStepPropTypes & + StackScreenProps & { + isFromGlobalCreate: boolean; + }; + +function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepScanProps) { const theme = useTheme(); const styles = useThemeStyles(); const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; - const camera = useRef(null); + const camera = useRef(null); const [flash, setFlash] = useState(false); - const [cameraPermissionStatus, setCameraPermissionStatus] = useState(undefined); + const [cameraPermissionStatus, setCameraPermissionStatus] = useState(null); const {translate} = useLocalize(); @@ -127,19 +109,21 @@ function IOURequestStepScan({ }; }, []); - const validateReceipt = (file) => { - const {fileExtension} = FileUtils.splitExtensionFromFileName(lodashGet(file, 'name', '')); - if (!CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS.includes(fileExtension.toLowerCase())) { + const validateReceipt = (file: File) => { + const {fileExtension} = FileUtils.splitExtensionFromFileName(file?.name ?? ''); + if ( + !CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS.includes(fileExtension.toLowerCase() as (typeof CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS)[number]) + ) { Alert.alert(translate('attachmentPicker.wrongFileType'), translate('attachmentPicker.notAllowedExtension')); return false; } - if (lodashGet(file, 'size', 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { + if ((file?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { Alert.alert(translate('attachmentPicker.attachmentTooLarge'), translate('attachmentPicker.sizeExceeded')); return false; } - if (lodashGet(file, 'size', 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { + if ((file?.size ?? 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { Alert.alert(translate('attachmentPicker.attachmentTooSmall'), translate('attachmentPicker.sizeNotMet')); return false; } @@ -167,44 +151,44 @@ function IOURequestStepScan({ }; const navigateToConfirmationStep = useCallback(() => { - if (backTo) { - Navigation.goBack(backTo); + if (route.params.backTo) { + Navigation.goBack(route.params.backTo); return; } // If the transaction was created from the global create, the person needs to select participants, so take them there. if (isFromGlobalCreate) { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(route.params.iouType, route.params.transactionID, route.params.reportID)); return; } // If the transaction was created from the + menu from the composer inside of a chat, the participants can automatically // be added to the transaction (taken from the chat report participants) and then the person is taken to the confirmation step. - IOU.setMoneyRequestParticipantsFromReport(transactionID, report); - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID)); - }, [iouType, report, reportID, transactionID, isFromGlobalCreate, backTo]); + IOU.setMoneyRequestParticipantsFromReport(route.params.transactionID, report); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(route.params.iouType, route.params.transactionID, route.params.reportID)); + }, [route.params.iouType, report, route.params.reportID, route.params.transactionID, isFromGlobalCreate, route.params.backTo]); const updateScanAndNavigate = useCallback( - (file, source) => { + (file: File, source: string) => { Navigation.dismissModal(); - IOU.replaceReceipt(transactionID, file, source); + IOU.replaceReceipt(route.params.transactionID, file, source); }, - [transactionID], + [route.params.transactionID], ); /** * Sets the Receipt objects and navigates the user to the next page * @param {Object} file */ - const setReceiptAndNavigate = (file) => { + const setReceiptAndNavigate = (file: File) => { if (!validateReceipt(file)) { return; } // Store the receipt on the transaction object in Onyx - IOU.setMoneyRequestReceipt(transactionID, file.uri, file.name, action !== CONST.IOU.ACTION.EDIT); + IOU.setMoneyRequestReceipt(route.params.transactionID, file.uri, file.name, route.params.action !== CONST.IOU.ACTION.EDIT); - if (action === CONST.IOU.ACTION.EDIT) { + if (route.params.action === CONST.IOU.ACTION.EDIT) { updateScanAndNavigate(file, file.uri); return; } @@ -230,9 +214,9 @@ function IOURequestStepScan({ .then((photo) => { // Store the receipt on the transaction object in Onyx const source = `file://${photo.path}`; - IOU.setMoneyRequestReceipt(transactionID, source, photo.path, action !== CONST.IOU.ACTION.EDIT); + IOU.setMoneyRequestReceipt(route.params.transactionID, source, photo.path, route.params.action !== CONST.IOU.ACTION.EDIT); - if (action === CONST.IOU.ACTION.EDIT) { + if (route.params.action === CONST.IOU.ACTION.EDIT) { FileUtils.readFileAsync(source, photo.path, (file) => { updateScanAndNavigate(file, source); }); @@ -245,7 +229,7 @@ function IOURequestStepScan({ showCameraAlert(); Log.warn('Error taking photo', error); }); - }, [flash, action, translate, transactionID, updateScanAndNavigate, navigateToConfirmationStep]); + }, [flash, route.params.action, translate, route.params.transactionID, updateScanAndNavigate, navigateToConfirmationStep]); // Wait for camera permission status to render if (cameraPermissionStatus == null) { @@ -257,7 +241,7 @@ function IOURequestStepScan({ includeSafeAreaPaddingBottom headerTitle={translate('common.receipt')} onBackButtonPress={navigateBack} - shouldShowWrapper={Boolean(backTo)} + shouldShowWrapper={Boolean(route.params.backTo)} testID={IOURequestStepScan.displayName} > {cameraPermissionStatus !== RESULTS.GRANTED && ( @@ -363,8 +347,6 @@ function IOURequestStepScan({ ); } -IOURequestStepScan.defaultProps = defaultProps; -IOURequestStepScan.propTypes = propTypes; IOURequestStepScan.displayName = 'IOURequestStepScan'; export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepScan); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.js b/src/pages/iou/request/step/IOURequestStepScan/index.tsx similarity index 82% rename from src/pages/iou/request/step/IOURequestStepScan/index.js rename to src/pages/iou/request/step/IOURequestStepScan/index.tsx index 7da97c34cc2b..6a221a423818 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.js +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -1,4 +1,4 @@ -import lodashGet from 'lodash/get'; +import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useContext, useReducer, useRef, useState} from 'react'; import {ActivityIndicator, PanResponder, PixelRatio, View} from 'react-native'; import Hand from '@assets/images/hand.svg'; @@ -13,7 +13,6 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Text from '@components/Text'; -import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -22,41 +21,25 @@ import * as Browser from '@libs/Browser'; import compose from '@libs/compose'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import Navigation from '@libs/Navigation/Navigation'; +import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; import ReceiptDropUI from '@pages/iou/ReceiptDropUI'; -import IOURequestStepRoutePropTypes from '@pages/iou/request/step/IOURequestStepRoutePropTypes'; import StepScreenDragAndDropWrapper from '@pages/iou/request/step/StepScreenDragAndDropWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; -import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type IOURequestStepPropTypes from './IOURequestStepScanProps'; import NavigationAwareCamera from './NavigationAwareCamera'; -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: IOURequestStepRoutePropTypes.isRequired, - - /* Onyx Props */ - /** The report that the transaction belongs to */ - report: reportPropTypes, - - /** The transaction (or draft transaction) being changed */ - transaction: transactionPropTypes, -}; - -const defaultProps = { - report: {}, - transaction: {}, -}; - -function IOURequestStepScan({ - report, - route: { - params: {action, iouType, reportID, transactionID, backTo}, - }, - transaction: {isFromGlobalCreate}, -}) { +type IOURequestStepScanProps = IOURequestStepPropTypes & + StackScreenProps & { + isFromGlobalCreate: boolean; + }; + +function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepScanProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -81,29 +64,28 @@ function IOURequestStepScan({ /** * Sets the upload receipt error modal content when an invalid receipt is uploaded - * @param {*} isInvalid - * @param {*} title - * @param {*} reason */ - const setUploadReceiptError = (isInvalid, title, reason) => { + const setUploadReceiptError = (isInvalid: boolean, title: string, reason: string) => { setIsAttachmentInvalid(isInvalid); setAttachmentInvalidReasonTitle(title); setAttachmentValidReason(reason); }; - function validateReceipt(file) { - const {fileExtension} = FileUtils.splitExtensionFromFileName(lodashGet(file, 'name', '')); - if (!CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS.includes(fileExtension.toLowerCase())) { + function validateReceipt(file: File) { + const {fileExtension} = FileUtils.splitExtensionFromFileName(file?.name ?? ''); + if ( + !CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS.includes(fileExtension.toLowerCase() as (typeof CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS)[number]) + ) { setUploadReceiptError(true, 'attachmentPicker.wrongFileType', 'attachmentPicker.notAllowedExtension'); return false; } - if (lodashGet(file, 'size', 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { + if ((file?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { setUploadReceiptError(true, 'attachmentPicker.attachmentTooLarge', 'attachmentPicker.sizeExceeded'); return false; } - if (lodashGet(file, 'size', 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { + if ((file?.size ?? 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { setUploadReceiptError(true, 'attachmentPicker.attachmentTooSmall', 'attachmentPicker.sizeNotMet'); return false; } @@ -112,49 +94,48 @@ function IOURequestStepScan({ } const navigateBack = () => { - Navigation.goBack(backTo); + Navigation.goBack(route.params.backTo); }; const navigateToConfirmationStep = useCallback(() => { - if (backTo) { - Navigation.goBack(backTo); + if (route.params.backTo) { + Navigation.goBack(route.params.backTo); return; } // If the transaction was created from the global create, the person needs to select participants, so take them there. if (isFromGlobalCreate) { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(route.params.iouType, route.params.transactionID, route.params.reportID)); return; } // If the transaction was created from the + menu from the composer inside of a chat, the participants can automatically // be added to the transaction (taken from the chat report participants) and then the person is taken to the confirmation step. - IOU.setMoneyRequestParticipantsFromReport(transactionID, report); - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID)); - }, [iouType, report, reportID, transactionID, isFromGlobalCreate, backTo]); + IOU.setMoneyRequestParticipantsFromReport(route.params.transactionID, report); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(route.params.iouType, route.params.transactionID, route.params.reportID)); + }, [route.params.iouType, report, route.params.reportID, route.params.transactionID, isFromGlobalCreate, route.params.backTo]); const updateScanAndNavigate = useCallback( - (file, source) => { - IOU.replaceReceipt(transactionID, file, source); + (file: File, source: string) => { + IOU.replaceReceipt(route.params.transactionID, file, source); Navigation.dismissModal(); }, - [transactionID], + [route.params.transactionID], ); /** * Sets the Receipt objects and navigates the user to the next page - * @param {Object} file */ - const setReceiptAndNavigate = (file) => { + const setReceiptAndNavigate = (file: File) => { if (!validateReceipt(file)) { return; } // Store the receipt on the transaction object in Onyx const source = URL.createObjectURL(file); - IOU.setMoneyRequestReceipt(transactionID, source, file.name, action !== CONST.IOU.ACTION.EDIT); + IOU.setMoneyRequestReceipt(route.params.transactionID, source, file.name, route.params.action !== CONST.IOU.ACTION.EDIT); - if (action === CONST.IOU.ACTION.EDIT) { + if (route.params.action === CONST.IOU.ACTION.EDIT) { updateScanAndNavigate(file, source); return; } @@ -163,22 +144,22 @@ function IOURequestStepScan({ }; const capturePhoto = useCallback(() => { - if (!cameraRef.current.getScreenshot) { + if (!cameraRef?.current?.getScreenshot) { return; } - const imageBase64 = cameraRef.current.getScreenshot(); + const imageBase64 = cameraRef?.current?.getScreenshot(); const filename = `receipt_${Date.now()}.png`; const file = FileUtils.base64ToFile(imageBase64, filename); const source = URL.createObjectURL(file); - IOU.setMoneyRequestReceipt(transactionID, source, file.name, action !== CONST.IOU.ACTION.EDIT); + IOU.setMoneyRequestReceipt(route.params.transactionID, source, file.name, route.params.action !== CONST.IOU.ACTION.EDIT); - if (action === CONST.IOU.ACTION.EDIT) { + if (route.params.action === CONST.IOU.ACTION.EDIT) { updateScanAndNavigate(file, source); return; } navigateToConfirmationStep(); - }, [cameraRef, action, transactionID, updateScanAndNavigate, navigateToConfirmationStep]); + }, [cameraRef, route.params.action, route.params.transactionID, updateScanAndNavigate, navigateToConfirmationStep]); const panResponder = useRef( PanResponder.create({ @@ -320,24 +301,24 @@ function IOURequestStepScan({ {!isDraggingOver && (Browser.isMobile() ? mobileCameraView() : desktopUploadView())} { - const file = lodashGet(e, ['dataTransfer', 'files', 0]); + const file = e?.dataTransfer?.files?.[0]; setReceiptAndNavigate(file); }} receiptImageTopPosition={receiptImageTopPosition} /> @@ -346,8 +327,6 @@ function IOURequestStepScan({ ); } -IOURequestStepScan.defaultProps = defaultProps; -IOURequestStepScan.propTypes = propTypes; IOURequestStepScan.displayName = 'IOURequestStepScan'; export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepScan); From 9a291eb22cb408ac35a2a53c56f7a227b5ff21bf Mon Sep 17 00:00:00 2001 From: codinggeek2023 Date: Mon, 12 Feb 2024 14:20:53 +0530 Subject: [PATCH 02/54] Put Request types --- src/libs/Navigation/types.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index bbdb03ab3df8..667215467160 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -283,6 +283,13 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; + [SCREENS.MONEY_REQUEST.CREATE]: { + action: ValueOf; + iouType: ValueOf; + transactionID: string; + reportID: string; + backTo: Route; + } }; type NewTaskNavigatorParamList = { From 5bcb3e387dd7de6aa2932f65cff629a01069caeb Mon Sep 17 00:00:00 2001 From: codinggeek2023 Date: Mon, 12 Feb 2024 14:38:52 +0530 Subject: [PATCH 03/54] Update types --- .../iou/request/step/IOURequestStepScan/index.native.tsx | 8 ++++---- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index bd6dc55f946d..597b14f0d8bd 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -4,7 +4,8 @@ import {ActivityIndicator, Alert, AppState, View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import {RESULTS} from 'react-native-permissions'; import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withDelay, withSequence, withSpring, withTiming} from 'react-native-reanimated'; -import {useCameraDevices} from 'react-native-vision-camera'; +import { useCameraDevices} from 'react-native-vision-camera'; +import type {Camera, Point} from 'react-native-vision-camera' import Hand from '@assets/images/hand.svg'; import Shutter from '@assets/images/shutter.svg'; import AttachmentPicker from '@components/AttachmentPicker'; @@ -44,7 +45,7 @@ function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepS const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; - const camera = useRef(null); + const camera = useRef(null); const [flash, setFlash] = useState(false); const [cameraPermissionStatus, setCameraPermissionStatus] = useState(null); @@ -59,7 +60,7 @@ function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepS transform: [{translateX: focusIndicatorPosition.value.x}, {translateY: focusIndicatorPosition.value.y}, {scale: focusIndicatorScale.value}], })); - const focusCamera = (point) => { + const focusCamera = (point: Point) => { if (!camera.current) { return; } @@ -178,7 +179,6 @@ function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepS /** * Sets the Receipt objects and navigates the user to the next page - * @param {Object} file */ const setReceiptAndNavigate = (file: File) => { if (!validateReceipt(file)) { diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 6a221a423818..4c10218fb925 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -31,6 +31,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import type Webcam from 'react-webcam'; import type IOURequestStepPropTypes from './IOURequestStepScanProps'; import NavigationAwareCamera from './NavigationAwareCamera'; @@ -56,7 +57,7 @@ function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepS const [cameraPermissionState, setCameraPermissionState] = useState('prompt'); const [isFlashLightOn, toggleFlashlight] = useReducer((state) => !state, false); const [isTorchAvailable, setIsTorchAvailable] = useState(false); - const cameraRef = useRef(null); + const cameraRef = useRef(null) const hideRecieptModal = () => { setIsAttachmentInvalid(false); @@ -149,7 +150,7 @@ function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepS } const imageBase64 = cameraRef?.current?.getScreenshot(); const filename = `receipt_${Date.now()}.png`; - const file = FileUtils.base64ToFile(imageBase64, filename); + const file = FileUtils.base64ToFile(imageBase64 ?? '', filename); const source = URL.createObjectURL(file); IOU.setMoneyRequestReceipt(route.params.transactionID, source, file.name, route.params.action !== CONST.IOU.ACTION.EDIT); From eb8dd5fd35a2debacc458ad80e421e9e17f9c776 Mon Sep 17 00:00:00 2001 From: codinggeek2023 Date: Mon, 12 Feb 2024 20:03:53 +0530 Subject: [PATCH 04/54] blank commit for HOC --- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 4c10218fb925..281844d0567d 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -280,6 +280,7 @@ function IOURequestStepScan({report, route, isFromGlobalCreate}: IOURequestStepS + {({openPicker}) => (