diff --git a/src/CONST.ts b/src/CONST.ts
index 58fdea7654cb..a92244ba001a 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1476,6 +1476,7 @@ const CONST = {
MAKE_REQUEST_WITH_SIDE_EFFECTS: 'makeRequestWithSideEffects',
},
+ ERECEIPT_PATH: 'eReceipt/',
ERECEIPT_COLORS: {
YELLOW: 'Yellow',
ICE: 'Ice',
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 7127c1483c26..06f8de303e2c 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -286,6 +286,11 @@ export default {
I_AM_A_TEACHER: 'teachersunite/i-am-a-teacher',
INTRO_SCHOOL_PRINCIPAL: 'teachersunite/intro-school-principal',
+ ERECEIPT: {
+ route: 'eReceipt/:transactionID',
+ getRoute: (transactionID: string) => `eReceipt/${transactionID}`,
+ },
+
WORKSPACE_NEW: 'workspace/new',
WORKSPACE_NEW_ROOM: 'workspace/new-room',
WORKSPACE_INITIAL: {
diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js
index 8a623a44709f..dae0191b2158 100644
--- a/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js
+++ b/src/components/Attachments/AttachmentCarousel/extractAttachmentsFromReport.js
@@ -53,7 +53,7 @@ function extractAttachmentsFromReport(report, reportActions) {
const transaction = TransactionUtils.getTransaction(transactionID);
if (TransactionUtils.hasReceipt(transaction)) {
- const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename);
+ const {image} = ReceiptUtils.getThumbnailAndImageURIs(transaction);
attachments.unshift({
source: tryResolveUrlFromApiRoot(image),
isAuthTokenRequired: true,
diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js
index f4d3036ff802..5de495cbb2c3 100755
--- a/src/components/Attachments/AttachmentView/index.js
+++ b/src/components/Attachments/AttachmentView/index.js
@@ -18,6 +18,8 @@ import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL
import * as StyleUtils from '../../../styles/StyleUtils';
import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes';
import useNetwork from '../../../hooks/useNetwork';
+import CONST from '../../../CONST';
+import EReceipt from '../../EReceipt';
const propTypes = {
...attachmentViewPropTypes,
@@ -92,6 +94,15 @@ function AttachmentView({
);
}
+ if((_.isString(source)) && source.startsWith(CONST.ERECEIPT_PATH)) {
+ const transactionIDFromURL = source.split(CONST.ERECEIPT_PATH)[1];
+ return (
+
+
+
+ );
+ }
+
// Check both source and file.name since PDFs dragged into the text field
// will appear with a source that is a blob
if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) {
diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index 98ec01de16f8..86cc43ea043d 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -519,8 +519,7 @@ function MoneyRequestConfirmationList(props) {
);
}, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions, translate, formError]);
- const {image: receiptImage, thumbnail: receiptThumbnail} =
- props.receiptPath && props.receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptFilename) : {};
+ const {image: receiptImage, thumbnail: receiptThumbnail} = transaction ? ReceiptUtils.getThumbnailAndImageURIs(transaction) : {};
return (
{
if (isExpensifyCardTransaction || isDistanceRequest) {
diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js
index 079bc64d96bf..89372270833e 100644
--- a/src/components/ReportActionItem/MoneyRequestView.js
+++ b/src/components/ReportActionItem/MoneyRequestView.js
@@ -126,7 +126,7 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
let receiptURIs;
let hasErrors = false;
if (hasReceipt) {
- receiptURIs = ReceiptUtils.getThumbnailAndImageURIs(transaction.receipt.source, transaction.filename);
+ receiptURIs = ReceiptUtils.getThumbnailAndImageURIs(transaction);
hasErrors = canEdit && TransactionUtils.hasMissingSmartscanFields(transaction);
}
diff --git a/src/components/ReportActionItem/ReportActionItemImage.js b/src/components/ReportActionItem/ReportActionItemImage.js
index 98bdede0fe26..2b27ac50777c 100644
--- a/src/components/ReportActionItem/ReportActionItemImage.js
+++ b/src/components/ReportActionItem/ReportActionItemImage.js
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import {View} from 'react-native';
import styles from '../../styles/styles';
import Image from '../Image';
import ThumbnailImage from '../ThumbnailImage';
@@ -10,6 +11,7 @@ import {ShowContextMenuContext} from '../ShowContextMenuContext';
import Navigation from '../../libs/Navigation/Navigation';
import PressableWithoutFocus from '../Pressable/PressableWithoutFocus';
import useLocalize from '../../hooks/useLocalize';
+import EReceiptThumbnail from '../EReceiptThumbnail';
const propTypes = {
/** thumbnail URI for the image */
@@ -38,7 +40,11 @@ function ReportActionItemImage({thumbnail, image, enablePreviewModal}) {
const imageSource = tryResolveUrlFromApiRoot(image || '');
const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || '');
- const receiptImageComponent = thumbnail ? (
+
+ const isEReceipt = imageSource.startsWith(CONST.ERECEIPT_PATH);
+
+
+ let receiptImageComponent = thumbnail ? (
);
+ if(isEReceipt) {
+ const transactionIDFromURL = imageSource.split(CONST.ERECEIPT_PATH)[1];
+ receiptImageComponent = (
+
+ );
+ }
+
if (enablePreviewModal) {
return (
diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js
index f9001ed51258..b9bec38878d3 100644
--- a/src/components/ReportActionItem/ReportPreview.js
+++ b/src/components/ReportActionItem/ReportPreview.js
@@ -126,7 +126,7 @@ function ReportPreview(props) {
const isScanning = hasReceipts && ReportUtils.areAllRequestsBeingSmartScanned(props.iouReportID, props.action);
const hasErrors = hasReceipts && ReportUtils.hasMissingSmartscanFields(props.iouReportID);
const lastThreeTransactionsWithReceipts = ReportUtils.getReportPreviewDisplayTransactions(props.action);
- const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, ({receipt, filename}) => ReceiptUtils.getThumbnailAndImageURIs(receipt.source, filename || ''));
+ const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction));
const hasOnlyOneReceiptRequest = numberOfRequests === 1 && hasReceipts;
const previewSubtitle = hasOnlyOneReceiptRequest
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index e6c7480974ca..03ff9248552b 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -47,7 +47,8 @@ function getCardDescription(cardID: number) {
return '';
}
const cardDescriptor = card.state === CONST.EXPENSIFY_CARD.STATE.NOT_ACTIVATED ? Localize.translateLocal('cardTransactions.notActivated') : card.lastFourPAN;
- return `${card.bank} - ${cardDescriptor}`;
+ const result = cardDescriptor ? `${card.bank} - ${cardDescriptor}` : `${card.bank}`;
+ return result;
}
/**
diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts
index cdc45cb119d5..1a7fccb3fa9f 100644
--- a/src/libs/ReceiptUtils.ts
+++ b/src/libs/ReceiptUtils.ts
@@ -6,6 +6,8 @@ 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 { Transaction } from '../types/onyx';
+import ROUTES from '../ROUTES';
type ThumbnailAndImageURI = {
image: ImageSourcePropType | string;
@@ -20,12 +22,21 @@ type FileNameAndExtension = {
/**
* Grab the appropriate receipt image and thumbnail URIs based on file type
*
- * @param path URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg
- * @param filename of uploaded image or last part of remote URI
+ * @param transaction
*/
-function getThumbnailAndImageURIs(path: string, filename: string): ThumbnailAndImageURI {
+function getThumbnailAndImageURIs(transaction: Transaction): ThumbnailAndImageURI {
+ // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg
+ const path = transaction?.receipt?.source ?? 'https://hips.hearstapps.com/hmg-prod/images/dog-puppy-on-garden-royalty-free-image-1586966191.jpg?crop=0.752xw:1.00xh;0.175xw,0&resize=1200:*';
+ // filename of uploaded image or last part of remote URI
+ const filename = transaction?.filename ?? '';
const isReceiptImage = Str.isImage(filename);
+ const hasEReceipt = transaction?.hasEReceipt;
+
+ if(hasEReceipt){
+ return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction.transactionID)};
+ }
+
// For local files, we won't have a thumbnail yet
if (isReceiptImage && (path.startsWith('blob:') || path.startsWith('file:'))) {
return {thumbnail: null, image: path};
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index 43b1c2f39902..aa3ddbbb302c 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -76,8 +76,15 @@ function buildOptimisticTransaction(
};
}
+/**
+ * Check if the transaction has an Ereceipt
+ */
+function hasEreceipt(transaction: Transaction | undefined | null): boolean {
+ return !!transaction?.hasEReceipt;
+}
+
function hasReceipt(transaction: Transaction | undefined | null): boolean {
- return !!transaction?.receipt?.state;
+ return !!transaction?.receipt?.state || hasEreceipt(transaction);
}
function areRequiredFieldsEmpty(transaction: Transaction): boolean {
@@ -332,13 +339,6 @@ function hasRoute(transaction: Transaction): boolean {
return !!transaction?.routes?.route0?.geometry?.coordinates;
}
-/**
- * Check if the transaction has an Ereceipt
- */
-function hasEreceipt(transaction: Transaction): boolean {
- return !!transaction?.hasEReceipt;
-}
-
/**
* Get the transactions related to a report preview with receipts
* Get the details linked to the IOU reportAction
diff --git a/src/stories/ReportActionItemImages.stories.js b/src/stories/ReportActionItemImages.stories.js
new file mode 100644
index 000000000000..f7a3dd038c2e
--- /dev/null
+++ b/src/stories/ReportActionItemImages.stories.js
@@ -0,0 +1,149 @@
+import React from 'react';
+import ReportActionItemImages from '../components/ReportActionItem/ReportActionItemImages';
+import PressableWithoutFeedback from '../components/Pressable/PressableWithoutFeedback';
+
+/**
+ * We use the Component Story Format for writing stories. Follow the docs here:
+ *
+ * https://storybook.js.org/docs/react/writing-stories/introduction#component-story-format
+ */
+const story = {
+ title: 'Components/ReportActionItemImages',
+ component: ReportActionItemImages,
+};
+
+function Template(args) {
+ return (
+
+ {({hovered}) => (
+
+ )}
+
+ );
+}
+
+// Arguments can be passed to the component by binding
+// See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
+const Default = Template.bind({});
+Default.args = {
+ images: [{image: 'https://c02.purpledshub.com/uploads/sites/41/2021/05/sleeping-cat-27126ee.jpg', thumbnail: ''}],
+ size: 1,
+ total: 1,
+};
+
+const DisplayJSX = Template.bind({});
+DisplayJSX.args = {
+ images: [{image: 'eReceipt/FAKE_3', thumbnail: ''}],
+ size: 1,
+ total: 1,
+};
+
+const TwoImages = Template.bind({});
+TwoImages.args = {
+ images: [
+ {
+ image: 'https://c02.purpledshub.com/uploads/sites/41/2021/05/sleeping-cat-27126ee.jpg',
+ thumbnail: '',
+ },
+ {
+ image: 'https://i.guim.co.uk/img/media/7d04c4cb7510a4bd9a8bec449f53425aeccee895/298_266_1150_690/master/1150.jpg?width=1200&quality=85&auto=format&fit=max&s=4ae508ecb99c15ec04610b617efb3fa7',
+ thumbnail: '',
+ },
+ ],
+ size: 2,
+ total: 2,
+};
+
+const ThreeImages = Template.bind({});
+ThreeImages.args = {
+ images: [
+ {
+ image: 'https://c02.purpledshub.com/uploads/sites/41/2021/05/sleeping-cat-27126ee.jpg',
+ thumbnail: '',
+ },
+ {
+ image: 'https://i.guim.co.uk/img/media/7d04c4cb7510a4bd9a8bec449f53425aeccee895/298_266_1150_690/master/1150.jpg?width=1200&quality=85&auto=format&fit=max&s=4ae508ecb99c15ec04610b617efb3fa7',
+ thumbnail: '',
+ },
+ {
+ image: 'https://cdn.theatlantic.com/thumbor/d8lh_KAZuOgBYslMOP4T0iu9Fks=/0x62:2000x1187/1600x900/media/img/mt/2018/03/AP_325360162607/original.jpg',
+ thumbnail: '',
+ },
+ ],
+ size: 3,
+ total: 3,
+};
+
+const FourImages = Template.bind({});
+FourImages.args = {
+ images: [
+ {
+ image: 'https://c02.purpledshub.com/uploads/sites/41/2021/05/sleeping-cat-27126ee.jpg',
+ thumbnail: '',
+ },
+ {
+ image: 'https://i.guim.co.uk/img/media/7d04c4cb7510a4bd9a8bec449f53425aeccee895/298_266_1150_690/master/1150.jpg?width=1200&quality=85&auto=format&fit=max&s=4ae508ecb99c15ec04610b617efb3fa7',
+ thumbnail: '',
+ },
+ {
+ image: 'https://cdn.theatlantic.com/thumbor/d8lh_KAZuOgBYslMOP4T0iu9Fks=/0x62:2000x1187/1600x900/media/img/mt/2018/03/AP_325360162607/original.jpg',
+ thumbnail: '',
+ },
+ {
+ image: 'https://www.alleycat.org/wp-content/uploads/2019/03/FELV-cat.jpg',
+ thumbnail: '',
+ },
+ ],
+ size: 4,
+ total: 4,
+};
+
+const ThreePlusTwoImages = Template.bind({});
+ThreePlusTwoImages.args = {
+ images: [
+ {
+ image: 'https://c02.purpledshub.com/uploads/sites/41/2021/05/sleeping-cat-27126ee.jpg',
+ thumbnail: '',
+ },
+ {
+ image: 'https://i.guim.co.uk/img/media/7d04c4cb7510a4bd9a8bec449f53425aeccee895/298_266_1150_690/master/1150.jpg?width=1200&quality=85&auto=format&fit=max&s=4ae508ecb99c15ec04610b617efb3fa7',
+ thumbnail: '',
+ },
+ {
+ image: 'https://cdn.theatlantic.com/thumbor/d8lh_KAZuOgBYslMOP4T0iu9Fks=/0x62:2000x1187/1600x900/media/img/mt/2018/03/AP_325360162607/original.jpg',
+ thumbnail: '',
+ },
+ ],
+ size: 3,
+ total: 5,
+};
+
+const ThreePlusTenImages = Template.bind({});
+ThreePlusTenImages.args = {
+ images: [
+ {
+ image: 'https://c02.purpledshub.com/uploads/sites/41/2021/05/sleeping-cat-27126ee.jpg',
+ thumbnail: '',
+ },
+ {
+ image: 'https://i.guim.co.uk/img/media/7d04c4cb7510a4bd9a8bec449f53425aeccee895/298_266_1150_690/master/1150.jpg?width=1200&quality=85&auto=format&fit=max&s=4ae508ecb99c15ec04610b617efb3fa7',
+ thumbnail: '',
+ },
+ {
+ image: 'https://cdn.theatlantic.com/thumbor/d8lh_KAZuOgBYslMOP4T0iu9Fks=/0x62:2000x1187/1600x900/media/img/mt/2018/03/AP_325360162607/original.jpg',
+ thumbnail: '',
+ },
+ ],
+ size: 3,
+ total: 13,
+};
+
+export default story;
+export {Default, TwoImages, ThreeImages, FourImages, ThreePlusTwoImages, ThreePlusTenImages, DisplayJSX};
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 8fa81cd98b21..f004aeada4fe 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -3303,7 +3303,6 @@ const styles = (theme) => ({
},
eReceiptContainer: {
- flex: 1,
width: 335,
minHeight: 540,
borderRadius: 20,