diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 6c92e2cf29bb..f82fec156f9f 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -117,14 +117,14 @@ function AttachmentModal(props) { const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); const [isDeleteReceiptConfirmModalVisible, setIsDeleteReceiptConfirmModalVisible] = useState(false); const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired); - const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(false); + const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(null); const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState(''); const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null); const [source, setSource] = useState(props.source); const [modalType, setModalType] = useState(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE); const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false); const [confirmButtonFadeAnimation] = useState(() => new Animated.Value(1)); - const [shouldShowDownloadButton, setShouldShowDownloadButton] = React.useState(true); + const [isDownloadButtonReadyToBeShown, setIsDownloadButtonReadyToBeShown] = React.useState(true); const {windowWidth} = useWindowDimensions(); const [file, setFile] = useState( @@ -173,13 +173,13 @@ function AttachmentModal(props) { ); const setDownloadButtonVisibility = useCallback( - (shouldShowButton) => { - if (shouldShowDownloadButton === shouldShowButton) { + (isButtonVisible) => { + if (isDownloadButtonReadyToBeShown === isButtonVisible) { return; } - setShouldShowDownloadButton(shouldShowButton); + setIsDownloadButtonReadyToBeShown(isButtonVisible); }, - [shouldShowDownloadButton], + [isDownloadButtonReadyToBeShown], ); /** @@ -393,6 +393,17 @@ function AttachmentModal(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]); + // There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment. + // isAttachmentReceipt will be null until its certain what the file is, in which case it will then be true|false. + let headerTitle = props.headerTitle; + let shouldShowDownloadButton = false; + let shouldShowThreeDotsButton = false; + if (!_.isNull(isAttachmentReceipt)) { + headerTitle = translate(isAttachmentReceipt ? 'common.receipt' : 'common.attachment'); + shouldShowDownloadButton = props.allowDownload && isDownloadButtonReadyToBeShown && !isAttachmentReceipt && !isOffline; + shouldShowThreeDotsButton = isAttachmentReceipt && isModalOpen; + } + return ( <> {props.isSmallScreenWidth && } downloadAttachment(source)} shouldShowCloseButton={!props.isSmallScreenWidth} shouldShowBackButton={props.isSmallScreenWidth} onBackButtonPress={closeModal} onCloseButtonPress={closeModal} - shouldShowThreeDotsButton={isAttachmentReceipt && isModalOpen} + shouldShowThreeDotsButton={shouldShowThreeDotsButton} threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)} threeDotsMenuItems={threeDotsMenuItems} shouldOverlay diff --git a/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js b/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js index 8048773de42c..7543e8d9c099 100644 --- a/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js +++ b/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js @@ -1,4 +1,5 @@ import PropTypes from 'prop-types'; +import transactionPropTypes from '@components/transactionPropTypes'; import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; import reportPropTypes from '@pages/reportPropTypes'; @@ -16,15 +17,27 @@ const propTypes = { setDownloadButtonVisibility: PropTypes.func, /** Object of report actions for this report */ - reportActions: PropTypes.shape(reportActionPropTypes), + reportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), /** The report currently being looked at */ report: reportPropTypes.isRequired, + + /** The parent of `report` */ + parentReport: reportPropTypes, + + /** The report actions of the parent report */ + parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), + + /** The transaction attached to the parent report action */ + transaction: transactionPropTypes, }; const defaultProps = { source: '', reportActions: {}, + parentReport: {}, + parentReportActions: {}, + transaction: {}, onNavigate: () => {}, onClose: () => {}, setDownloadButtonVisibility: () => {}, diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js index 6f0dd335c2bb..28af6b511641 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js +++ b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js @@ -9,12 +9,13 @@ import CONST from '@src/CONST'; /** * Constructs the initial component state from report actions - * @param {Object} report - * @param {Array} reportActions + * @param {Object} parentReportAction + * @param {Object} reportActions + * @param {Object} transaction * @returns {Array} */ -function extractAttachmentsFromReport(report, reportActions) { - const actions = [ReportActionsUtils.getParentReportAction(report), ...ReportActionsUtils.getSortedReportActions(_.values(reportActions))]; +function extractAttachmentsFromReport(parentReportAction, reportActions, transaction) { + const actions = [parentReportAction, ...ReportActionsUtils.getSortedReportActions(_.values(reportActions))]; const attachments = []; const htmlParser = new HtmlParser({ @@ -51,7 +52,6 @@ function extractAttachmentsFromReport(report, reportActions) { return; } - const transaction = TransactionUtils.getTransaction(transactionID); if (TransactionUtils.hasReceipt(transaction)) { const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction); const isLocalFile = typeof image === 'string' && (image.startsWith('blob:') || image.startsWith('file:')); diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js index 7d0500433377..c489f554edd4 100644 --- a/src/components/Attachments/AttachmentCarousel/index.js +++ b/src/components/Attachments/AttachmentCarousel/index.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {FlatList, Keyboard, PixelRatio, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -9,7 +10,6 @@ import withWindowDimensions from '@components/withWindowDimensions'; import compose from '@libs/compose'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import styles from '@styles/styles'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -27,7 +27,7 @@ const viewabilityConfig = { itemVisiblePercentThreshold: 95, }; -function AttachmentCarousel({report, reportActions, source, onNavigate, setDownloadButtonVisibility, translate}) { +function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, transaction}) { const scrollRef = useRef(null); const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); @@ -42,17 +42,16 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl const compareImage = useCallback( (attachment) => { if (attachment.isReceipt && isReceipt) { - const action = ReportActionsUtils.getParentReportAction(report); - const transactionID = _.get(action, ['originalMessage', 'IOUTransactionID']); - return attachment.transactionID === transactionID; + return attachment.transactionID === transaction.transactionID; } return attachment.source === source; }, - [source, report, isReceipt], + [source, isReceipt, transaction], ); useEffect(() => { - const attachmentsFromReport = extractAttachmentsFromReport(report, reportActions); + const parentReportAction = parentReportActions[report.parentReportActionID]; + const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions, transaction); const initialPage = _.findIndex(attachmentsFromReport, compareImage); @@ -72,7 +71,7 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [reportActions, compareImage]); + }, [reportActions, parentReportActions, compareImage]); /** * Updates the page state when the user navigates between attachments @@ -226,11 +225,28 @@ AttachmentCarousel.defaultProps = defaultProps; AttachmentCarousel.displayName = 'AttachmentCarousel'; export default compose( + // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ reportActions: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, canEvict: false, }, + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`, + }, + parentReportActions: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, + canEvict: false, + }, + }), + // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file + withOnyx({ + transaction: { + key: ({report, parentReportActions}) => { + const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]); + return `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`; + }, + }, }), withLocalize, withWindowDimensions, diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js index b86c9b1c786e..1527b027db8d 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.js +++ b/src/components/Attachments/AttachmentCarousel/index.native.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {Keyboard, PixelRatio, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -7,7 +8,6 @@ import * as Illustrations from '@components/Icon/Illustrations'; import withLocalize from '@components/withLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import styles from '@styles/styles'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -18,7 +18,7 @@ import extractAttachmentsFromReport from './extractAttachmentsFromReport'; import AttachmentCarouselPager from './Pager'; import useCarouselArrows from './useCarouselArrows'; -function AttachmentCarousel({report, reportActions, source, onNavigate, onClose, setDownloadButtonVisibility, translate}) { +function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, transaction, onClose}) { const pagerRef = useRef(null); const [containerDimensions, setContainerDimensions] = useState({width: 0, height: 0}); @@ -32,17 +32,16 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, onClose, const compareImage = useCallback( (attachment) => { if (attachment.isReceipt && isReceipt) { - const action = ReportActionsUtils.getParentReportAction(report); - const transactionID = _.get(action, ['originalMessage', 'IOUTransactionID']); - return attachment.transactionID === transactionID; + return attachment.transactionID === transaction.transactionID; } return attachment.source === source; }, - [source, report, isReceipt], + [source, isReceipt, transaction], ); useEffect(() => { - const attachmentsFromReport = extractAttachmentsFromReport(report, reportActions); + const parentReportAction = parentReportActions[report.parentReportActionID]; + const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions, transaction); const initialPage = _.findIndex(attachmentsFromReport, compareImage); @@ -172,11 +171,28 @@ AttachmentCarousel.defaultProps = defaultProps; AttachmentCarousel.displayName = 'AttachmentCarousel'; export default compose( + // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ reportActions: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, canEvict: false, }, + parentReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`, + }, + parentReportActions: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, + canEvict: false, + }, + }), + // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file + withOnyx({ + transaction: { + key: ({report, parentReportActions}) => { + const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]); + return `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`; + }, + }, }), withLocalize, )(AttachmentCarousel);