Skip to content
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 receipts in chat #24235

Merged
merged 68 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
a3b0dd2
Disable copy to clipboard for report previews
Aug 7, 2023
f143e1f
Show receipt image
Aug 8, 2023
04d0c3b
Fix receipt image loading
Aug 8, 2023
7466501
Begin styling for multiple receipts
Aug 8, 2023
941d730
Fit images to container
Aug 9, 2023
cf4680f
Polish preview box on hover
Aug 9, 2023
f0ced3a
Only apply full width when fitting container
Aug 9, 2023
e695401
Add scanning in progress
Aug 9, 2023
4ae0c89
Fix thumbnail and add correct optimistic filename
Aug 9, 2023
5140fed
Fix filename when not present
Aug 10, 2023
4177c40
Rename IOUPreview to MoneyRequestPreview
Aug 10, 2023
3f7acbe
Implement whispers
Aug 10, 2023
0f25a34
Update MoneyRequestPreview for receipts
Aug 10, 2023
d97b3bb
Refactor thumbnail/image for receipts
Aug 10, 2023
fb4120e
Update request header, disable receipt modal, update image URI blob c…
Aug 10, 2023
b6280f2
Update scan in progress string
Aug 10, 2023
a7fe6c8
Add receipt in money request view
Aug 10, 2023
3a1cd71
Add receipt whisper to details
Aug 11, 2023
1c98982
Move receipt whisper to header
Aug 11, 2023
b2b9529
Show merchant and number of requests in report preview
Aug 11, 2023
2c9768f
Add receipt preview modal to money request view
Aug 11, 2023
570a5da
Add receipt title and options for preview modal
Aug 11, 2023
904edce
Add additional money requests indicator and refactor
Aug 11, 2023
ca2efcd
Add number of requests scanning in report preview subtitle
Aug 11, 2023
2d2e584
Refactor image component and reuse
Aug 14, 2023
b2ae453
Merge main
Aug 14, 2023
ee8e83d
Fix refactor of MoneyRequestImage
Aug 14, 2023
acb2956
Show comment and request count info correctly
Aug 14, 2023
5d61c87
Fix linting and dependency cycle
Aug 14, 2023
2189b1e
Run prettier
Aug 14, 2023
c039cd3
Update optimistic report preview data
Aug 14, 2023
b20f2a5
Add optimistic data for updating the report preview
Aug 14, 2023
12536f9
Remove MoneyRequestImage and update optimistic data
Aug 15, 2023
3c2a7b7
Show number of requests before comment, update optimistic data
Aug 15, 2023
2fcf052
Add file URI when using drag and drop
Aug 15, 2023
64e5b37
Address comments and update comments and docs
Aug 15, 2023
4aee47b
Separate status bar from header
Aug 15, 2023
60af804
Refactor image to different component and fix linting
Aug 15, 2023
143c780
Rename to areAllRequestsBeingSmartScanned
Aug 16, 2023
4f2726a
Fix settlement button padding and typos
Aug 16, 2023
02086e0
Merge branch 'main' into andrewli-displayreceipts
Li357 Aug 16, 2023
6d026dd
Add maxWidth to receipt preview in money request view
Aug 16, 2023
eb96c58
Add arrow right on report preview/iou preview
Aug 16, 2023
2fe3b2b
Remove test code
Aug 16, 2023
9ea46f9
Attach URI to file for iOS uploads, change optimistic update for whisper
Aug 16, 2023
a87ddcf
Merge main
Aug 16, 2023
739c315
Fix linting and prettier
Aug 16, 2023
ecd553b
Remove identifier
Aug 16, 2023
a5b6dff
Address comments
Aug 17, 2023
b28d952
Move status bar component and fix conditions
Aug 17, 2023
6c8be71
Fix prettier
Aug 17, 2023
25c91a0
Remove dependency cycle by moving getNumberOfScanningReceipts and are…
Aug 17, 2023
34ab456
Update Spanish translations
Aug 17, 2023
69b1e8f
Fix typo
Aug 17, 2023
5155857
Show number of additional scanning receipts in overlay
Aug 17, 2023
cc483cc
Separate last 3 receipts to display from total number of receipts in …
Aug 17, 2023
7687d7f
Remove getNumberOfScanningReceipts
Aug 17, 2023
4788324
Fix header name
Aug 17, 2023
dec55b4
Run prettier
Aug 17, 2023
5875061
Merge main
Aug 17, 2023
55a66a3
Fix weird Android image upload issue
Aug 18, 2023
daa7e85
Merge branch 'main' into andrewli-displayreceipts
Li357 Aug 18, 2023
e057aca
Use optimistic transaction in updating report preview
Aug 19, 2023
aad1961
Merge branch 'main' into andrewli-displayreceipts
Li357 Aug 21, 2023
b12644c
Access transactionID safely in case of pay IOU actions
Aug 21, 2023
fec32bf
Fix lint
Aug 21, 2023
c63f160
Merge branch 'main' into andrewli-displayreceipts
Li357 Aug 21, 2023
de09a89
Fix lint
Aug 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,9 @@ const CONST = {
AMOUNT_MAX_LENGTH: 10,
RECEIPT_STATE: {
SCANREADY: 'SCANREADY',
SCANNING: 'SCANNING',
SCANCOMPLETE: 'SCANCOMPLETE',
SCANFAILED: 'SCANFAILED',
},
FILE_TYPES: {
HTML: 'html',
Expand Down
2 changes: 1 addition & 1 deletion src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default {
// Credentials to authenticate the user
CREDENTIALS: 'credentials',

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

// Keeps track if there is modal currently visible or not
Expand Down
7 changes: 5 additions & 2 deletions src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ function AttachmentModal(props) {
const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false);
const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired);
const [isAttachmentReceipt, setIsAttachmentReceipt] = useState(false);
const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState('');
const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null);
const [source, setSource] = useState(props.source);
Expand All @@ -118,12 +119,13 @@ function AttachmentModal(props) {

/**
* Keeps the attachment source in sync with the attachment displayed currently in the carousel.
* @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string } }} attachment
* @param {{ source: String, isAuthTokenRequired: Boolean, file: { name: string }, isReceipt: Boolean }} attachment
*/
const onNavigate = useCallback(
(attachment) => {
setSource(attachment.source);
setFile(attachment.file);
setIsAttachmentReceipt(attachment.isReceipt);
setIsAuthTokenRequired(attachment.isAuthTokenRequired);
onCarouselAttachmentChange(attachment);
},
Expand Down Expand Up @@ -314,6 +316,7 @@ function AttachmentModal(props) {
}, []);

const sourceForAttachmentView = props.source || source;

return (
<>
<Modal
Expand All @@ -334,7 +337,7 @@ function AttachmentModal(props) {
>
{props.isSmallScreenWidth && <HeaderGap />}
<HeaderWithBackButton
title={props.headerTitle || translate('common.attachment')}
title={props.headerTitle || translate(isAttachmentReceipt ? 'common.receipt' : 'common.attachment')}
shouldShowBorderBottom
shouldShowDownloadButton={props.allowDownload && shouldShowDownloadButton}
onDownloadButtonPress={() => downloadAttachment(source)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {Parser as HtmlParser} from 'htmlparser2';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import * as ReportActionsUtils from '../../../libs/ReportActionsUtils';
import * as TransactionUtils from '../../../libs/TransactionUtils';
import * as ReceiptUtils from '../../../libs/ReceiptUtils';
import CONST from '../../../CONST';
import tryResolveUrlFromApiRoot from '../../../libs/tryResolveUrlFromApiRoot';

Expand Down Expand Up @@ -28,6 +31,7 @@ function extractAttachmentsFromReport(report, reportActions) {
source: tryResolveUrlFromApiRoot(expensifySource || attribs.src),
isAuthTokenRequired: Boolean(expensifySource),
file: {name: attribs[CONST.ATTACHMENT_ORIGINAL_FILENAME_ATTRIBUTE]},
isReceipt: false,
});
},
});
Expand All @@ -36,6 +40,28 @@ function extractAttachmentsFromReport(report, reportActions) {
if (!ReportActionsUtils.shouldReportActionBeVisible(action, key)) {
return;
}

// We're handling receipts differently here because receipt images are not
// part of the report action message, the images are constructed client-side
if (ReportActionsUtils.isMoneyRequestAction(action)) {
const transactionID = lodashGet(action, ['originalMessage', 'IOUTransactionID']);
if (!transactionID) {
return;
}

const transaction = TransactionUtils.getTransaction(transactionID);
if (TransactionUtils.hasReceipt(transaction)) {
const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename);
attachments.unshift({
source: tryResolveUrlFromApiRoot(image),
isAuthTokenRequired: true,
file: {name: transaction.filename},
isReceipt: true,
});
return;
}
}

htmlParser.write(_.get(action, ['message', 0, 'html']));
});
htmlParser.end();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const customHTMLElementModels = {
'mention-here': defaultHTMLElementModels.span.extend({tagName: 'mention-here'}),
};

