Skip to content

Commit bb0d85e

Browse files
committed
resolve conflicts #3
2 parents 18da29d + bfda5ed commit bb0d85e

28 files changed

+725
-188
lines changed

src/CONST.js

+3
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,9 @@ const CONST = {
10291029
AMOUNT_MAX_LENGTH: 10,
10301030
RECEIPT_STATE: {
10311031
SCANREADY: 'SCANREADY',
1032+
SCANNING: 'SCANNING',
1033+
SCANCOMPLETE: 'SCANCOMPLETE',
1034+
SCANFAILED: 'SCANFAILED',
10321035
},
10331036
FILE_TYPES: {
10341037
HTML: 'html',

src/ONYXKEYS.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default {
3333
// Credentials to authenticate the user
3434
CREDENTIALS: 'credentials',
3535

36-
// Contains loading data for the IOU feature (MoneyRequestModal, IOUDetail, & IOUPreview Components)
36+
// Contains loading data for the IOU feature (MoneyRequestModal, IOUDetail, & MoneyRequestPreview Components)
3737
IOU: 'iou',
3838

3939
// Keeps track if there is modal currently visible or not

src/components/AttachmentModal.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ function AttachmentModal(props) {
9898
const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false);
9999
const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
100100
const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired);
101+
const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(false);
101102
const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState('');
102103
const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null);
103104
const [source, setSource] = useState(props.source);
@@ -118,12 +119,13 @@ function AttachmentModal(props) {
118119

119120
/**
120121
* Keeps the attachment source in sync with the attachment displayed currently in the carousel.
121-
* @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string } }} attachment
122+
* @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string }, isReceipt: Boolean }} attachment
122123
*/
123124
const onNavigate = useCallback(
124125
(attachment) => {
125126
setSource(attachment.source);
126127
setFile(attachment.file);
128+
setIsAttachmentReceipt(attachment.isReceipt);
127129
setIsAuthTokenRequired(attachment.isAuthTokenRequired);
128130
onCarouselAttachmentChange(attachment);
129131
},
@@ -314,6 +316,7 @@ function AttachmentModal(props) {
314316
}, []);
315317

316318
const sourceForAttachmentView = props.source || source;
319+
317320
return (
318321
<>
319322
<Modal
@@ -334,7 +337,7 @@ function AttachmentModal(props) {
334337
>
335338
{props.isSmallScreenWidth && <HeaderGap />}
336339
<HeaderWithBackButton
337-
title={props.headerTitle || translate('common.attachment')}
340+
title={props.headerTitle || translate(isAttachmentReceipt ? 'common.receipt' : 'common.attachment')}
338341
shouldShowBorderBottom
339342
shouldShowDownloadButton={props.allowDownload && shouldShowDownloadButton}
340343
onDownloadButtonPress={() => downloadAttachment(source)}

src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js

+26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import {Parser as HtmlParser} from 'htmlparser2';
22
import _ from 'underscore';
3+
import lodashGet from 'lodash/get';
34
import * as ReportActionsUtils from '../../../libs/ReportActionsUtils';
5+
import * as TransactionUtils from '../../../libs/TransactionUtils';
6+
import * as ReceiptUtils from '../../../libs/ReceiptUtils';
47
import CONST from '../../../CONST';
58
import tryResolveUrlFromApiRoot from '../../../libs/tryResolveUrlFromApiRoot';
69

@@ -28,6 +31,7 @@ function extractAttachmentsFromReport(report, reportActions) {
2831
source: tryResolveUrlFromApiRoot(expensifySource || attribs.src),
2932
isAuthTokenRequired: Boolean(expensifySource),
3033
file: {name: attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE]},
34+
isReceipt: false,
3135
});
3236
},
3337
});
@@ -36,6 +40,28 @@ function extractAttachmentsFromReport(report, reportActions) {
3640
if (!ReportActionsUtils.shouldReportActionBeVisible(action, key)) {
3741
return;
3842
}
43+
44+
// We're handling receipts differently here because receipt images are not
45+
// part of the report action message, the images are constructed client-side
46+
if (ReportActionsUtils.isMoneyRequestAction(action)) {
47+
const transactionID = lodashGet(action, ['originalMessage', 'IOUTransactionID']);
48+
if (!transactionID) {
49+
return;
50+
}
51+
52+
const transaction = TransactionUtils.getTransaction(transactionID);
53+
if (TransactionUtils.hasReceipt(transaction)) {
54+
const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename);
55+
attachments.unshift({
56+
source: tryResolveUrlFromApiRoot(image),
57+
isAuthTokenRequired: true,
58+
file: {name: transaction.filename},
59+
isReceipt: true,
60+
});
61+
return;
62+
}
63+
}
64+
3965
htmlParser.write(_.get(action, ['message', 0, 'html']));
4066
});
4167
htmlParser.end();

