diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 46d693a507dd..57bf3218abbc 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -25,6 +25,7 @@ import HeaderGap from './HeaderGap'; import SafeAreaConsumer from './SafeAreaConsumer'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import reportPropTypes from '../pages/reportPropTypes'; +import tryResolveUrlFromApiRoot from '../libs/tryResolveUrlFromApiRoot'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -103,6 +104,7 @@ function AttachmentModal(props) { 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 [file, setFile] = useState( props.originalFileName ? { @@ -142,6 +144,16 @@ function AttachmentModal(props) { [translate], ); + const setDownloadButtonVisibility = useCallback( + (shouldShowButton) => { + if (shouldShowDownloadButton === shouldShowButton) { + return; + } + setShouldShowDownloadButton(shouldShowButton); + }, + [shouldShowDownloadButton], + ); + /** * Download the currently viewed attachment. */ @@ -324,7 +336,7 @@ function AttachmentModal(props) { downloadAttachment(source)} shouldShowCloseButton={!props.isSmallScreenWidth} shouldShowBackButton={props.isSmallScreenWidth} @@ -336,9 +348,10 @@ function AttachmentModal(props) { ) : ( Boolean(sourceForAttachmentView) && diff --git a/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js b/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js index a63b0f23d1ab..81f22f684243 100644 --- a/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js +++ b/src/components/Attachments/AttachmentCarousel/attachmentCarouselPropTypes.js @@ -12,6 +12,9 @@ const propTypes = { /** Callback to close carousel when user swipes down (on native) */ onClose: PropTypes.func, + /** Function to change the download button Visibility */ + setDownloadButtonVisibility: PropTypes.func, + /** Object of report actions for this report */ reportActions: PropTypes.shape(reportActionPropTypes), @@ -24,6 +27,7 @@ const defaultProps = { reportActions: {}, onNavigate: () => {}, onClose: () => {}, + setDownloadButtonVisibility: () => {}, }; export {propTypes, defaultProps}; diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js index 047a016674b7..f4df0fc0c3e2 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js +++ b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js @@ -3,18 +3,16 @@ import _ from 'underscore'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; import CONST from '../../../CONST'; import tryResolveUrlFromApiRoot from '../../../libs/tryResolveUrlFromApiRoot'; -import Navigation from '../../../libs/Navigation/Navigation'; /** * Constructs the initial component state from report actions * @param {Object} report * @param {Array} reportActions - * @param {String} source - * @returns {{attachments: Array, initialPage: Number, initialItem: Object, initialActiveSource: String}} + * @returns {Array} */ -function extractAttachmentsFromReport(report, reportActions, source) { +function extractAttachmentsFromReport(report, reportActions) { const actions = [ReportActionsUtils.getParentReportAction(report), ...ReportActionsUtils.getSortedReportActions(_.values(reportActions))]; - let attachments = []; + const attachments = []; const htmlParser = new HtmlParser({ onopentag: (name, attribs) => { @@ -42,27 +40,7 @@ function extractAttachmentsFromReport(report, reportActions, source) { }); htmlParser.end(); - attachments = attachments.reverse(); - - const initialPage = _.findIndex(attachments, (a) => a.source === source); - if (initialPage === -1) { - Navigation.dismissModal(); - return { - attachments: [], - initialPage: 0, - initialItem: undefined, - initialActiveSource: null, - }; - } - - const initialItem = attachments[initialPage]; - - return { - attachments, - initialPage, - initialItem, - initialActiveSource: initialItem.source, - }; + return attachments.reverse(); } export default extractAttachmentsFromReport; diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js index 564e60b65dd1..cec5f54508cb 100644 --- a/src/components/Attachments/AttachmentCarousel/index.js +++ b/src/components/Attachments/AttachmentCarousel/index.js @@ -1,4 +1,4 @@ -import React, {useRef, useCallback, useState, useEffect, useMemo} from 'react'; +import React, {useRef, useCallback, useState, useEffect} from 'react'; import {View, FlatList, PixelRatio, Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -15,6 +15,10 @@ import withLocalize from '../../withLocalize'; import compose from '../../../libs/compose'; import useCarouselArrows from './useCarouselArrows'; import useWindowDimensions from '../../../hooks/useWindowDimensions'; +import Navigation from '../../../libs/Navigation/Navigation'; +import BlockingView from '../../BlockingViews/BlockingView'; +import * as Illustrations from '../../Icon/Illustrations'; +import variables from '../../../styles/variables'; const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); const viewabilityConfig = { @@ -23,24 +27,37 @@ const viewabilityConfig = { itemVisiblePercentThreshold: 95, }; -function AttachmentCarousel({report, reportActions, source, onNavigate}) { +function AttachmentCarousel({report, reportActions, source, onNavigate, setDownloadButtonVisibility, translate}) { const scrollRef = useRef(null); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); - const {attachments, initialPage, initialActiveSource, initialItem} = useMemo(() => extractAttachmentsFromReport(report, reportActions, source), [report, reportActions, source]); + const [containerWidth, setContainerWidth] = useState(0); + const [page, setPage] = useState(0); + const [attachments, setAttachments] = useState([]); + const [activeSource, setActiveSource] = useState(source); + const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows(); useEffect(() => { - // Update the parent modal's state with the source and name from the mapped attachments - if (!initialItem) return; - onNavigate(initialItem); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialItem]); + const attachmentsFromReport = extractAttachmentsFromReport(report, reportActions); - const [containerWidth, setContainerWidth] = useState(0); - const [page, setPage] = useState(initialPage); - const [activeSource, setActiveSource] = useState(initialActiveSource); - const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows(); + const initialPage = _.findIndex(attachmentsFromReport, (a) => a.source === source); + + // Dismiss the modal when deleting an attachment during its display in preview. + if (initialPage === -1 && _.find(attachments, (a) => a.source === source)) { + Navigation.dismissModal(); + } else { + setPage(initialPage); + setAttachments(attachmentsFromReport); + + // Update the download button visibility in the parent modal + setDownloadButtonVisibility(initialPage !== -1); + + // Update the parent modal's state with the source and name from the mapped attachments + if (!_.isUndefined(attachmentsFromReport[initialPage])) onNavigate(attachmentsFromReport[initialPage]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [report, reportActions, source]); /** * Updates the page state when the user navigates between attachments @@ -153,49 +170,60 @@ function AttachmentCarousel({report, reportActions, source, onNavigate}) { onMouseEnter={() => !canUseTouchScreen && setShouldShowArrows(true)} onMouseLeave={() => !canUseTouchScreen && setShouldShowArrows(false)} > - cycleThroughAttachments(-1)} - onForward={() => cycleThroughAttachments(1)} - autoHideArrow={autoHideArrows} - cancelAutoHideArrow={cancelAutoHideArrows} - /> - - {containerWidth > 0 && ( - item.source} - viewabilityConfig={viewabilityConfig} - onViewableItemsChanged={updatePage.current} + {page === -1 ? ( + + ) : ( + <> + cycleThroughAttachments(-1)} + onForward={() => cycleThroughAttachments(1)} + autoHideArrow={autoHideArrows} + cancelAutoHideArrow={cancelAutoHideArrows} + /> + + {containerWidth > 0 && ( + item.source} + viewabilityConfig={viewabilityConfig} + onViewableItemsChanged={updatePage.current} + /> + )} + + + )} - - ); } diff --git a/src/components/Attachments/AttachmentCarousel/index.native.js b/src/components/Attachments/AttachmentCarousel/index.native.js index 58e248d514e1..4162cfae88e9 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.js +++ b/src/components/Attachments/AttachmentCarousel/index.native.js @@ -1,6 +1,7 @@ -import React, {useCallback, useEffect, useRef, useState, useMemo} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View, Keyboard, PixelRatio} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import AttachmentCarouselPager from './Pager'; import styles from '../../../styles/styles'; import CarouselButtons from './CarouselButtons'; @@ -9,24 +10,44 @@ import ONYXKEYS from '../../../ONYXKEYS'; import {propTypes, defaultProps} from './attachmentCarouselPropTypes'; import extractAttachmentsFromReport from './extractAttachmentsFromReport'; import useCarouselArrows from './useCarouselArrows'; +import Navigation from '../../../libs/Navigation/Navigation'; +import BlockingView from '../../BlockingViews/BlockingView'; +import * as Illustrations from '../../Icon/Illustrations'; +import variables from '../../../styles/variables'; +import compose from '../../../libs/compose'; +import withLocalize from '../../withLocalize'; -function AttachmentCarousel({report, reportActions, source, onNavigate, onClose}) { - const {attachments, initialPage, initialActiveSource, initialItem} = useMemo(() => extractAttachmentsFromReport(report, reportActions, source), [report, reportActions, source]); - - useEffect(() => { - // Update the parent modal's state with the source and name from the mapped attachments - onNavigate(initialItem); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initialItem]); - +function AttachmentCarousel({report, reportActions, source, onNavigate, onClose, setDownloadButtonVisibility, translate}) { const pagerRef = useRef(null); const [containerDimensions, setContainerDimensions] = useState({width: 0, height: 0}); - const [page, setPage] = useState(initialPage); - const [activeSource, setActiveSource] = useState(initialActiveSource); + const [page, setPage] = useState(0); + const [attachments, setAttachments] = useState([]); + const [activeSource, setActiveSource] = useState(source); const [isPinchGestureRunning, setIsPinchGestureRunning] = useState(true); const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows(); + useEffect(() => { + const attachmentsFromReport = extractAttachmentsFromReport(report, reportActions); + + const initialPage = _.findIndex(attachmentsFromReport, (a) => a.source === source); + + // Dismiss the modal when deleting an attachment during its display in preview. + if (initialPage === -1 && _.find(attachments, (a) => a.source === source)) { + Navigation.dismissModal(); + } else { + setPage(initialPage); + setAttachments(attachmentsFromReport); + + // Update the download button visibility in the parent modal + setDownloadButtonVisibility(initialPage !== -1); + + // Update the parent modal's state with the source and name from the mapped attachments + if (!_.isUndefined(attachmentsFromReport[initialPage])) onNavigate(attachmentsFromReport[initialPage]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [report, reportActions, source]); + /** * Updates the page state when the user navigates between attachments * @param {Object} item @@ -90,31 +111,42 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, onClose} onMouseEnter={() => setShouldShowArrows(true)} onMouseLeave={() => setShouldShowArrows(false)} > - cycleThroughAttachments(-1)} - onForward={() => cycleThroughAttachments(1)} - autoHideArrow={autoHideArrows} - cancelAutoHideArrow={cancelAutoHideArrows} - /> - - {containerDimensions.width > 0 && containerDimensions.height > 0 && ( - updatePage(newPage)} - onPinchGestureChange={(newIsPinchGestureRunning) => { - setIsPinchGestureRunning(newIsPinchGestureRunning); - if (!newIsPinchGestureRunning && !shouldShowArrows) setShouldShowArrows(true); - }} - onSwipeDown={onClose} - containerWidth={containerDimensions.width} - containerHeight={containerDimensions.height} - ref={pagerRef} + {page === -1 ? ( + + ) : ( + <> + cycleThroughAttachments(-1)} + onForward={() => cycleThroughAttachments(1)} + autoHideArrow={autoHideArrows} + cancelAutoHideArrow={cancelAutoHideArrows} + /> + + {containerDimensions.width > 0 && containerDimensions.height > 0 && ( + updatePage(newPage)} + onPinchGestureChange={(newIsPinchGestureRunning) => { + setIsPinchGestureRunning(newIsPinchGestureRunning); + if (!newIsPinchGestureRunning && !shouldShowArrows) setShouldShowArrows(true); + }} + onSwipeDown={onClose} + containerWidth={containerDimensions.width} + containerHeight={containerDimensions.height} + ref={pagerRef} + /> + )} + )} ); @@ -122,9 +154,12 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, onClose} AttachmentCarousel.propTypes = propTypes; AttachmentCarousel.defaultProps = defaultProps; -export default withOnyx({ - reportActions: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, - canEvict: false, - }, -})(AttachmentCarousel); +export default compose( + withOnyx({ + reportActions: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, + canEvict: false, + }, + }), + withLocalize, +)(AttachmentCarousel);