From cbc94853098fda1f4e896913b1adc78116923918 Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 28 Sep 2023 21:59:45 +0700 Subject: [PATCH 1/3] Apply attachment picker to native app --- src/pages/iou/ReceiptSelector/index.native.js | 150 ++++-------------- 1 file changed, 33 insertions(+), 117 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 4de4e9bb9148..969d3e560137 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -3,12 +3,12 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import {useCameraDevices} from 'react-native-vision-camera'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import {launchImageLibrary} from 'react-native-image-picker'; import {withOnyx} from 'react-native-onyx'; import {RESULTS} from 'react-native-permissions'; import PressableWithFeedback from '../../../components/Pressable/PressableWithFeedback'; import Icon from '../../../components/Icon'; import * as Expensicons from '../../../components/Icon/Expensicons'; +import AttachmentPicker from '../../../components/AttachmentPicker'; import styles from '../../../styles/styles'; import Shutter from '../../../../assets/images/shutter.svg'; import Hand from '../../../../assets/images/hand.svg'; @@ -63,31 +63,6 @@ const defaultProps = { isInTabNavigator: true, }; -/** - * See https://github.com/react-native-image-picker/react-native-image-picker/#options - * for ImagePicker configuration options - */ -const imagePickerOptions = { - includeBase64: false, - saveToPhotos: false, - selectionLimit: 1, - includeExtra: false, -}; - -/** - * Return imagePickerOptions based on the type - * @param {String} type - * @returns {Object} - */ -function getImagePickerOptions(type) { - // mediaType property is one of the ImagePicker configuration to restrict types' - const mediaType = type === CONST.ATTACHMENT_PICKER_TYPE.IMAGE ? 'photo' : 'mixed'; - return { - mediaType, - ...imagePickerOptions, - }; -} - function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) { const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; @@ -123,35 +98,6 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) }; }, []); - /** - * Inform the users when they need to grant camera access and guide them to settings - */ - const showPermissionsAlert = () => { - Alert.alert( - translate('attachmentPicker.cameraPermissionRequired'), - translate('attachmentPicker.expensifyDoesntHaveAccessToCamera'), - [ - { - text: translate('common.cancel'), - style: 'cancel', - }, - { - text: translate('common.settings'), - onPress: () => Linking.openSettings(), - }, - ], - {cancelable: false}, - ); - }; - - /** - * A generic handling when we don't know the exact reason for an error - * - */ - const showGeneralAlert = () => { - Alert.alert(translate('attachmentPicker.attachmentError'), translate('attachmentPicker.errorWhileSelectingAttachment')); - }; - const askForPermissions = () => { // There's no way we can check for the BLOCKED status without requesting the permission first // https://github.com/zoontek/react-native-permissions/blob/a836e114ce3a180b2b23916292c79841a267d828/README.md?plain=1#L670 @@ -165,36 +111,6 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) } }; - /** - * Common image picker handling - * - * @param {function} imagePickerFunc - RNImagePicker.launchCamera or RNImagePicker.launchImageLibrary - * @returns {Promise} - */ - const showImagePicker = (imagePickerFunc) => - new Promise((resolve, reject) => { - imagePickerFunc(getImagePickerOptions(CONST.ATTACHMENT_PICKER_TYPE.IMAGE), (response) => { - if (response.didCancel) { - // When the user cancelled resolve with no attachment - return resolve(); - } - if (response.errorCode) { - switch (response.errorCode) { - case 'permission': - showPermissionsAlert(); - return resolve(); - default: - showGeneralAlert(); - break; - } - - return reject(new Error(`Error during attachment selection: ${response.errorMessage}`)); - } - - return resolve(response.assets); - }); - }); - const takePhoto = useCallback(() => { const showCameraAlert = () => { Alert.alert(translate('receipt.cameraErrorTitle'), translate('receipt.cameraErrorMessage')); @@ -276,38 +192,38 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) /> )} - { - showImagePicker(launchImageLibrary) - .then((receiptImage) => { - const filePath = receiptImage[0].uri; - IOU.setMoneyRequestReceipt(filePath, receiptImage[0].fileName); - - if (transactionID) { - FileUtils.readFileAsync(filePath, receiptImage[0].fileName).then((receipt) => { - IOU.replaceReceipt(transactionID, receipt, filePath); - }); - Navigation.dismissModal(); - return; - } - - IOU.navigateToNextPage(iou, iouType, report, route.path); - }) - .catch(() => { - Log.info('User did not select an image from gallery'); - }); - }} - > - - + + {({openPicker}) => ( + { + openPicker({ + onPicked: (file) => { + const filePath = file.uri; + IOU.setMoneyRequestReceipt(filePath, file.name); + + if (transactionID) { + IOU.replaceReceipt(transactionID, file, filePath); + Navigation.dismissModal(); + return; + } + + IOU.navigateToNextPage(iou, iouType, report, route.path); + }, + }); + }} + > + + + )} + Date: Thu, 28 Sep 2023 22:16:43 +0700 Subject: [PATCH 2/3] Hide camera option --- .../AttachmentPicker/index.native.js | 29 ++++++++++++------- src/pages/iou/ReceiptSelector/index.native.js | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js index 8b1bb54da920..d3d673b7de71 100644 --- a/src/components/AttachmentPicker/index.native.js +++ b/src/components/AttachmentPicker/index.native.js @@ -1,10 +1,12 @@ import _ from 'underscore'; import React, {useState, useRef, useCallback, useMemo} from 'react'; +import PropTypes from 'prop-types'; import {View, Alert, Linking} from 'react-native'; import RNDocumentPicker from 'react-native-document-picker'; import RNFetchBlob from 'react-native-blob-util'; +import lodashCompact from 'lodash/compact'; import {launchImageLibrary} from 'react-native-image-picker'; -import {propTypes as basePropTypes, defaultProps} from './attachmentPickerPropTypes'; +import {propTypes as basePropTypes, defaultProps as baseDefaultProps} from './attachmentPickerPropTypes'; import CONST from '../../CONST'; import * as FileUtils from '../../libs/fileDownload/FileUtils'; import * as Expensicons from '../Icon/Expensicons'; @@ -19,8 +21,16 @@ import useArrowKeyFocusManager from '../../hooks/useArrowKeyFocusManager'; const propTypes = { ...basePropTypes, + + /** If this value is true, then we exclude Camera option. */ + shouldHideCameraOption: PropTypes.bool, }; +const defaultProps = { + ...baseDefaultProps, + shouldHideCameraOption: false, +} + /** * See https://github.com/react-native-image-picker/react-native-image-picker/#options * for ImagePicker configuration options @@ -90,7 +100,7 @@ const getDataForUpload = (fileData) => { * @param {propTypes} props * @returns {JSX.Element} */ -function AttachmentPicker({type, children}) { +function AttachmentPicker({type, children, shouldHideCameraOption}) { const [isVisible, setIsVisible] = useState(false); const completeAttachmentSelection = useRef(); @@ -180,8 +190,8 @@ function AttachmentPicker({type, children}) { ); const menuItemData = useMemo(() => { - const data = [ - { + const data = lodashCompact([ + !shouldHideCameraOption && { icon: Expensicons.Camera, textTranslationKey: 'attachmentPicker.takePhoto', pickAttachment: () => showImagePicker(launchCamera), @@ -191,18 +201,15 @@ function AttachmentPicker({type, children}) { textTranslationKey: 'attachmentPicker.chooseFromGallery', pickAttachment: () => showImagePicker(launchImageLibrary), }, - ]; - - if (type !== CONST.ATTACHMENT_PICKER_TYPE.IMAGE) { - data.push({ + type !== CONST.ATTACHMENT_PICKER_TYPE.IMAGE && { icon: Expensicons.Paperclip, textTranslationKey: 'attachmentPicker.chooseDocument', pickAttachment: showDocumentPicker, - }); - } + } + ]); return data; - }, [showDocumentPicker, showImagePicker, type]); + }, [showDocumentPicker, showImagePicker, type, shouldHideCameraOption]); const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({initialFocusedIndex: -1, maxIndex: menuItemData.length - 1, isActive: isVisible}); diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 969d3e560137..a7fef7842e94 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -192,7 +192,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) /> )} - + {({openPicker}) => ( Date: Thu, 28 Sep 2023 22:23:25 +0700 Subject: [PATCH 3/3] Fix linter --- src/components/AttachmentPicker/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js index d3d673b7de71..0167bcbb7a4e 100644 --- a/src/components/AttachmentPicker/index.native.js +++ b/src/components/AttachmentPicker/index.native.js @@ -29,7 +29,7 @@ const propTypes = { const defaultProps = { ...baseDefaultProps, shouldHideCameraOption: false, -} +}; /** * See https://github.com/react-native-image-picker/react-native-image-picker/#options @@ -205,7 +205,7 @@ function AttachmentPicker({type, children, shouldHideCameraOption}) { icon: Expensicons.Paperclip, textTranslationKey: 'attachmentPicker.chooseDocument', pickAttachment: showDocumentPicker, - } + }, ]); return data;