src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const customHTMLElementModels = {
4848
'mention-here': defaultHTMLElementModels.span.extend({tagName: 'mention-here'}),
4949
};
5050

51-
const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]};
51+
const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText, styles.w100, styles.h100]};
5252

5353
// We are using the explicit composite architecture for performance gains.
5454
// Configuration for RenderHTML is handled in a top-level component providing

src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,33 @@ function ImageRenderer(props) {
3333
// Concierge responder attachments are uploaded to S3 without any access
3434
// control and thus require no authToken to verify access.
3535
//
36-
const isAttachment = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]);
36+
const isAttachmentOrReceipt = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]);
3737

3838
// Files created/uploaded/hosted by App should resolve from API ROOT. Other URLs aren't modified
3939
const previewSource = tryResolveUrlFromApiRoot(htmlAttribs.src);
40-
const source = tryResolveUrlFromApiRoot(isAttachment ? htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] : htmlAttribs.src);
40+
const source = tryResolveUrlFromApiRoot(isAttachmentOrReceipt ? htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] : htmlAttribs.src);
4141

4242
const imageWidth = htmlAttribs['data-expensify-width'] ? parseInt(htmlAttribs['data-expensify-width'], 10) : undefined;
4343
const imageHeight = htmlAttribs['data-expensify-height'] ? parseInt(htmlAttribs['data-expensify-height'], 10) : undefined;
4444
const imagePreviewModalDisabled = htmlAttribs['data-expensify-preview-modal-disabled'] === 'true';
4545