const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]};
const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText, styles.w100, styles.h100]};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a global change, is this really needed? Can we use the w100 and h100 just for the instance where it's actually required

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is easily doable without refactoring a lot of ReportActionItemImage's use of RenderHTML. I can do this in a followup PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the line that causes #25678 unfortunately, can we think of a way to update it without refactoring everything else?


// We are using the explicit composite architecture for performance gains.
// Configuration for RenderHTML is handled in a top-level component providing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,33 @@ function ImageRenderer(props) {
// Concierge responder attachments are uploaded to S3 without any access
// control and thus require no authToken to verify access.
//
const isAttachment = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]);
const isAttachmentOrReceipt = Boolean(htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]);

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

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

const shouldFitContainer = htmlAttribs['data-expensify-fit-container'] === 'true';
const sizingStyle = shouldFitContainer ? [styles.w100, styles.h100] : [];

return imagePreviewModalDisabled ? (
<ThumbnailImage
previewSourceURL={previewSource}
style={styles.webViewStyles.tagStyles.img}
isAuthTokenRequired={isAttachment}
style={shouldFitContainer ? [styles.w100, styles.h100] : styles.webViewStyles.tagStyles.img}
isAuthTokenRequired={isAttachmentOrReceipt}
imageWidth={imageWidth}
imageHeight={imageHeight}
shouldDynamicallyResize={!shouldFitContainer}
/>
) : (
<ShowContextMenuContext.Consumer>
{({anchor, report, action, checkIfContextMenuActive}) => (
<PressableWithoutFocus
style={[styles.noOutline]}
style={[styles.noOutline, ...sizingStyle]}
onPress={() => {
const route = ROUTES.getReportAttachmentRoute(report.reportID, source);
Navigation.navigate(route);
Expand All @@ -66,10 +70,11 @@ function ImageRenderer(props) {
>
<ThumbnailImage
previewSourceURL={previewSource}
style={styles.webViewStyles.tagStyles.img}
isAuthTokenRequired={isAttachment}
style={shouldFitContainer ? [styles.w100, styles.h100] : styles.webViewStyles.tagStyles.img}
isAuthTokenRequired={isAttachmentOrReceipt}
imageWidth={imageWidth}
imageHeight={imageHeight}
shouldDynamicallyResize={!shouldFitContainer}
/>
</PressableWithoutFocus>
)}
Expand Down
39 changes: 2 additions & 37 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {withOnyx} from 'react-native-onyx';
import {format} from 'date-fns';
import _ from 'underscore';
import {View} from 'react-native';
import Str from 'expensify-common/lib/str';
import styles from '../styles/styles';
import * as ReportUtils from '../libs/ReportUtils';
import * as OptionsListUtils from '../libs/OptionsListUtils';
Expand All @@ -26,12 +25,8 @@ import Button from './Button';
import * as Expensicons from './Icon/Expensicons';
import themeColors from '../styles/themes/default';
import Image from './Image';
import ReceiptHTML from '../../assets/images/receipt-html.png';
import ReceiptDoc from '../../assets/images/receipt-doc.png';
import ReceiptGeneric from '../../assets/images/receipt-generic.png';
import ReceiptSVG from '../../assets/images/receipt-svg.png';
import * as FileUtils from '../libs/fileDownload/FileUtils';
import useLocalize from '../hooks/useLocalize';
import * as ReceiptUtils from '../libs/ReceiptUtils';

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

/**
* Grab the appropriate image URI based on file type
*
* @param {String} receiptPath
* @param {String} receiptSource
* @returns {*}
*/
const getImageURI = (receiptPath, receiptSource) => {
const {fileExtension} = FileUtils.splitExtensionFromFileName(receiptSource);
const isReceiptImage = Str.isImage(props.receiptSource);

if (isReceiptImage) {
return receiptPath;
}

if (fileExtension === CONST.IOU.FILE_TYPES.HTML) {
return ReceiptHTML;
}

if (fileExtension === CONST.IOU.FILE_TYPES.DOC || fileExtension === CONST.IOU.FILE_TYPES.DOCX) {
return ReceiptDoc;
}

if (fileExtension === CONST.IOU.FILE_TYPES.SVG) {
return ReceiptSVG;
}

return ReceiptGeneric;
};

return (
<OptionsSelector
sections={optionSelectorSections}
Expand All @@ -369,7 +334,7 @@ function MoneyRequestConfirmationList(props) {
{!_.isEmpty(props.receiptPath) ? (
<Image
style={styles.moneyRequestImage}
source={{uri: getImageURI(props.receiptPath, props.receiptSource)}}
source={{uri: ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource).image}}
/>
) : (
<MenuItemWithTopDescription
Expand Down
7 changes: 6 additions & 1 deletion src/components/MoneyRequestHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import * as IOU from '../libs/actions/IOU';
import * as ReportActionsUtils from '../libs/ReportActionsUtils';
import ConfirmModal from './ConfirmModal';
import useLocalize from '../hooks/useLocalize';
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
import * as TransactionUtils from '../libs/TransactionUtils';

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

const transaction = TransactionUtils.getLinkedTransaction(parentReportAction);
const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);

return (
<>
<View style={[styles.pl0]}>
Expand All @@ -90,8 +95,8 @@ function MoneyRequestHeader(props) {
personalDetails={props.personalDetails}
shouldShowBackButton={props.isSmallScreenWidth}
onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)}
shouldShowBorderBottom
/>
{isScanning && <MoneyRequestHeaderStatusBar />}
</View>
<ConfirmModal
title={translate('iou.deleteRequest')}
Expand Down
37 changes: 37 additions & 0 deletions src/components/MoneyRequestHeaderStatusBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import {View} from 'react-native';
import styles from '../styles/styles';
import useLocalize from '../hooks/useLocalize';
import Text from './Text';

function MoneyRequestHeaderStatusBar() {
const {translate} = useLocalize();

return (
<View
style={[
styles.dFlex,
styles.flexRow,
styles.alignItemsCenter,
styles.flexGrow1,
styles.justifyContentBetween,
styles.overflowHidden,
styles.ph5,
styles.pv3,
styles.borderBottom,
styles.w100,
]}
>
<View style={[styles.moneyRequestHeaderStatusBarBadge]}>
<Text style={[styles.textStrong, styles.textLabel]}>{translate('iou.receiptStatusTitle')}</Text>
</View>
<View style={[styles.flexShrink1]}>
<Text style={[styles.textLabelSupporting]}>{translate('iou.receiptStatusText')}</Text>
</View>
</View>
);
}

MoneyRequestHeaderStatusBar.displayName = 'MoneyRequestHeaderStatusBar';

export default MoneyRequestHeaderStatusBar;
12 changes: 6 additions & 6 deletions src/components/ReportActionItem/MoneyRequestAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import compose from '../../libs/compose';
import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes';
import networkPropTypes from '../networkPropTypes';
import iouReportPropTypes from '../../pages/iouReportPropTypes';
import IOUPreview from './IOUPreview';
import MoneyRequestPreview from './MoneyRequestPreview';
Li357 marked this conversation as resolved.
Show resolved Hide resolved
import Navigation from '../../libs/Navigation/Navigation';
import ROUTES from '../../ROUTES';
import styles from '../../styles/styles';
Expand Down Expand Up @@ -87,7 +87,7 @@ const defaultProps = {
function MoneyRequestAction(props) {
const isSplitBillAction = lodashGet(props.action, 'originalMessage.type', '') === CONST.IOU.REPORT_ACTION_TYPE.SPLIT;

const onIOUPreviewPressed = () => {
const onMoneyRequestPreviewPressed = () => {
if (isSplitBillAction) {
const reportActionID = lodashGet(props.action, 'reportActionID', '0');
Navigation.navigate(ROUTES.getSplitBillDetailsRoute(props.chatReportID, reportActionID));
Expand Down Expand Up @@ -138,16 +138,16 @@ function MoneyRequestAction(props) {
return isDeletedParentAction ? (
<RenderHTML html={`<comment>${props.translate('parentReportAction.deletedRequest')}</comment>`} />
) : (
<IOUPreview
chatReportID={props.chatReportID}
<MoneyRequestPreview
iouReportID={props.requestReportID}
chatReportID={props.chatReportID}
isBillSplit={isSplitBillAction}
action={props.action}
contextMenuAnchor={props.contextMenuAnchor}
checkIfContextMenuActive={props.checkIfContextMenuActive}
shouldShowPendingConversionMessage={shouldShowPendingConversionMessage}
onPreviewPressed={onIOUPreviewPressed}
containerStyles={[styles.cursorPointer, props.isHovered ? styles.iouPreviewBoxHover : undefined, ...props.style]}
onPreviewPressed={onMoneyRequestPreviewPressed}
containerStyles={[styles.cursorPointer, props.isHovered ? styles.reportPreviewBoxHoverBorder : undefined, ...props.style]}
isHovered={props.isHovered}
/>
);
Expand Down
Loading
Loading