Skip to content

Commit 62ef87a

Browse files
Merge pull request #32516 from Expensify/neil-fix-distance-eReceipt
[CP Staging] Manually revert "Merge pull request #31467 from dukenv0307/fix/31105"
2 parents d40c017 + 5b11d3a commit 62ef87a

File tree

5 files changed

+103
-43
lines changed

5 files changed

+103
-43
lines changed

src/components/AttachmentModal.js

+12-14
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@ const propTypes = {
9090

9191
/** Denotes whether it is a workspace avatar or not */
9292
isWorkspaceAvatar: PropTypes.bool,
93-
94-
/** Whether it is a receipt attachment or not */
95-
isReceiptAttachment: PropTypes.bool,
9693
};
9794

9895
const defaultProps = {
@@ -110,7 +107,6 @@ const defaultProps = {
110107
onModalHide: () => {},
111108
onCarouselAttachmentChange: () => {},
112109
isWorkspaceAvatar: false,
113-
isReceiptAttachment: false,
114110
};
115111

116112
function AttachmentModal(props) {
@@ -122,6 +118,7 @@ function AttachmentModal(props) {
122118
const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
123119
const [isDeleteReceiptConfirmModalVisible, setIsDeleteReceiptConfirmModalVisible] = useState(false);
124120
const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired);
121+
const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(null);
125122
const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState('');
126123
const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null);
127124
const [source, setSource] = useState(props.source);
@@ -157,6 +154,7 @@ function AttachmentModal(props) {
157154
(attachment) => {
158155
setSource(attachment.source);
159156
setFile(attachment.file);
157+
setIsAttachmentReceipt(attachment.isReceipt);
160158
setIsAuthTokenRequired(attachment.isAuthTokenRequired);
161159
onCarouselAttachmentChange(attachment);
162160
},
@@ -359,7 +357,7 @@ function AttachmentModal(props) {
359357
const sourceForAttachmentView = props.source || source;
360358

361359
const threeDotsMenuItems = useMemo(() => {
362-
if (!props.isReceiptAttachment || !props.parentReport || !props.parentReportActions) {
360+
if (!isAttachmentReceipt || !props.parentReport || !props.parentReportActions) {
363361
return [];
364362
}
365363
const menuItems = [];
@@ -394,17 +392,17 @@ function AttachmentModal(props) {
394392
}
395393
return menuItems;
396394
// eslint-disable-next-line react-hooks/exhaustive-deps
397-
}, [props.isReceiptAttachment, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]);
395+
}, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]);
398396

399397
// There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment.
400-
// props.isReceiptAttachment will be null until its certain what the file is, in which case it will then be true|false.
398+
// isAttachmentReceipt will be null until its certain what the file is, in which case it will then be true|false.
401399
let headerTitle = props.headerTitle;
402400
let shouldShowDownloadButton = false;
403401
let shouldShowThreeDotsButton = false;
404-
if (!_.isNull(props.isReceiptAttachment)) {
405-
headerTitle = translate(props.isReceiptAttachment ? 'common.receipt' : 'common.attachment');
406-
shouldShowDownloadButton = props.allowDownload && isDownloadButtonReadyToBeShown && !props.isReceiptAttachment && !isOffline;
407-
shouldShowThreeDotsButton = props.isReceiptAttachment && isModalOpen;
402+
if (!_.isNull(isAttachmentReceipt)) {
403+
headerTitle = translate(isAttachmentReceipt ? 'common.receipt' : 'common.attachment');
404+
shouldShowDownloadButton = props.allowDownload && isDownloadButtonReadyToBeShown && !isAttachmentReceipt && !isOffline;
405+
shouldShowThreeDotsButton = isAttachmentReceipt && isModalOpen;
408406
}
409407

410408
return (
@@ -445,7 +443,7 @@ function AttachmentModal(props) {
445443
shouldOverlay
446444
/>
447445
<View style={styles.imageModalImageCenterContainer}>
448-
{!_.isEmpty(props.report) && !props.isReceiptAttachment ? (
446+
{!_.isEmpty(props.report) ? (
449447
<AttachmentCarousel
450448
report={props.report}
451449
onNavigate={onNavigate}
@@ -488,7 +486,7 @@ function AttachmentModal(props) {
488486
)}
489487
</SafeAreaConsumer>
490488
)}
491-
{props.isReceiptAttachment && (
489+
{isAttachmentReceipt && (
492490
<ConfirmModal
493491
title={translate('receipt.deleteReceipt')}
494492
isVisible={isDeleteReceiptConfirmModalVisible}
@@ -501,7 +499,7 @@ function AttachmentModal(props) {
501499
/>
502500
)}
503501
</Modal>
504-
{!props.isReceiptAttachment && (
502+
{!isAttachmentReceipt && (
505503
<ConfirmModal
506504
title={attachmentInvalidReasonTitle ? translate(attachmentInvalidReasonTitle) : ''}
507505
onConfirm={closeConfirmModal}

src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js

+30-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import {Parser as HtmlParser} from 'htmlparser2';
2+
import lodashGet from 'lodash/get';
23
import _ from 'underscore';
34
import * as FileUtils from '@libs/fileDownload/FileUtils';
5+
import * as ReceiptUtils from '@libs/ReceiptUtils';
46
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
7+
import * as TransactionUtils from '@libs/TransactionUtils';
58
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
69
import CONST from '@src/CONST';
710

811
/**
912
* Constructs the initial component state from report actions
1013
* @param {Object} parentReportAction
1114
* @param {Object} reportActions
15+
* @param {Object} transaction
1216
* @returns {Array}
1317
*/
14-
function extractAttachmentsFromReport(parentReportAction, reportActions) {
18+
function extractAttachmentsFromReport(parentReportAction, reportActions, transaction) {
1519
const actions = [parentReportAction, ...ReportActionsUtils.getSortedReportActions(_.values(reportActions))];
1620
const attachments = [];
1721

@@ -28,20 +32,43 @@ function extractAttachmentsFromReport(parentReportAction, reportActions) {
2832
// By iterating actions in chronological order and prepending each attachment
2933
// we ensure correct order of attachments even across actions with multiple attachments.
3034
attachments.unshift({
31-
source,
3235
reportActionID: attribs['data-id'],
36+
source,
3337
isAuthTokenRequired: Boolean(expensifySource),
3438
file: {name: fileName},
39+
isReceipt: false,
3540
hasBeenFlagged: attribs['data-flagged'] === 'true',
3641
});
3742
},
3843
});
3944

4045
_.forEach(actions, (action, key) => {
41-
if (!ReportActionsUtils.shouldReportActionBeVisible(action, key) || ReportActionsUtils.isMoneyRequestAction(action)) {
46+
if (!ReportActionsUtils.shouldReportActionBeVisible(action, key)) {
4247
return;
4348
}
4449

50+
// We're handling receipts differently here because receipt images are not
51+
// part of the report action message, the images are constructed client-side
52+
if (ReportActionsUtils.isMoneyRequestAction(action)) {
53+
const transactionID = lodashGet(action, ['originalMessage', 'IOUTransactionID']);
54+
if (!transactionID) {
55+
return;
56+
}
57+
58+
if (TransactionUtils.hasReceipt(transaction)) {
59+
const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction);
60+
const isLocalFile = typeof image === 'string' && _.some(CONST.ATTACHMENT_LOCAL_URL_PREFIX, (prefix) => image.startsWith(prefix));
61+
attachments.unshift({
62+
source: tryResolveUrlFromApiRoot(image),
63+
isAuthTokenRequired: !isLocalFile,
64+
file: {name: transaction.filename},
65+
isReceipt: true,
66+
transactionID,
67+
});
68+
return;
69+
}
70+
}
71+
4572
const decision = _.get(action, ['message', 0, 'moderationDecision', 'decision'], '');
4673
const hasBeenFlagged = decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || decision === CONST.MODERATION.MODERATOR_DECISION_HIDDEN;
4774
const html = _.get(action, ['message', 0, 'html'], '').replace('/>', `data-flagged="${hasBeenFlagged}" data-id="${action.reportActionID}"/>`);

src/components/Attachments/AttachmentCarousel/index.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import lodashGet from 'lodash/get';
12
import React, {useCallback, useEffect, useRef, useState} from 'react';
23
import {FlatList, Keyboard, PixelRatio, View} from 'react-native';
34
import {withOnyx} from 'react-native-onyx';
@@ -27,7 +28,7 @@ const viewabilityConfig = {
2728
itemVisiblePercentThreshold: 95,
2829
};
2930

30-
function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate}) {
31+
function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, transaction}) {
3132
const styles = useThemeStyles();
3233
const scrollRef = useRef(null);
3334

@@ -38,12 +39,21 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
3839
const [attachments, setAttachments] = useState([]);
3940
const [activeSource, setActiveSource] = useState(source);
4041
const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows();
42+
const [isReceipt, setIsReceipt] = useState(false);
4143

42-
const compareImage = useCallback((attachment) => attachment.source === source, [source]);
44+
const compareImage = useCallback(
45+
(attachment) => {
46+
if (attachment.isReceipt && isReceipt) {
47+
return attachment.transactionID === transaction.transactionID;
48+
}
49+
return attachment.source === source;
50+
},
51+
[source, isReceipt, transaction],
52+
);
4353

4454
useEffect(() => {
4555
const parentReportAction = parentReportActions[report.parentReportActionID];
46-
const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions);
56+
const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions, transaction);
4757

4858
const initialPage = _.findIndex(attachmentsFromReport, compareImage);
4959

@@ -78,10 +88,12 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
7888
// to get the index of the current page
7989
const entry = _.first(viewableItems);
8090
if (!entry) {
91+
setIsReceipt(false);
8192
setActiveSource(null);
8293
return;
8394
}
8495

96+
setIsReceipt(entry.item.isReceipt);
8597
setPage(entry.index);
8698
setActiveSource(entry.item.source);
8799

@@ -229,6 +241,15 @@ export default compose(
229241
canEvict: false,
230242
},
231243
}),
244+
// eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
245+
withOnyx({
246+
transaction: {
247+
key: ({report, parentReportActions}) => {
248+
const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]);
249+
return `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`;
250+
},
251+
},
252+
}),
232253
withLocalize,
233254
withWindowDimensions,
234255
)(AttachmentCarousel);

src/components/Attachments/AttachmentCarousel/index.native.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import lodashGet from 'lodash/get';
12
import React, {useCallback, useEffect, useRef, useState} from 'react';
23
import {Keyboard, PixelRatio, View} from 'react-native';
34
import {withOnyx} from 'react-native-onyx';
@@ -17,7 +18,7 @@ import extractAttachmentsFromReport from './extractAttachmentsFromReport';
1718
import AttachmentCarouselPager from './Pager';
1819
import useCarouselArrows from './useCarouselArrows';
1920

20-
function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, onClose}) {
21+
function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate, setDownloadButtonVisibility, translate, transaction, onClose}) {
2122
const styles = useThemeStyles();
2223
const pagerRef = useRef(null);
2324

@@ -27,12 +28,21 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
2728
const [activeSource, setActiveSource] = useState(source);
2829
const [isPinchGestureRunning, setIsPinchGestureRunning] = useState(true);
2930
const [shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows] = useCarouselArrows();
31+
const [isReceipt, setIsReceipt] = useState(false);
3032

31-
const compareImage = useCallback((attachment) => attachment.source === source, [source]);
33+
const compareImage = useCallback(
34+
(attachment) => {
35+
if (attachment.isReceipt && isReceipt) {
36+
return attachment.transactionID === transaction.transactionID;
37+
}
38+
return attachment.source === source;
39+
},
40+
[source, isReceipt, transaction],
41+
);
3242

3343
useEffect(() => {
3444
const parentReportAction = parentReportActions[report.parentReportActionID];
35-
const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions);
45+
const attachmentsFromReport = extractAttachmentsFromReport(parentReportAction, reportActions, transaction);
3646

3747
const initialPage = _.findIndex(attachmentsFromReport, compareImage);
3848

@@ -67,7 +77,9 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
6777
const item = attachments[newPageIndex];
6878

6979
setPage(newPageIndex);
80+
setIsReceipt(item.isReceipt);
7081
setActiveSource(item.source);
82+
7183
onNavigate(item);
7284
},
7385
[setShouldShowArrows, attachments, onNavigate],
@@ -174,5 +186,14 @@ export default compose(
174186
canEvict: false,
175187
},
176188
}),
189+
// eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
190+
withOnyx({
191+
transaction: {
192+
key: ({report, parentReportActions}) => {
193+
const parentReportAction = lodashGet(parentReportActions, [report.parentReportActionID]);
194+
return `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(parentReportAction, 'originalMessage.IOUTransactionID', 0)}`;
195+
},
196+
},
197+
}),
177198
withLocalize,
178199
)(AttachmentCarousel);

src/components/ReportActionItem/ReportActionItemImage.js

+13-20
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ import PropTypes from 'prop-types';
22
import React from 'react';
33
import {View} from 'react-native';
44
import _ from 'underscore';
5-
import AttachmentModal from '@components/AttachmentModal';
65
import EReceiptThumbnail from '@components/EReceiptThumbnail';
76
import Image from '@components/Image';
87
import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus';
98
import {ShowContextMenuContext} from '@components/ShowContextMenuContext';
109
import ThumbnailImage from '@components/ThumbnailImage';
1110
import transactionPropTypes from '@components/transactionPropTypes';
1211
import useLocalize from '@hooks/useLocalize';
12+
import Navigation from '@libs/Navigation/Navigation';
1313
import * as TransactionUtils from '@libs/TransactionUtils';
1414
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
1515
import useThemeStyles from '@styles/useThemeStyles';
1616
import CONST from '@src/CONST';
17+
import ROUTES from '@src/ROUTES';
1718

1819
const propTypes = {
1920
/** thumbnail URI for the image */
@@ -46,11 +47,11 @@ const defaultProps = {
4647
*/
4748

4849
function ReportActionItemImage({thumbnail, image, enablePreviewModal, transaction, isLocalFile}) {
50+
const styles = useThemeStyles();
4951
const {translate} = useLocalize();
5052
const imageSource = tryResolveUrlFromApiRoot(image || '');
5153
const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || '');
5254
const isEReceipt = !_.isEmpty(transaction) && TransactionUtils.hasEReceipt(transaction);
53-
const styles = useThemeStyles();
5455

5556
let receiptImageComponent;
5657

@@ -82,25 +83,17 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal, transactio
8283
return (
8384
<ShowContextMenuContext.Consumer>
8485
{({report}) => (
85-
<AttachmentModal
86-
headerTitle="Receipt"
87-
source={imageSource}
88-
isAuthTokenRequired={!isLocalFile}
89-
report={report}
90-
isReceiptAttachment
91-
allowToDownload
86+
<PressableWithoutFocus
87+
style={[styles.noOutline, styles.w100, styles.h100]}
88+
onPress={() => {
89+
const route = ROUTES.REPORT_ATTACHMENTS.getRoute(report.reportID, imageSource);
90+
Navigation.navigate(route);
91+
}}
92+
role={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON}
93+
accessibilityLabel={translate('accessibilityHints.viewAttachment')}
9294
>
93-
{({show}) => (
94-
<PressableWithoutFocus
95-
style={[styles.noOutline, styles.w100, styles.h100]}
96-
onPress={show}
97-
role={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON}
98-
accessibilityLabel={translate('accessibilityHints.viewAttachment')}
99-
>
100-
{receiptImageComponent}
101-
</PressableWithoutFocus>
102-
)}
103-
</AttachmentModal>
95+
{receiptImageComponent}
96+
</PressableWithoutFocus>
10497
)}
10598
</ShowContextMenuContext.Consumer>
10699
);

0 commit comments

Comments
 (0)