46+
const shouldFitContainer = htmlAttribs['data-expensify-fit-container'] === 'true';
47+
const sizingStyle = shouldFitContainer ? [styles.w100, styles.h100] : [];
48+
4649
return imagePreviewModalDisabled ? (
4750
<ThumbnailImage
4851
previewSourceURL={previewSource}
49-
style={styles.webViewStyles.tagStyles.img}
50-
isAuthTokenRequired={isAttachment}
52+
style={shouldFitContainer ? [styles.w100, styles.h100] : styles.webViewStyles.tagStyles.img}
53+
isAuthTokenRequired={isAttachmentOrReceipt}
5154
imageWidth={imageWidth}
5255
imageHeight={imageHeight}
56+
shouldDynamicallyResize={!shouldFitContainer}
5357
/>
5458
) : (
5559
<ShowContextMenuContext.Consumer>
5660
{({anchor, report, action, checkIfContextMenuActive}) => (
5761
<PressableWithoutFocus
58-
style={[styles.noOutline]}
62+
style={[styles.noOutline, ...sizingStyle]}
5963
onPress={() => {
6064
const route = ROUTES.getReportAttachmentRoute(report.reportID, source);
6165
Navigation.navigate(route);
@@ -66,10 +70,11 @@ function ImageRenderer(props) {
6670
>
6771
<ThumbnailImage
6872
previewSourceURL={previewSource}
69-
style={styles.webViewStyles.tagStyles.img}
70-
isAuthTokenRequired={isAttachment}
73+
style={shouldFitContainer ? [styles.w100, styles.h100] : styles.webViewStyles.tagStyles.img}
74+
isAuthTokenRequired={isAttachmentOrReceipt}
7175
imageWidth={imageWidth}
7276
imageHeight={imageHeight}
77+
shouldDynamicallyResize={!shouldFitContainer}
7378
/>
7479
</PressableWithoutFocus>
7580
)}

src/components/MoneyRequestConfirmationList.js

+2-37
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx';
44
import {format} from 'date-fns';
55
import _ from 'underscore';
66
import {View} from 'react-native';
7-
import Str from 'expensify-common/lib/str';
87
import styles from '../styles/styles';
98
import * as ReportUtils from '../libs/ReportUtils';
109
import * as OptionsListUtils from '../libs/OptionsListUtils';
@@ -26,12 +25,8 @@ import Button from './Button';
2625
import * as Expensicons from './Icon/Expensicons';
2726
import themeColors from '../styles/themes/default';
2827
import Image from './Image';
29-
import ReceiptHTML from '../../assets/images/receipt-html.png';
30-
import ReceiptDoc from '../../assets/images/receipt-doc.png';
31-
import ReceiptGeneric from '../../assets/images/receipt-generic.png';
32-
import ReceiptSVG from '../../assets/images/receipt-svg.png';
33-
import * as FileUtils from '../libs/fileDownload/FileUtils';
3428
import useLocalize from '../hooks/useLocalize';
29+
import * as ReceiptUtils from '../libs/ReceiptUtils';
3530

3631
const propTypes = {
3732
/** Callback to inform parent modal of success */
@@ -319,36 +314,6 @@ function MoneyRequestConfirmationList(props) {
319314
);
320315
}, [confirm, props.selectedParticipants, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions]);
321316

322-
/**
323-
* Grab the appropriate image URI based on file type
324-
*
325-
* @param {String} receiptPath
326-
* @param {String} receiptSource
327-
* @returns {*}
328-
*/
329-
const getImageURI = (receiptPath, receiptSource) => {
330-
const {fileExtension} = FileUtils.splitExtensionFromFileName(receiptSource);
331-
const isReceiptImage = Str.isImage(props.receiptSource);
332-
333-
if (isReceiptImage) {
334-
return receiptPath;
335-
}
336-
337-
if (fileExtension === CONST.IOU.FILE_TYPES.HTML) {
338-
return ReceiptHTML;
339-
}
340-
341-
if (fileExtension === CONST.IOU.FILE_TYPES.DOC || fileExtension === CONST.IOU.FILE_TYPES.DOCX) {
342-
return ReceiptDoc;
343-
}
344-
345-
if (fileExtension === CONST.IOU.FILE_TYPES.SVG) {
346-
return ReceiptSVG;
347-
}
348-
349-
return ReceiptGeneric;
350-
};
351-
352317
return (
353318
<OptionsSelector
354319
sections={optionSelectorSections}
@@ -369,7 +334,7 @@ function MoneyRequestConfirmationList(props) {
369334
{!_.isEmpty(props.receiptPath) ? (
370335
<Image
371336
style={styles.moneyRequestImage}
372-
source={{uri: getImageURI(props.receiptPath, props.receiptSource)}}
337+
source={{uri: ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource).image}}
373338
/>
374339
) : (
375340
<MenuItemWithTopDescription

src/components/MoneyRequestHeader.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import * as IOU from '../libs/actions/IOU';
1919
import * as ReportActionsUtils from '../libs/ReportActionsUtils';
2020
import ConfirmModal from './ConfirmModal';
2121
import useLocalize from '../hooks/useLocalize';
22+
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
23+
import * as TransactionUtils from '../libs/TransactionUtils';
2224

2325
const propTypes = {
2426
/** The report currently being looked at */
@@ -70,6 +72,9 @@ function MoneyRequestHeader(props) {
7072
setIsDeleteModalVisible(false);
7173
}, [parentReportAction, setIsDeleteModalVisible]);
7274

75+
const transaction = TransactionUtils.getLinkedTransaction(parentReportAction);
76+
const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
77+
7378
return (
7479
<>
7580
<View style={[styles.pl0]}>
@@ -90,8 +95,8 @@ function MoneyRequestHeader(props) {
9095
personalDetails={props.personalDetails}
9196
shouldShowBackButton={props.isSmallScreenWidth}
9297
onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)}
93-
shouldShowBorderBottom
9498
/>
99+
{isScanning && <MoneyRequestHeaderStatusBar />}
95100
</View>
96101
<ConfirmModal
97102
title={translate('iou.deleteRequest')}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import styles from '../styles/styles';
4+
import useLocalize from '../hooks/useLocalize';
5+
import Text from './Text';
6+
7+
function MoneyRequestHeaderStatusBar() {
8+
const {translate} = useLocalize();
9+
10+
return (
11+
<View
12+
style={[
13+
styles.dFlex,
14+
styles.flexRow,
15+
styles.alignItemsCenter,
16+
styles.flexGrow1,
17+
styles.justifyContentBetween,
18+
styles.overflowHidden,
19+
styles.ph5,
20+
styles.pv3,
21+
styles.borderBottom,
22+
styles.w100,
23+
]}
24+
>
25+
<View style={[styles.moneyRequestHeaderStatusBarBadge]}>
26+
<Text style={[styles.textStrong, styles.textLabel]}>{translate('iou.receiptStatusTitle')}</Text>
27+
</View>
28+
<View style={[styles.flexShrink1]}>
29+
<Text style={[styles.textLabelSupporting]}>{translate('iou.receiptStatusText')}</Text>
30+
</View>
31+
</View>
32+
);
33+
}
34+
35+
MoneyRequestHeaderStatusBar.displayName = 'MoneyRequestHeaderStatusBar';
36+
37+
export default MoneyRequestHeaderStatusBar;

src/components/ReportActionItem/MoneyRequestAction.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import compose from '../../libs/compose';
1010
import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes';
1111
import networkPropTypes from '../networkPropTypes';
1212
import iouReportPropTypes from '../../pages/iouReportPropTypes';
13-
import IOUPreview from './IOUPreview';
13+
import MoneyRequestPreview from './MoneyRequestPreview';
1414
import Navigation from '../../libs/Navigation/Navigation';
1515
import ROUTES from '../../ROUTES';
1616
import styles from '../../styles/styles';
@@ -87,7 +87,7 @@ const defaultProps = {
8787
function MoneyRequestAction(props) {
8888
const isSplitBillAction = lodashGet(props.action, 'originalMessage.type', '') === CONST.IOU.REPORT_ACTION_TYPE.SPLIT;
8989

90-
const onIOUPreviewPressed = () => {
90+
const onMoneyRequestPreviewPressed = () => {
9191
if (isSplitBillAction) {
9292
const reportActionID = lodashGet(props.action, 'reportActionID', '0');
9393
Navigation.navigate(ROUTES.getSplitBillDetailsRoute(props.chatReportID, reportActionID));
@@ -138,16 +138,16 @@ function MoneyRequestAction(props) {
138138
return isDeletedParentAction ? (
139139
<RenderHTML html={`<comment>${props.translate('parentReportAction.deletedRequest')}</comment>`} />
140140
) : (
141-
<IOUPreview
142-
chatReportID={props.chatReportID}
141+
<MoneyRequestPreview
143142
iouReportID={props.requestReportID}
143+
chatReportID={props.chatReportID}
144144
isBillSplit={isSplitBillAction}
145145
action={props.action}
146146
contextMenuAnchor={props.contextMenuAnchor}
147147
checkIfContextMenuActive={props.checkIfContextMenuActive}
148148
shouldShowPendingConversionMessage={shouldShowPendingConversionMessage}
149-
onPreviewPressed={onIOUPreviewPressed}
150-
containerStyles={[styles.cursorPointer, props.isHovered ? styles.iouPreviewBoxHover : undefined, ...props.style]}
149+
onPreviewPressed={onMoneyRequestPreviewPressed}
150+
containerStyles={[styles.cursorPointer, props.isHovered ? styles.reportPreviewBoxHoverBorder : undefined, ...props.style]}
151151
isHovered={props.isHovered}
152152
/>
153153
);

0 commit comments

Comments
 (0)