-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
display the not found page when attachment is deleted. #23143
Changes from all commits
1feeb80
c9e86db
1974052
8cf2cdb
9419385
0d20879
2a7b2e5
533a553
4aff8e2
2fa9a37
b4c04b9
0dad55b
5f2b425
b9af016
a0962d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -99,6 +100,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 | ||
? { | ||
|
@@ -138,6 +140,16 @@ function AttachmentModal(props) { | |
[translate], | ||
); | ||
|
||
const setDownloadButtonVisibility = useCallback( | ||
(shouldShowButton) => { | ||
if (shouldShowDownloadButton === shouldShowButton) { | ||
return; | ||
} | ||
setShouldShowDownloadButton(shouldShowButton); | ||
}, | ||
[shouldShowDownloadButton], | ||
); | ||
|
||
/** | ||
* Download the currently viewed attachment. | ||
*/ | ||
|
@@ -330,7 +342,7 @@ function AttachmentModal(props) { | |
<HeaderWithBackButton | ||
title={props.headerTitle || translate('common.attachment')} | ||
shouldShowBorderBottom | ||
shouldShowDownloadButton={props.allowDownload} | ||
shouldShowDownloadButton={props.allowDownload && shouldShowDownloadButton} | ||
onDownloadButtonPress={() => downloadAttachment(source)} | ||
shouldShowCloseButton={!props.isSmallScreenWidth} | ||
shouldShowBackButton={props.isSmallScreenWidth} | ||
|
@@ -342,9 +354,10 @@ function AttachmentModal(props) { | |
<AttachmentCarousel | ||
report={props.report} | ||
onNavigate={onNavigate} | ||
source={props.source} | ||
source={tryResolveUrlFromApiRoot(props.source)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ahmedGaber93 Can you please help explain why do we need to use tryResolveUrlFromApiRoot here? Thanks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tienifr |
||
onClose={closeModal} | ||
onToggleKeyboard={updateConfirmButtonVisibility} | ||
setDownloadButtonVisibility={setDownloadButtonVisibility} | ||
/> | ||
) : ( | ||
Boolean(sourceForAttachmentView) && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why couldn't we have this logic here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need only to move |
||
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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just looking at the code I am not able to make out why did couldn't go in a common method? What's the difference between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm… I thought about this, but I can't create a valuable function. // I think we need to use `_.find` twice to make the function readable, but as you comment before we are not sure if _.find for a lot of attachments could be expensive or not?
function isAttachmentDeletingInLivePreview(prevAttachments, nextAttachments, activeSource){
return _.find(prevAttachments, (a) => a.source === activeSource)
&& !_.find(nextAttachments, (a) => a.source === activeSource)
}
// i am not sure if this need to be a function.
function getInitialPage(attachments, source){
return _.findIndex(attachments, (a) => a.source === source);
} What do you think, or suggest about the desired function? @mananjadhav |
||
Navigation.dismissModal(); | ||
} else { | ||
setPage(initialPage); | ||
setAttachments(attachmentsFromReport); | ||
|
||
// Update the download button visibility in the parent modal | ||
ahmedGaber93 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
setDownloadButtonVisibility(initialPage !== -1); | ||
|
||
// Update the parent modal's state with the source and name from the mapped attachments | ||
ahmedGaber93 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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)} | ||
> | ||
<CarouselButtons | ||
shouldShowArrows={shouldShowArrows} | ||
page={page} | ||
attachments={attachments} | ||
onBack={() => cycleThroughAttachments(-1)} | ||
onForward={() => cycleThroughAttachments(1)} | ||
autoHideArrow={autoHideArrows} | ||
cancelAutoHideArrow={cancelAutoHideArrows} | ||
/> | ||
|
||
{containerWidth > 0 && ( | ||
<FlatList | ||
keyboardShouldPersistTaps="handled" | ||
listKey="AttachmentCarousel" | ||
horizontal | ||
decelerationRate="fast" | ||
showsHorizontalScrollIndicator={false} | ||
bounces={false} | ||
// Scroll only one image at a time no matter how fast the user swipes | ||
disableIntervalMomentum | ||
pagingEnabled | ||
snapToAlignment="start" | ||
snapToInterval={containerWidth} | ||
// Enable scrolling by swiping on mobile (touch) devices only | ||
// disable scroll for desktop/browsers because they add their scrollbars | ||
// Enable scrolling FlatList only when PDF is not in a zoomed state | ||
scrollEnabled={canUseTouchScreen} | ||
ref={scrollRef} | ||
initialScrollIndex={page} | ||
initialNumToRender={3} | ||
windowSize={5} | ||
maxToRenderPerBatch={3} | ||
data={attachments} | ||
CellRendererComponent={renderCell} | ||
renderItem={renderItem} | ||
getItemLayout={getItemLayout} | ||
keyExtractor={(item) => item.source} | ||
viewabilityConfig={viewabilityConfig} | ||
onViewableItemsChanged={updatePage.current} | ||
{page === -1 ? ( | ||
<BlockingView | ||
icon={Illustrations.ToddBehindCloud} | ||
iconWidth={variables.modalTopIconWidth} | ||
iconHeight={variables.modalTopIconHeight} | ||
title={translate('notFound.notHere')} | ||
/> | ||
) : ( | ||
<> | ||
<CarouselButtons | ||
shouldShowArrows={shouldShowArrows} | ||
page={page} | ||
attachments={attachments} | ||
onBack={() => cycleThroughAttachments(-1)} | ||
onForward={() => cycleThroughAttachments(1)} | ||
autoHideArrow={autoHideArrows} | ||
cancelAutoHideArrow={cancelAutoHideArrows} | ||
/> | ||
|
||
{containerWidth > 0 && ( | ||
<FlatList | ||
keyboardShouldPersistTaps="handled" | ||
listKey="AttachmentCarousel" | ||
horizontal | ||
decelerationRate="fast" | ||
showsHorizontalScrollIndicator={false} | ||
bounces={false} | ||
// Scroll only one image at a time no matter how fast the user swipes | ||
disableIntervalMomentum | ||
pagingEnabled | ||
snapToAlignment="start" | ||
snapToInterval={containerWidth} | ||
// Enable scrolling by swiping on mobile (touch) devices only | ||
// disable scroll for desktop/browsers because they add their scrollbars | ||
// Enable scrolling FlatList only when PDF is not in a zoomed state | ||
scrollEnabled={canUseTouchScreen} | ||
ref={scrollRef} | ||
initialScrollIndex={page} | ||
initialNumToRender={3} | ||
windowSize={5} | ||
maxToRenderPerBatch={3} | ||
data={attachments} | ||
CellRendererComponent={renderCell} | ||
renderItem={renderItem} | ||
getItemLayout={getItemLayout} | ||
keyExtractor={(item) => item.source} | ||
viewabilityConfig={viewabilityConfig} | ||
onViewableItemsChanged={updatePage.current} | ||
/> | ||
)} | ||
|
||
<CarouselActions onCycleThroughAttachments={cycleThroughAttachments} /> | ||
</> | ||
)} | ||
|
||
<CarouselActions onCycleThroughAttachments={cycleThroughAttachments} /> | ||
</View> | ||
); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if this can be just replaced with
state
, andprops.allowDownload
can be used in the useCallback?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using
props.allowDownload
inuseCallback
dependencies will trigger change definition for methodsetDownloadButtonVisibility
notshouldShowDownloadButton
value.but we can use it in useEffect.
what do you think? @mananjadhav
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can ignore this change.