From d2f3e5fcea1eda0e9cf2bde070bdc9472cae9438 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Sep 2023 12:11:47 -0600 Subject: [PATCH 01/28] create EditRequestReceiptPage --- src/pages/EditRequestReceiptPage.js | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/pages/EditRequestReceiptPage.js diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js new file mode 100644 index 000000000000..6fa6e8edde3c --- /dev/null +++ b/src/pages/EditRequestReceiptPage.js @@ -0,0 +1,47 @@ +import React from 'react'; +import {View} from 'react-native'; +import PropTypes from 'prop-types'; +import ScreenWrapper from '../components/ScreenWrapper'; +import HeaderWithBackButton from '../components/HeaderWithBackButton'; +import Navigation from '../libs/Navigation/Navigation'; +import useLocalize from '../hooks/useLocalize'; +import styles from '../styles/styles'; +import ReportActionItemImage from '../components/ReportActionItem/ReportActionItemImage'; + +const propTypes = { + /** TransactionID associated with the receipt */ + transactionID: PropTypes.string.isRequired, + + /** The receipt object with its ID, source and filename */ + receipt: PropTypes.shape({ + source: PropTypes.string, + }).isRequired, +}; + +function EditRequestReceiptPage({transactionID, receipt}) { + const {translate} = useLocalize(); + + return ( + + + + + + + ); +} + +EditRequestReceiptPage.propTypes = propTypes; +EditRequestReceiptPage.displayName = 'EditRequestReceiptPage'; + +export default EditRequestReceiptPage; From 6c96baf58539bed0b1d2495129cbc3e1afb83b70 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Sep 2023 13:58:17 -0600 Subject: [PATCH 02/28] rm EditRequestReceiptPage --- src/pages/EditRequestReceiptPage.js | 47 ----------------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/pages/EditRequestReceiptPage.js diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js deleted file mode 100644 index 6fa6e8edde3c..000000000000 --- a/src/pages/EditRequestReceiptPage.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import ScreenWrapper from '../components/ScreenWrapper'; -import HeaderWithBackButton from '../components/HeaderWithBackButton'; -import Navigation from '../libs/Navigation/Navigation'; -import useLocalize from '../hooks/useLocalize'; -import styles from '../styles/styles'; -import ReportActionItemImage from '../components/ReportActionItem/ReportActionItemImage'; - -const propTypes = { - /** TransactionID associated with the receipt */ - transactionID: PropTypes.string.isRequired, - - /** The receipt object with its ID, source and filename */ - receipt: PropTypes.shape({ - source: PropTypes.string, - }).isRequired, -}; - -function EditRequestReceiptPage({transactionID, receipt}) { - const {translate} = useLocalize(); - - return ( - - - - - - - ); -} - -EditRequestReceiptPage.propTypes = propTypes; -EditRequestReceiptPage.displayName = 'EditRequestReceiptPage'; - -export default EditRequestReceiptPage; From c1bd0b133f2fe12b68d0375d53ff00da0a1d3c5c Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Sep 2023 14:03:35 -0600 Subject: [PATCH 03/28] add three dot menu --- src/components/AttachmentModal.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index c07a4474a68b..150fe93e5733 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -26,6 +26,7 @@ import SafeAreaConsumer from './SafeAreaConsumer'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import reportPropTypes from '../pages/reportPropTypes'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; +import * as Expensicons from './Icon/Expensicons'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -339,12 +340,25 @@ function AttachmentModal(props) { downloadAttachment(source)} shouldShowCloseButton={!props.isSmallScreenWidth} shouldShowBackButton={props.isSmallScreenWidth} onBackButtonPress={closeModal} onCloseButtonPress={closeModal} + shouldShowThreeDotsButton={isAttachmentReceipt} + threeDotsMenuItems={[ + { + icon: Expensicons.Camera, + text: props.translate('common.replace'), + onSelected: () => {}, // TODO + }, + { + icon: Expensicons.Download, + text: props.translate('common.download'), + onSelected: () => downloadAttachment(source), + }, + ]} /> {!_.isEmpty(props.report) ? ( From 3c2594ad8285de18316c39c0851125129f39dcb8 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Sep 2023 14:27:50 -0600 Subject: [PATCH 04/28] use correct anchor position --- src/components/AttachmentModal.js | 4 ++++ src/styles/styles.js | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 150fe93e5733..12e9abaae585 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -27,6 +27,7 @@ import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import reportPropTypes from '../pages/reportPropTypes'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import * as Expensicons from './Icon/Expensicons'; +import useWindowDimensions from '../hooks/useWindowDimensions'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -107,6 +108,8 @@ function AttachmentModal(props) { const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false); const [confirmButtonFadeAnimation] = useState(new Animated.Value(1)); const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true); + const {windowWidth} = useWindowDimensions(); + const [file, setFile] = useState( props.originalFileName ? { @@ -347,6 +350,7 @@ function AttachmentModal(props) { onBackButtonPress={closeModal} onCloseButtonPress={closeModal} shouldShowThreeDotsButton={isAttachmentReceipt} + threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)} threeDotsMenuItems={[ { icon: Expensicons.Camera, diff --git a/src/styles/styles.js b/src/styles/styles.js index c38da2ecc814..a8d1be6cc94b 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3131,6 +3131,11 @@ const styles = { horizontal: windowWidth - 10, }), + threeDotsPopoverOffsetAttachmentModal: (windowWidth) => ({ + ...getPopOverVerticalOffset(60), + horizontal: windowWidth - 120, + }), + invert: { // It's important to invert the Y AND X axis to prevent a react native issue that can lead to ANRs on android 13 transform: [{scaleX: -1}, {scaleY: -1}], From 3a69d70ae3ed44b0262d7d5e4dbc320aa21ef3b2 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Sep 2023 14:35:29 -0600 Subject: [PATCH 05/28] add overlay --- src/components/AttachmentModal.js | 1 + src/components/HeaderWithBackButton/index.js | 2 ++ src/components/ThreeDotsMenu/index.js | 8 ++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 12e9abaae585..39f337158a16 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -363,6 +363,7 @@ function AttachmentModal(props) { onSelected: () => downloadAttachment(source), }, ]} + shouldOverlay /> {!_.isEmpty(props.report) ? ( diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index bbf905cc1ac2..cf61a4cf4eb7 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -47,6 +47,7 @@ function HeaderWithBackButton({ }, threeDotsMenuItems = [], children = null, + shouldOverlay = false, }) { const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState(); const {translate} = useLocalize(); @@ -137,6 +138,7 @@ function HeaderWithBackButton({ menuItems={threeDotsMenuItems} onIconPress={onThreeDotsButtonPress} anchorPosition={threeDotsAnchorPosition} + shouldOverlay={shouldOverlay} /> )} {shouldShowCloseButton && ( diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js index b5637a4f3879..f0cee6fdea2f 100644 --- a/src/components/ThreeDotsMenu/index.js +++ b/src/components/ThreeDotsMenu/index.js @@ -45,6 +45,9 @@ const propTypes = { horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), }), + + /** Whether the popover menu should overlay the current view */ + shouldOverlay: PropTypes.bool, }; const defaultProps = { @@ -57,9 +60,10 @@ const defaultProps = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP }, + shouldOverlay: false, }; -function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment}) { +function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment, shouldOverlay}) { const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); const buttonRef = useRef(null); const {translate} = useLocalize(); @@ -106,7 +110,7 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me anchorAlignment={anchorAlignment} onItemSelected={hidePopoverMenu} menuItems={menuItems} - withoutOverlay + withoutOverlay={!shouldOverlay} anchorRef={buttonRef} /> From 8116a8e8924dd96ca58e780647524801e9cbe2e8 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 1 Sep 2023 14:43:42 -0600 Subject: [PATCH 06/28] adjust offset --- src/styles/styles.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index a8d1be6cc94b..6c882dbddd83 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3132,8 +3132,8 @@ const styles = { }), threeDotsPopoverOffsetAttachmentModal: (windowWidth) => ({ - ...getPopOverVerticalOffset(60), - horizontal: windowWidth - 120, + ...getPopOverVerticalOffset(80), + horizontal: windowWidth - 140, }), invert: { From 6ea483c2c22cc605ca20782fc349b033e2962c5c Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 12:33:00 -0600 Subject: [PATCH 07/28] add replace callback --- src/components/AttachmentModal.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 39f337158a16..9e21818f07b9 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -28,6 +28,8 @@ import reportPropTypes from '../pages/reportPropTypes'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import * as Expensicons from './Icon/Expensicons'; import useWindowDimensions from '../hooks/useWindowDimensions'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -355,7 +357,10 @@ function AttachmentModal(props) { { icon: Expensicons.Camera, text: props.translate('common.replace'), - onSelected: () => {}, // TODO + onSelected: () => { + closeModal(); + Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, 'amount')); + }, }, { icon: Expensicons.Download, From eb50cd0e42d2d5d65d47c6489b980145521bde5e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 12:49:22 -0600 Subject: [PATCH 08/28] add comment --- src/components/AttachmentModal.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 9e21818f07b9..abbc656bf844 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -111,6 +111,7 @@ function AttachmentModal(props) { const [confirmButtonFadeAnimation] = useState(new Animated.Value(1)); const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true); const {windowWidth} = useWindowDimensions(); + const [isEditingReceipt, setIsEditingReceipt] = useState(false); const [file, setFile] = useState( props.originalFileName @@ -359,6 +360,8 @@ function AttachmentModal(props) { text: props.translate('common.replace'), onSelected: () => { closeModal(); + + // TODO: replace with actual edit receipt route Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, 'amount')); }, }, From 356f371d4533716eae1216bdbcd51d85eec78227 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 13:34:12 -0600 Subject: [PATCH 09/28] use attachmentmodal in reportActionItemImage --- src/components/AttachmentModal.js | 15 ++++---- .../ReportActionItem/ReportActionItemImage.js | 38 ++++++++++--------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index abbc656bf844..1d759eb958bb 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -73,6 +73,9 @@ const propTypes = { /** The report that has this attachment */ report: reportPropTypes, + /** Whether the attachment is a receipt image */ + isReceipt: PropTypes.bool, + ...withLocalizePropTypes, ...windowDimensionsPropTypes, @@ -95,6 +98,7 @@ const defaultProps = { onModalHide: () => {}, onCarouselAttachmentChange: () => {}, isWorkspaceAvatar: false, + isReceipt: false, }; function AttachmentModal(props) { @@ -102,7 +106,6 @@ function AttachmentModal(props) { const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false); const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired); - const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(false); const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState(''); const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null); const [source, setSource] = useState(props.source); @@ -111,7 +114,6 @@ function AttachmentModal(props) { const [confirmButtonFadeAnimation] = useState(new Animated.Value(1)); const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true); const {windowWidth} = useWindowDimensions(); - const [isEditingReceipt, setIsEditingReceipt] = useState(false); const [file, setFile] = useState( props.originalFileName @@ -126,13 +128,12 @@ function AttachmentModal(props) { /** * Keeps the attachment source in sync with the attachment displayed currently in the carousel. - * @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string }, isReceipt: Boolean }} attachment + * @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string } }} attachment */ const onNavigate = useCallback( (attachment) => { setSource(attachment.source); setFile(attachment.file); - setIsAttachmentReceipt(attachment.isReceipt); setIsAuthTokenRequired(attachment.isAuthTokenRequired); onCarouselAttachmentChange(attachment); }, @@ -344,15 +345,15 @@ function AttachmentModal(props) { > {props.isSmallScreenWidth && } downloadAttachment(source)} shouldShowCloseButton={!props.isSmallScreenWidth} shouldShowBackButton={props.isSmallScreenWidth} onBackButtonPress={closeModal} onCloseButtonPress={closeModal} - shouldShowThreeDotsButton={isAttachmentReceipt} + shouldShowThreeDotsButton={props.isReceipt} threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)} threeDotsMenuItems={[ { diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js index 5f8444af0b21..ad4a6ed27c2e 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.js +++ b/src/components/ReportActionItem/ReportActionItemImage.js @@ -4,12 +4,10 @@ import styles from '../../styles/styles'; import Image from '../Image'; import ThumbnailImage from '../ThumbnailImage'; import tryResolveUrlFromApiRoot from '../../libs/tryResolveUrlFromApiRoot'; -import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; -import {ShowContextMenuContext} from '../ShowContextMenuContext'; -import Navigation from '../../libs/Navigation/Navigation'; import PressableWithoutFocus from '../Pressable/PressableWithoutFocus'; import useLocalize from '../../hooks/useLocalize'; +import AttachmentModal from '../AttachmentModal'; const propTypes = { /** thumbnail URI for the image */ @@ -50,21 +48,25 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal}) { if (enablePreviewModal) { return ( - - {({report}) => ( - { - const route = ROUTES.getReportAttachmentRoute(report.reportID, imageSource); - Navigation.navigate(route); - }} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} - accessibilityLabel={translate('accessibilityHints.viewAttachment')} - > - {thumbnailComponent} - - )} - + <> + + {({show}) => ( + + {thumbnailComponent} + + )} + + ); } return thumbnailComponent; From 1c54e5939b6eb8242cd69fb3cc02a18076b8de79 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 14:44:57 -0600 Subject: [PATCH 10/28] Revert "use attachmentmodal in reportActionItemImage" This reverts commit 356f371d4533716eae1216bdbcd51d85eec78227. --- src/components/AttachmentModal.js | 15 ++++---- .../ReportActionItem/ReportActionItemImage.js | 38 +++++++++---------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 1d759eb958bb..abbc656bf844 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -73,9 +73,6 @@ const propTypes = { /** The report that has this attachment */ report: reportPropTypes, - /** Whether the attachment is a receipt image */ - isReceipt: PropTypes.bool, - ...withLocalizePropTypes, ...windowDimensionsPropTypes, @@ -98,7 +95,6 @@ const defaultProps = { onModalHide: () => {}, onCarouselAttachmentChange: () => {}, isWorkspaceAvatar: false, - isReceipt: false, }; function AttachmentModal(props) { @@ -106,6 +102,7 @@ function AttachmentModal(props) { const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false); const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired); + const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(false); const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState(''); const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null); const [source, setSource] = useState(props.source); @@ -114,6 +111,7 @@ function AttachmentModal(props) { const [confirmButtonFadeAnimation] = useState(new Animated.Value(1)); const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true); const {windowWidth} = useWindowDimensions(); + const [isEditingReceipt, setIsEditingReceipt] = useState(false); const [file, setFile] = useState( props.originalFileName @@ -128,12 +126,13 @@ function AttachmentModal(props) { /** * Keeps the attachment source in sync with the attachment displayed currently in the carousel. - * @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string } }} attachment + * @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string }, isReceipt: Boolean }} attachment */ const onNavigate = useCallback( (attachment) => { setSource(attachment.source); setFile(attachment.file); + setIsAttachmentReceipt(attachment.isReceipt); setIsAuthTokenRequired(attachment.isAuthTokenRequired); onCarouselAttachmentChange(attachment); }, @@ -345,15 +344,15 @@ function AttachmentModal(props) { > {props.isSmallScreenWidth && } downloadAttachment(source)} shouldShowCloseButton={!props.isSmallScreenWidth} shouldShowBackButton={props.isSmallScreenWidth} onBackButtonPress={closeModal} onCloseButtonPress={closeModal} - shouldShowThreeDotsButton={props.isReceipt} + shouldShowThreeDotsButton={isAttachmentReceipt} threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)} threeDotsMenuItems={[ { diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js index ad4a6ed27c2e..5f8444af0b21 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.js +++ b/src/components/ReportActionItem/ReportActionItemImage.js @@ -4,10 +4,12 @@ import styles from '../../styles/styles'; import Image from '../Image'; import ThumbnailImage from '../ThumbnailImage'; import tryResolveUrlFromApiRoot from '../../libs/tryResolveUrlFromApiRoot'; +import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; +import {ShowContextMenuContext} from '../ShowContextMenuContext'; +import Navigation from '../../libs/Navigation/Navigation'; import PressableWithoutFocus from '../Pressable/PressableWithoutFocus'; import useLocalize from '../../hooks/useLocalize'; -import AttachmentModal from '../AttachmentModal'; const propTypes = { /** thumbnail URI for the image */ @@ -48,25 +50,21 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal}) { if (enablePreviewModal) { return ( - <> - - {({show}) => ( - - {thumbnailComponent} - - )} - - + + {({report}) => ( + { + const route = ROUTES.getReportAttachmentRoute(report.reportID, imageSource); + Navigation.navigate(route); + }} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} + accessibilityLabel={translate('accessibilityHints.viewAttachment')} + > + {thumbnailComponent} + + )} + ); } return thumbnailComponent; From 84ef3bd16151bc1cedb4cf01155f1688980808af Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 15:53:37 -0600 Subject: [PATCH 11/28] create EditRequestReceiptPage --- src/components/AttachmentModal.js | 7 ++++--- src/pages/EditRequestReceiptPage.js | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/pages/EditRequestReceiptPage.js diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index abbc656bf844..56183aa20643 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -111,7 +111,6 @@ function AttachmentModal(props) { const [confirmButtonFadeAnimation] = useState(new Animated.Value(1)); const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true); const {windowWidth} = useWindowDimensions(); - const [isEditingReceipt, setIsEditingReceipt] = useState(false); const [file, setFile] = useState( props.originalFileName @@ -361,8 +360,10 @@ function AttachmentModal(props) { onSelected: () => { closeModal(); - // TODO: replace with actual edit receipt route - Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, 'amount')); + // TODO: remove timeout + setTimeout(() => { + Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, 'receipt')); + }, 1000) }, }, { diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js new file mode 100644 index 000000000000..fd0f4893bb3a --- /dev/null +++ b/src/pages/EditRequestReceiptPage.js @@ -0,0 +1,27 @@ +import React from 'react'; +import ScreenWrapper from '../components/ScreenWrapper'; +import HeaderWithBackButton from '../components/HeaderWithBackButton'; +import Navigation from '../libs/Navigation/Navigation'; +import useLocalize from '../hooks/useLocalize'; +import ReceiptSelector from './iou/ReceiptSelector'; + +function EditRequestReceiptPage() { + const {translate} = useLocalize(); + + return ( + + + + + ); +} + +EditRequestReceiptPage.displayName = 'EditRequestReceiptPage'; + +export default EditRequestReceiptPage; From 2ac57b754eb8c62286ac709143f67575c6550893 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 15:58:26 -0600 Subject: [PATCH 12/28] create route const, add onSubmit callback, navigate to correct route --- src/CONST.ts | 1 + src/components/AttachmentModal.js | 2 +- src/pages/EditRequestPage.js | 9 +++++++++ src/pages/EditRequestReceiptPage.js | 9 ++++++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1c67535e54e4..7cecf1a02a11 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1315,6 +1315,7 @@ const CONST = { DATE: 'date', DESCRIPTION: 'description', MERCHANT: 'merchant', + RECEIPT: 'receipt', }, FOOTER: { EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`, diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 56183aa20643..7e86abbd4d05 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -362,7 +362,7 @@ function AttachmentModal(props) { // TODO: remove timeout setTimeout(() => { - Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, 'receipt')); + Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT)); }, 1000) }, }, diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 83b0019315e4..098379e20d42 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -15,6 +15,7 @@ import EditRequestDescriptionPage from './EditRequestDescriptionPage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestAmountPage from './EditRequestAmountPage'; +import EditRequestReceiptPage from './EditRequestReceiptPage'; import reportPropTypes from './reportPropTypes'; import * as IOU from '../libs/actions/IOU'; import * as CurrencyUtils from '../libs/CurrencyUtils'; @@ -168,6 +169,14 @@ function EditRequestPage({report, route, parentReport, policy, session}) { ); } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT) { + return ( + {}} + /> + ); + } + return null; } diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index fd0f4893bb3a..ea59fdae3522 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -1,10 +1,16 @@ import React from 'react'; +import PropTypes from 'prop-types'; import ScreenWrapper from '../components/ScreenWrapper'; import HeaderWithBackButton from '../components/HeaderWithBackButton'; import Navigation from '../libs/Navigation/Navigation'; import useLocalize from '../hooks/useLocalize'; import ReceiptSelector from './iou/ReceiptSelector'; +const propTypes = { + /** The callback fired when we confirm to replace the receipt */ + onSubmit: PropTypes.func.isRequired, +} + function EditRequestReceiptPage() { const {translate} = useLocalize(); @@ -14,7 +20,7 @@ function EditRequestReceiptPage() { shouldEnableMaxHeight > @@ -22,6 +28,7 @@ function EditRequestReceiptPage() { ); } +EditRequestReceiptPage.propTypes = propTypes; EditRequestReceiptPage.displayName = 'EditRequestReceiptPage'; export default EditRequestReceiptPage; From 945d500c073f639195880c3ea9cd7be8b51c2dd5 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 16:10:27 -0600 Subject: [PATCH 13/28] rm double onyx usage --- src/pages/EditRequestPage.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 098379e20d42..456ef0f7172b 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -189,8 +189,6 @@ export default compose( report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`, }, - }), - withOnyx({ parentReport: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`, }, From f4392a187b55c876d4699f9df7b9c39d38d626dc Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 16:55:09 -0600 Subject: [PATCH 14/28] add onyx back, add dropui provider --- src/components/AttachmentModal.js | 5 +---- src/components/ReportActionItem/ReportActionItemImage.js | 3 ++- src/pages/EditRequestPage.js | 2 ++ src/pages/EditRequestReceiptPage.js | 6 +++++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 7e86abbd4d05..e7a23bdf79e4 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -360,10 +360,7 @@ function AttachmentModal(props) { onSelected: () => { closeModal(); - // TODO: remove timeout - setTimeout(() => { - Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT)); - }, 1000) + // TODO: Navigate user to edit receipt page }, }, { diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js index 5f8444af0b21..36a51d1d7c7b 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.js +++ b/src/components/ReportActionItem/ReportActionItemImage.js @@ -55,7 +55,8 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal}) { { - const route = ROUTES.getReportAttachmentRoute(report.reportID, imageSource); + // const route = ROUTES.getReportAttachmentRoute(report.reportID, imageSource); + const route = ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT); Navigation.navigate(route); }} accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 456ef0f7172b..098379e20d42 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -189,6 +189,8 @@ export default compose( report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`, }, + }), + withOnyx({ parentReport: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`, }, diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index ea59fdae3522..55ab919f74bd 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -5,6 +5,8 @@ import HeaderWithBackButton from '../components/HeaderWithBackButton'; import Navigation from '../libs/Navigation/Navigation'; import useLocalize from '../hooks/useLocalize'; import ReceiptSelector from './iou/ReceiptSelector'; +import CONST from '../CONST'; +import DragAndDropProvider from '../components/DragAndDrop/Provider'; const propTypes = { /** The callback fired when we confirm to replace the receipt */ @@ -23,7 +25,9 @@ function EditRequestReceiptPage() { title={translate('common.receipt')} onBackButtonPress={Navigation.goBack} /> - + + + ); } From 125117bf80da4ebae9a6992511787babd8ca3c05 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 17:12:54 -0600 Subject: [PATCH 15/28] add props --- src/components/AttachmentModal.js | 2 -- .../ReportActionItem/ReportActionItemImage.js | 3 +-- src/pages/EditRequestPage.js | 3 ++- src/pages/EditRequestReceiptPage.js | 17 ++++++++++++++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index e7a23bdf79e4..82c58eed752c 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -28,8 +28,6 @@ import reportPropTypes from '../pages/reportPropTypes'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import * as Expensicons from './Icon/Expensicons'; import useWindowDimensions from '../hooks/useWindowDimensions'; -import Navigation from '../libs/Navigation/Navigation'; -import ROUTES from '../ROUTES'; /** * Modal render prop component that exposes modal launching triggers that can be used diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js index 36a51d1d7c7b..5f8444af0b21 100644 --- a/src/components/ReportActionItem/ReportActionItemImage.js +++ b/src/components/ReportActionItem/ReportActionItemImage.js @@ -55,8 +55,7 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal}) { { - // const route = ROUTES.getReportAttachmentRoute(report.reportID, imageSource); - const route = ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.AMOUNT); + const route = ROUTES.getReportAttachmentRoute(report.reportID, imageSource); Navigation.navigate(route); }} accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 098379e20d42..c061d1b2331a 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -172,7 +172,8 @@ function EditRequestPage({report, route, parentReport, policy, session}) { if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT) { return ( {}} + route={route} + replaceReceipt={(transactionID, file) => IOU.replaceReceipt(transactionID, file)} /> ); } diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index 55ab919f74bd..7cdd39eb8d56 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -5,15 +5,26 @@ import HeaderWithBackButton from '../components/HeaderWithBackButton'; import Navigation from '../libs/Navigation/Navigation'; import useLocalize from '../hooks/useLocalize'; import ReceiptSelector from './iou/ReceiptSelector'; -import CONST from '../CONST'; import DragAndDropProvider from '../components/DragAndDrop/Provider'; 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, + + /** The report ID of the IOU */ + reportID: PropTypes.string, + }), + }).isRequired, + /** The callback fired when we confirm to replace the receipt */ onSubmit: PropTypes.func.isRequired, } -function EditRequestReceiptPage() { +function EditRequestReceiptPage({route, onSubmit}) { const {translate} = useLocalize(); return ( @@ -26,7 +37,7 @@ function EditRequestReceiptPage() { onBackButtonPress={Navigation.goBack} /> - + ); From 95fc8c23b64c3d26d97de53749d30751846dc596 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 17:20:13 -0600 Subject: [PATCH 16/28] pass replaceReceipt callback to ReceiptSelector --- src/pages/EditRequestPage.js | 2 +- src/pages/EditRequestReceiptPage.js | 6 ++-- src/pages/iou/ReceiptSelector/index.js | 10 +++++++ src/pages/iou/ReceiptSelector/index.native.js | 30 ++++++++++++++----- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index c061d1b2331a..cb2223433bfc 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -173,7 +173,7 @@ function EditRequestPage({report, route, parentReport, policy, session}) { return ( IOU.replaceReceipt(transactionID, file)} + replaceReceipt={(file) => IOU.replaceReceipt(transaction.transactionID, file)} /> ); } diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index 7cdd39eb8d56..186247ab6e39 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -21,10 +21,10 @@ const propTypes = { }).isRequired, /** The callback fired when we confirm to replace the receipt */ - onSubmit: PropTypes.func.isRequired, + replaceReceipt: PropTypes.func.isRequired, } -function EditRequestReceiptPage({route, onSubmit}) { +function EditRequestReceiptPage({route, replaceReceipt}) { const {translate} = useLocalize(); return ( @@ -37,7 +37,7 @@ function EditRequestReceiptPage({route, onSubmit}) { onBackButtonPress={Navigation.goBack} /> - + ); diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js index 94246b1e6fd1..86b03ad7737b 100644 --- a/src/pages/iou/ReceiptSelector/index.js +++ b/src/pages/iou/ReceiptSelector/index.js @@ -47,6 +47,9 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, + + /** Callback to fire if we're replacing the existing receipt */ + replaceReceipt: PropTypes.func, }; const defaultProps = { @@ -57,6 +60,7 @@ const defaultProps = { }, report: {}, iou: iouDefaultProps, + replaceReceipt: null, }; function ReceiptSelector(props) { @@ -83,6 +87,12 @@ function ReceiptSelector(props) { const filePath = URL.createObjectURL(file); IOU.setMoneyRequestReceipt(filePath, file.name); + + if (props.replaceReceipt) { + props.replaceReceipt(file); + return; + } + IOU.navigateToNextPage(iou, iouType, reportID, report); }; diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index f012905667c7..f0c597d35f9e 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -40,11 +40,15 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, + + /** Callback to fire if we're replacing the existing receipt */ + replaceReceipt: PropTypes.func, }; const defaultProps = { report: {}, iou: iouDefaultProps, + replaceReceipt: null, }; /** @@ -72,7 +76,7 @@ function getImagePickerOptions(type) { }; } -function ReceiptSelector(props) { +function ReceiptSelector({route, report, iou, replaceReceipt}) { const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; @@ -81,9 +85,9 @@ function ReceiptSelector(props) { const [permissions, setPermissions] = useState('authorized'); const appState = useRef(AppState.currentState); - const iouType = lodashGet(props.route, 'params.iouType', ''); - const reportID = lodashGet(props.route, 'params.reportID', ''); - const pageIndex = lodashGet(props.route, 'params.pageIndex', 1); + const iouType = lodashGet(route, 'params.iouType', ''); + const reportID = lodashGet(route, 'params.reportID', ''); + const pageIndex = lodashGet(route, 'params.pageIndex', 1); const {translate} = useLocalize(); @@ -190,13 +194,19 @@ function ReceiptSelector(props) { }) .then((photo) => { IOU.setMoneyRequestReceipt(`file://${photo.path}`, photo.path); - IOU.navigateToNextPage(props.iou, iouType, reportID, props.report); + + if (replaceReceipt) { + replaceReceipt(photo); + return; + } + + IOU.navigateToNextPage(iou, iouType, reportID, report); }) .catch((error) => { showCameraAlert(); Log.warn('Error taking photo', error); }); - }, [flash, iouType, props.iou, props.report, reportID, translate]); + }, [flash, iouType, iou, report, replaceReceipt, reportID, translate]); Camera.getCameraPermissionStatus().then((permissionStatus) => { setPermissions(permissionStatus); @@ -255,7 +265,13 @@ function ReceiptSelector(props) { showImagePicker(launchImageLibrary) .then((receiptImage) => { IOU.setMoneyRequestReceipt(receiptImage[0].uri, receiptImage[0].fileName); - IOU.navigateToNextPage(props.iou, iouType, reportID, props.report); + + if (replaceReceipt) { + replaceReceipt(receiptImage); + return; + } + + IOU.navigateToNextPage(iou, iouType, reportID, report); }) .catch(() => { Log.info('User did not select an image from gallery'); From 23acf54f74dbb15a409fe69e5cfbc4375784f8ce Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 17:33:18 -0600 Subject: [PATCH 17/28] create IOU.replaceReceipt --- src/libs/actions/IOU.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b4f04174c1ac..59a1aacfa37a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1764,6 +1764,42 @@ function payMoneyRequest(paymentType, chatReport, iouReport) { } } +/** + * @param {String} transactionID + * @param {Object} receipt + */ +function replaceReceipt(transactionID, receipt) { + const transaction = lodashGet(allTransactions, 'transactionID', {}); + const oldReceipt = lodashGet(transaction, 'receipt', {}); + + const optimisticData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + receipt: { + source: receipt.source, + state: CONST.IOU.RECEIPT_STATE.SCANREADY, + }, + filename: receipt.filename, + }, + }, + ]; + + const failureData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + receipt: oldReceipt, + filename: transaction.filename, + }, + }, + ]; + + API.write('ReplaceReceipt', {transactionID, receipt}, {optimisticData, failureData}); +} + /** * Initialize money request info and navigate to the MoneyRequest page * @param {String} iouType @@ -1898,4 +1934,5 @@ export { setMoneyRequestReceipt, createEmptyTransaction, navigateToNextPage, + replaceReceipt, }; From 69ac668a3463b05734c42b894b8dd16f351f6ce4 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 5 Sep 2023 17:41:16 -0600 Subject: [PATCH 18/28] dismiss modal --- src/pages/EditRequestPage.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index cb2223433bfc..634cd247512f 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -173,7 +173,10 @@ function EditRequestPage({report, route, parentReport, policy, session}) { return ( IOU.replaceReceipt(transaction.transactionID, file)} + replaceReceipt={(file) => { + IOU.replaceReceipt(transaction.transactionID, file); + Navigation.dismissModal(); + }} /> ); } From 24b38e6710177378426909955d47694ac3168291 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 11 Sep 2023 10:59:48 +0800 Subject: [PATCH 19/28] resolve navigation issue --- src/components/AttachmentModal.js | 17 +++++++++++++---- src/pages/home/report/ReportAttachments.js | 5 ++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 82c58eed752c..b64a5abd8c3c 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback} from 'react'; +import React, {useState, useCallback, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, Animated, Keyboard} from 'react-native'; import Str from 'expensify-common/lib/str'; @@ -28,6 +28,8 @@ import reportPropTypes from '../pages/reportPropTypes'; import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; import * as Expensicons from './Icon/Expensicons'; import useWindowDimensions from '../hooks/useWindowDimensions'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -96,6 +98,7 @@ const defaultProps = { }; function AttachmentModal(props) { + const onModalHideCallbackRef = useRef(null); const [isModalOpen, setIsModalOpen] = useState(props.defaultOpen); const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false); const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); @@ -334,7 +337,14 @@ function AttachmentModal(props) { setShouldLoadAttachment(true); }} onModalHide={(e) => { - props.onModalHide(e); + props.onModalHide(e) + .then(() => { + if (!onModalHideCallbackRef.current && !_.isFunction(onModalHideCallbackRef.current)) { + return; + } + + onModalHideCallbackRef.current(); + }) setShouldLoadAttachment(false); }} propagateSwipe @@ -356,9 +366,8 @@ function AttachmentModal(props) { icon: Expensicons.Camera, text: props.translate('common.replace'), onSelected: () => { + onModalHideCallbackRef.current = () => Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT)); closeModal(); - - // TODO: Navigate user to edit receipt page }, }, { diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js index c8b5dd1ae685..7e6b71a13744 100644 --- a/src/pages/home/report/ReportAttachments.js +++ b/src/pages/home/report/ReportAttachments.js @@ -30,7 +30,10 @@ function ReportAttachments(props) { defaultOpen report={report} source={source} - onModalHide={() => Navigation.dismissModal(reportID)} + onModalHide={() => new Promise(resolve => { + Navigation.dismissModal(reportID); + resolve(); + })} onCarouselAttachmentChange={(attachment) => { const route = ROUTES.getReportAttachmentRoute(reportID, attachment.source); Navigation.navigate(route); From 43c08f55d14eda84d93b59297a469264ca24616a Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 11 Sep 2023 11:06:02 +0800 Subject: [PATCH 20/28] use correct file path and name --- src/libs/actions/IOU.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 59a1aacfa37a..77a275f9aced 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1771,17 +1771,17 @@ function payMoneyRequest(paymentType, chatReport, iouReport) { function replaceReceipt(transactionID, receipt) { const transaction = lodashGet(allTransactions, 'transactionID', {}); const oldReceipt = lodashGet(transaction, 'receipt', {}); - + const filePath = URL.createObjectURL(receipt); const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: { receipt: { - source: receipt.source, + source: filePath, state: CONST.IOU.RECEIPT_STATE.SCANREADY, }, - filename: receipt.filename, + filename: receipt.name, }, }, ]; From 435bb619e5b5d2e54657d94ca3dfdff4ad203912 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 11 Sep 2023 11:19:21 +0800 Subject: [PATCH 21/28] replace in open state --- src/libs/actions/IOU.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 77a275f9aced..2670d9a37b74 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1779,7 +1779,7 @@ function replaceReceipt(transactionID, receipt) { value: { receipt: { source: filePath, - state: CONST.IOU.RECEIPT_STATE.SCANREADY, + state: CONST.IOU.RECEIPT_STATE.OPEN, }, filename: receipt.name, }, From a1c8657d15aa4e2cbb57222ce792488a8f468a54 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 11 Sep 2023 13:37:32 +0800 Subject: [PATCH 22/28] fix styles --- src/components/AttachmentModal.js | 15 +++++++-------- src/libs/actions/IOU.js | 4 ++-- src/pages/EditRequestReceiptPage.js | 7 +++++-- src/pages/home/report/ReportAttachments.js | 10 ++++++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index b64a5abd8c3c..02d78efadfe1 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -337,14 +337,13 @@ function AttachmentModal(props) { setShouldLoadAttachment(true); }} onModalHide={(e) => { - props.onModalHide(e) - .then(() => { - if (!onModalHideCallbackRef.current && !_.isFunction(onModalHideCallbackRef.current)) { - return; - } - - onModalHideCallbackRef.current(); - }) + props.onModalHide(e).then(() => { + if (!onModalHideCallbackRef.current && !_.isFunction(onModalHideCallbackRef.current)) { + return; + } + + onModalHideCallbackRef.current(); + }); setShouldLoadAttachment(false); }} propagateSwipe diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f02797b97ebe..4871dd494191 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1780,8 +1780,8 @@ function payMoneyRequest(paymentType, chatReport, iouReport) { } /** - * @param {String} transactionID - * @param {Object} receipt + * @param {String} transactionID + * @param {Object} receipt */ function replaceReceipt(transactionID, receipt) { const transaction = lodashGet(allTransactions, 'transactionID', {}); diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index 186247ab6e39..0ee7ebd5f82b 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -22,7 +22,7 @@ const propTypes = { /** The callback fired when we confirm to replace the receipt */ replaceReceipt: PropTypes.func.isRequired, -} +}; function EditRequestReceiptPage({route, replaceReceipt}) { const {translate} = useLocalize(); @@ -37,7 +37,10 @@ function EditRequestReceiptPage({route, replaceReceipt}) { onBackButtonPress={Navigation.goBack} /> - + ); diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js index 7e6b71a13744..1e232be0148d 100644 --- a/src/pages/home/report/ReportAttachments.js +++ b/src/pages/home/report/ReportAttachments.js @@ -30,10 +30,12 @@ function ReportAttachments(props) { defaultOpen report={report} source={source} - onModalHide={() => new Promise(resolve => { - Navigation.dismissModal(reportID); - resolve(); - })} + onModalHide={() => + new Promise((resolve) => { + Navigation.dismissModal(reportID); + resolve(); + }) + } onCarouselAttachmentChange={(attachment) => { const route = ROUTES.getReportAttachmentRoute(reportID, attachment.source); Navigation.navigate(route); From fda0cee2a80d5195ead61ed88ced96ebdef59a54 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 12 Sep 2023 12:34:18 +0800 Subject: [PATCH 23/28] update early return logic --- src/components/AttachmentModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 02d78efadfe1..d42a4d52e4d5 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -338,7 +338,7 @@ function AttachmentModal(props) { }} onModalHide={(e) => { props.onModalHide(e).then(() => { - if (!onModalHideCallbackRef.current && !_.isFunction(onModalHideCallbackRef.current)) { + if (!onModalHideCallbackRef.current) { return; } From 424ac340537bec5a8ce65481d62e8b6739b41e98 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 13 Sep 2023 16:41:23 +0800 Subject: [PATCH 24/28] rm promise --- src/components/AttachmentModal.js | 10 ++++------ src/pages/home/report/ReportAttachments.js | 7 +------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index d42a4d52e4d5..610b48e420b9 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -337,13 +337,11 @@ function AttachmentModal(props) { setShouldLoadAttachment(true); }} onModalHide={(e) => { - props.onModalHide(e).then(() => { - if (!onModalHideCallbackRef.current) { - return; - } - + props.onModalHide(e); + if (onModalHideCallbackRef.current) { onModalHideCallbackRef.current(); - }); + } + setShouldLoadAttachment(false); }} propagateSwipe diff --git a/src/pages/home/report/ReportAttachments.js b/src/pages/home/report/ReportAttachments.js index 1e232be0148d..c8b5dd1ae685 100644 --- a/src/pages/home/report/ReportAttachments.js +++ b/src/pages/home/report/ReportAttachments.js @@ -30,12 +30,7 @@ function ReportAttachments(props) { defaultOpen report={report} source={source} - onModalHide={() => - new Promise((resolve) => { - Navigation.dismissModal(reportID); - resolve(); - }) - } + onModalHide={() => Navigation.dismissModal(reportID)} onCarouselAttachmentChange={(attachment) => { const route = ROUTES.getReportAttachmentRoute(reportID, attachment.source); Navigation.navigate(route); From 164b1292390ba7b852b12167cd35ec6987d945aa Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 15 Sep 2023 09:51:07 +0800 Subject: [PATCH 25/28] refactor usage to build filepath differently on native --- src/libs/actions/IOU.js | 5 ++-- src/pages/EditRequestPage.js | 5 +--- src/pages/EditRequestReceiptPage.js | 9 ++++--- src/pages/iou/ReceiptSelector/index.js | 16 ++++++++---- src/pages/iou/ReceiptSelector/index.native.js | 25 ++++++++++++------- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 0a8fa9235330..46399cf332e0 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1827,11 +1827,12 @@ function payMoneyRequest(paymentType, chatReport, iouReport) { /** * @param {String} transactionID * @param {Object} receipt + * @param {String} filePath */ -function replaceReceipt(transactionID, receipt) { +function replaceReceipt(transactionID, receipt, filePath) { const transaction = lodashGet(allTransactions, 'transactionID', {}); const oldReceipt = lodashGet(transaction, 'receipt', {}); - const filePath = URL.createObjectURL(receipt); + const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 64bf2bf8857d..7528fcbcadbd 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -174,10 +174,7 @@ function EditRequestPage({report, route, parentReport, policy, session}) { return ( { - IOU.replaceReceipt(transaction.transactionID, file); - Navigation.dismissModal(); - }} + transactionID={transaction.transactionID} /> ); } diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index 0ee7ebd5f82b..601c74f8df7e 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -20,11 +20,11 @@ const propTypes = { }), }).isRequired, - /** The callback fired when we confirm to replace the receipt */ - replaceReceipt: PropTypes.func.isRequired, + /** The id of the transaction we're editing */ + transactionID: PropTypes.string.isRequired, }; -function EditRequestReceiptPage({route, replaceReceipt}) { +function EditRequestReceiptPage({route, transactionID}) { const {translate} = useLocalize(); return ( @@ -39,7 +39,8 @@ function EditRequestReceiptPage({route, replaceReceipt}) { diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js index b7cd71e79b60..34ef0a92789a 100644 --- a/src/pages/iou/ReceiptSelector/index.js +++ b/src/pages/iou/ReceiptSelector/index.js @@ -21,6 +21,7 @@ import useLocalize from '../../../hooks/useLocalize'; import {DragAndDropContext} from '../../../components/DragAndDrop/Provider'; import * as ReceiptUtils from '../../../libs/ReceiptUtils'; import {iouPropTypes, iouDefaultProps} from '../propTypes'; +import Navigation from '../../../libs/Navigation/Navigation'; const propTypes = { /** Information shown to the user when a receipt is not valid */ @@ -48,8 +49,11 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, - /** Callback to fire if we're replacing the existing receipt */ - replaceReceipt: PropTypes.func, + /** Whether the user is replacing the receipt */ + isReplacingReceipt: PropTypes.bool, + + /** The id of the transaction we're editing */ + transactionID: PropTypes.string, }; const defaultProps = { @@ -60,7 +64,8 @@ const defaultProps = { }, report: {}, iou: iouDefaultProps, - replaceReceipt: null, + isReplacingReceipt: false, + transactionID: '', }; function ReceiptSelector(props) { @@ -88,8 +93,9 @@ function ReceiptSelector(props) { const filePath = URL.createObjectURL(file); IOU.setMoneyRequestReceipt(filePath, file.name); - if (props.replaceReceipt) { - props.replaceReceipt(file); + if (props.isReplacingReceipt) { + IOU.replaceReceipt(props.transactionID, file, filePath); + Navigation.dismissModal(); return; } diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index a2a3869f1127..42f7242e7ece 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -23,6 +23,7 @@ import Log from '../../../libs/Log'; import * as CameraPermission from './CameraPermission'; import {iouPropTypes, iouDefaultProps} from '../propTypes'; import NavigationAwareCamera from './NavigationAwareCamera'; +import Navigation from '../../../libs/Navigation/Navigation'; const propTypes = { /** React Navigation route */ @@ -43,14 +44,18 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, - /** Callback to fire if we're replacing the existing receipt */ - replaceReceipt: PropTypes.func, + /** Whether the user is replacing the receipt */ + isReplacingReceipt: PropTypes.bool, + + /** The id of the transaction we're editing */ + transactionID: PropTypes.string, }; const defaultProps = { report: {}, iou: iouDefaultProps, - replaceReceipt: null, + isReplacingReceipt: false, + transactionID: '', }; /** @@ -78,7 +83,7 @@ function getImagePickerOptions(type) { }; } -function ReceiptSelector({route, report, iou, replaceReceipt}) { +function ReceiptSelector({route, report, iou, isReplacingReceipt, transactionID}) { const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; @@ -201,8 +206,9 @@ function ReceiptSelector({route, report, iou, replaceReceipt}) { .then((photo) => { IOU.setMoneyRequestReceipt(`file://${photo.path}`, photo.path); - if (replaceReceipt) { - replaceReceipt(photo); + if (isReplacingReceipt) { + IOU.replaceReceipt(transactionID, photo, `file://${photo.path}`); + Navigation.dismissModal(); return; } @@ -212,7 +218,7 @@ function ReceiptSelector({route, report, iou, replaceReceipt}) { showCameraAlert(); Log.warn('Error taking photo', error); }); - }, [flash, iouType, iou, report, replaceReceipt, reportID, translate]); + }, [flash, iouType, iou, report, reportID, translate, transactionID, isReplacingReceipt]); CameraPermission.getCameraPermissionStatus().then((permissionStatus) => { setPermissions(permissionStatus); @@ -272,8 +278,9 @@ function ReceiptSelector({route, report, iou, replaceReceipt}) { .then((receiptImage) => { IOU.setMoneyRequestReceipt(receiptImage[0].uri, receiptImage[0].fileName); - if (replaceReceipt) { - replaceReceipt(receiptImage); + if (isReplacingReceipt) { + IOU.replaceReceipt(transactionID, receiptImage, receiptImage[0].uri); + Navigation.dismissModal(); return; } From b81a15912667efe1bde1fb93f11ea759c31e86aa Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Fri, 15 Sep 2023 09:56:03 +0800 Subject: [PATCH 26/28] refactor props --- src/pages/EditRequestReceiptPage.js | 1 - src/pages/iou/ReceiptSelector/index.js | 6 +----- src/pages/iou/ReceiptSelector/index.native.js | 12 ++++-------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/pages/EditRequestReceiptPage.js b/src/pages/EditRequestReceiptPage.js index 601c74f8df7e..47aa23a93432 100644 --- a/src/pages/EditRequestReceiptPage.js +++ b/src/pages/EditRequestReceiptPage.js @@ -40,7 +40,6 @@ function EditRequestReceiptPage({route, transactionID}) { diff --git a/src/pages/iou/ReceiptSelector/index.js b/src/pages/iou/ReceiptSelector/index.js index 34ef0a92789a..a94a1e3783c8 100644 --- a/src/pages/iou/ReceiptSelector/index.js +++ b/src/pages/iou/ReceiptSelector/index.js @@ -49,9 +49,6 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, - /** Whether the user is replacing the receipt */ - isReplacingReceipt: PropTypes.bool, - /** The id of the transaction we're editing */ transactionID: PropTypes.string, }; @@ -64,7 +61,6 @@ const defaultProps = { }, report: {}, iou: iouDefaultProps, - isReplacingReceipt: false, transactionID: '', }; @@ -93,7 +89,7 @@ function ReceiptSelector(props) { const filePath = URL.createObjectURL(file); IOU.setMoneyRequestReceipt(filePath, file.name); - if (props.isReplacingReceipt) { + if (props.transactionID) { IOU.replaceReceipt(props.transactionID, file, filePath); Navigation.dismissModal(); return; diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 42f7242e7ece..66c6106dbf3d 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -44,9 +44,6 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, - /** Whether the user is replacing the receipt */ - isReplacingReceipt: PropTypes.bool, - /** The id of the transaction we're editing */ transactionID: PropTypes.string, }; @@ -54,7 +51,6 @@ const propTypes = { const defaultProps = { report: {}, iou: iouDefaultProps, - isReplacingReceipt: false, transactionID: '', }; @@ -83,7 +79,7 @@ function getImagePickerOptions(type) { }; } -function ReceiptSelector({route, report, iou, isReplacingReceipt, transactionID}) { +function ReceiptSelector({route, report, iou, transactionID}) { const devices = useCameraDevices('wide-angle-camera'); const device = devices.back; @@ -206,7 +202,7 @@ function ReceiptSelector({route, report, iou, isReplacingReceipt, transactionID} .then((photo) => { IOU.setMoneyRequestReceipt(`file://${photo.path}`, photo.path); - if (isReplacingReceipt) { + if (transactionID) { IOU.replaceReceipt(transactionID, photo, `file://${photo.path}`); Navigation.dismissModal(); return; @@ -218,7 +214,7 @@ function ReceiptSelector({route, report, iou, isReplacingReceipt, transactionID} showCameraAlert(); Log.warn('Error taking photo', error); }); - }, [flash, iouType, iou, report, reportID, translate, transactionID, isReplacingReceipt]); + }, [flash, iouType, iou, report, reportID, translate, transactionID]); CameraPermission.getCameraPermissionStatus().then((permissionStatus) => { setPermissions(permissionStatus); @@ -278,7 +274,7 @@ function ReceiptSelector({route, report, iou, isReplacingReceipt, transactionID} .then((receiptImage) => { IOU.setMoneyRequestReceipt(receiptImage[0].uri, receiptImage[0].fileName); - if (isReplacingReceipt) { + if (transactionID) { IOU.replaceReceipt(transactionID, receiptImage, receiptImage[0].uri); Navigation.dismissModal(); return; From 72eca6da2c5f5429986e766dca76ac8198fa008f Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 18 Sep 2023 14:36:44 +0800 Subject: [PATCH 27/28] fix file select --- src/pages/iou/ReceiptSelector/index.native.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 66c6106dbf3d..427dfc542910 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -24,6 +24,7 @@ import * as CameraPermission from './CameraPermission'; import {iouPropTypes, iouDefaultProps} from '../propTypes'; import NavigationAwareCamera from './NavigationAwareCamera'; import Navigation from '../../../libs/Navigation/Navigation'; +import * as FileUtils from '../../../libs/fileDownload/FileUtils'; const propTypes = { /** React Navigation route */ @@ -272,10 +273,13 @@ function ReceiptSelector({route, report, iou, transactionID}) { onPress={() => { showImagePicker(launchImageLibrary) .then((receiptImage) => { - IOU.setMoneyRequestReceipt(receiptImage[0].uri, receiptImage[0].fileName); + const filePath = receiptImage[0].uri; + IOU.setMoneyRequestReceipt(filePath, receiptImage[0].fileName); if (transactionID) { - IOU.replaceReceipt(transactionID, receiptImage, receiptImage[0].uri); + FileUtils.readFileAsync(filePath, receiptImage[0].fileName).then((receipt) => { + IOU.replaceReceipt(transactionID, receipt, filePath); + }); Navigation.dismissModal(); return; } From 39236e3d93223089a05352b6bdc5b573cbe3d19e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 18 Sep 2023 14:42:12 +0800 Subject: [PATCH 28/28] read photo file --- src/pages/iou/ReceiptSelector/index.native.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 427dfc542910..36f883ad08e5 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -201,10 +201,14 @@ function ReceiptSelector({route, report, iou, transactionID}) { flash: flash ? 'on' : 'off', }) .then((photo) => { - IOU.setMoneyRequestReceipt(`file://${photo.path}`, photo.path); + const filePath = `file://${photo.path}`; + IOU.setMoneyRequestReceipt(filePath, photo.path); if (transactionID) { - IOU.replaceReceipt(transactionID, photo, `file://${photo.path}`); + FileUtils.readFileAsync(filePath, photo.path).then((receipt) => { + IOU.replaceReceipt(transactionID, receipt, filePath); + }); + Navigation.dismissModal(); return; }