diff --git a/assets/images/receipt-slash.svg b/assets/images/receipt-slash.svg
new file mode 100644
index 000000000000..2af3fcbc60e6
--- /dev/null
+++ b/assets/images/receipt-slash.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx
index f6730f4b81d9..f0cd69f28401 100644
--- a/src/components/AttachmentPicker/index.native.tsx
+++ b/src/components/AttachmentPicker/index.native.tsx
@@ -212,7 +212,7 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s
* An attachment error dialog when user selected malformed images
*/
const showImageCorruptionAlert = useCallback(() => {
- Alert.alert(translate('attachmentPicker.attachmentError'), translate('attachmentPicker.errorWhileSelectingCorruptedImage'));
+ Alert.alert(translate('attachmentPicker.attachmentError'), translate('attachmentPicker.errorWhileSelectingCorruptedAttachment'));
}, [translate]);
/**
diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts
index a963a0d0908d..5c181bfdb29f 100644
--- a/src/components/Icon/Expensicons.ts
+++ b/src/components/Icon/Expensicons.ts
@@ -139,6 +139,7 @@ import QuestionMark from '@assets/images/question-mark-circle.svg';
import ReceiptPlus from '@assets/images/receipt-plus.svg';
import ReceiptScan from '@assets/images/receipt-scan.svg';
import ReceiptSearch from '@assets/images/receipt-search.svg';
+import ReceiptSlash from '@assets/images/receipt-slash.svg';
import Receipt from '@assets/images/receipt.svg';
import RemoveMembers from '@assets/images/remove-members.svg';
import Rotate from '@assets/images/rotate-image.svg';
@@ -313,6 +314,7 @@ export {
Receipt,
ReceiptPlus,
ReceiptScan,
+ ReceiptSlash,
RemoveMembers,
ReceiptSearch,
Rotate,
diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx
index a82cbbe61668..ebec9b71fc04 100755
--- a/src/components/MoneyRequestConfirmationList.tsx
+++ b/src/components/MoneyRequestConfirmationList.tsx
@@ -328,6 +328,7 @@ function MoneyRequestConfirmationList({
const [didConfirmSplit, setDidConfirmSplit] = useState(false);
const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);
+ const [invalidAttachmentPromt, setInvalidAttachmentPromt] = useState(translate('attachmentPicker.protectedPDFNotSupported'));
const navigateBack = useCallback(
() => Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID)),
@@ -1100,7 +1101,14 @@ function MoneyRequestConfirmationList({
previewSourceURL={resolvedReceiptImage as string}
// We don't support scanning password protected PDF receipt
enabled={!isAttachmentInvalid}
- onPassword={() => setIsAttachmentInvalid(true)}
+ onPassword={() => {
+ setIsAttachmentInvalid(true);
+ setInvalidAttachmentPromt(translate('attachmentPicker.protectedPDFNotSupported'));
+ }}
+ onLoadError={() => {
+ setInvalidAttachmentPromt(translate('attachmentPicker.errorWhileSelectingCorruptedAttachment'));
+ setIsAttachmentInvalid(true);
+ }}
/>
) : (
{shouldShowAllFields && supplementaryFields}
@@ -1226,6 +1235,7 @@ function MoneyRequestConfirmationList({
transaction,
transactionID,
translate,
+ invalidAttachmentPromt,
],
);
diff --git a/src/components/PDFThumbnail/PDFThumbnailError.tsx b/src/components/PDFThumbnail/PDFThumbnailError.tsx
new file mode 100644
index 000000000000..0598a995e030
--- /dev/null
+++ b/src/components/PDFThumbnail/PDFThumbnailError.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import {View} from 'react-native';
+import Icon from '@components/Icon';
+import * as Expensicons from '@components/Icon/Expensicons';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import variables from '@styles/variables';
+
+function PDFThumbnailError() {
+ const styles = useThemeStyles();
+ const theme = useTheme();
+
+ return (
+
+
+
+ );
+}
+
+export default PDFThumbnailError;
diff --git a/src/components/PDFThumbnail/index.native.tsx b/src/components/PDFThumbnail/index.native.tsx
index 0232dba99f05..27d41ede3263 100644
--- a/src/components/PDFThumbnail/index.native.tsx
+++ b/src/components/PDFThumbnail/index.native.tsx
@@ -1,19 +1,21 @@
-import React from 'react';
+import React, {useState} from 'react';
import {View} from 'react-native';
import Pdf from 'react-native-pdf';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import useThemeStyles from '@hooks/useThemeStyles';
import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL';
+import PDFThumbnailError from './PDFThumbnailError';
import type PDFThumbnailProps from './types';
-function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword}: PDFThumbnailProps) {
+function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword, onLoadError}: PDFThumbnailProps) {
const styles = useThemeStyles();
const sizeStyles = [styles.w100, styles.h100];
+ const [failedToLoad, setFailedToLoad] = useState(false);
return (
-
- {enabled && (
+
+ {enabled && !failedToLoad && (
{
- if (!('message' in error && typeof error.message === 'string' && error.message.match(/password/i))) {
- return;
+ if (onLoadError) {
+ onLoadError();
}
- if (!onPassword) {
+ if ('message' in error && typeof error.message === 'string' && error.message.match(/password/i) && onPassword) {
+ onPassword();
return;
}
- onPassword();
+ setFailedToLoad(true);
}}
/>
)}
+ {failedToLoad && }
);
diff --git a/src/components/PDFThumbnail/index.tsx b/src/components/PDFThumbnail/index.tsx
index ce631f3b611f..8e79c027cf03 100644
--- a/src/components/PDFThumbnail/index.tsx
+++ b/src/components/PDFThumbnail/index.tsx
@@ -1,18 +1,20 @@
import pdfWorkerSource from 'pdfjs-dist/legacy/build/pdf.worker';
-import React, {useMemo} from 'react';
+import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import {Document, pdfjs, Thumbnail} from 'react-pdf';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import useThemeStyles from '@hooks/useThemeStyles';
import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL';
+import PDFThumbnailError from './PDFThumbnailError';
import type PDFThumbnailProps from './types';
if (!pdfjs.GlobalWorkerOptions.workerSrc) {
pdfjs.GlobalWorkerOptions.workerSrc = URL.createObjectURL(new Blob([pdfWorkerSource], {type: 'text/javascript'}));
}
-function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword}: PDFThumbnailProps) {
+function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword, onLoadError}: PDFThumbnailProps) {
const styles = useThemeStyles();
+ const [failedToLoad, setFailedToLoad] = useState(false);
const thumbnail = useMemo(
() => (
@@ -25,18 +27,31 @@ function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, ena
}}
externalLinkTarget="_blank"
onPassword={onPassword}
+ onLoad={() => {
+ setFailedToLoad(false);
+ }}
+ onLoadError={() => {
+ if (onLoadError) {
+ onLoadError();
+ }
+ setFailedToLoad(true);
+ }}
+ error={() => null}
>
),
- [isAuthTokenRequired, previewSourceURL, onPassword],
+ [isAuthTokenRequired, previewSourceURL, onPassword, onLoadError],
);
return (
-
- {enabled && thumbnail}
+
+
+ {enabled && !failedToLoad && thumbnail}
+ {failedToLoad && }
+
);
}
diff --git a/src/components/PDFThumbnail/types.ts b/src/components/PDFThumbnail/types.ts
index 11253e462aca..349669ecc33e 100644
--- a/src/components/PDFThumbnail/types.ts
+++ b/src/components/PDFThumbnail/types.ts
@@ -15,6 +15,9 @@ type PDFThumbnailProps = {
/** Callback to call if PDF is password protected */
onPassword?: () => void;
+
+ /** Callback to call if PDF can't be loaded(corrupted) */
+ onLoadError?: () => void;
};
export default PDFThumbnailProps;
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 653697e271a7..8ed0ef8207f1 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -356,7 +356,7 @@ export default {
expensifyDoesntHaveAccessToCamera: "Expensify can't take photos without access to your camera. Tap Settings to update permissions.",
attachmentError: 'Attachment error',
errorWhileSelectingAttachment: 'An error occurred while selecting an attachment, please try again.',
- errorWhileSelectingCorruptedImage: 'An error occurred while selecting a corrupted attachment, please try another file.',
+ errorWhileSelectingCorruptedAttachment: 'An error occurred while selecting a corrupted attachment, please try another file.',
takePhoto: 'Take photo',
chooseFromGallery: 'Choose from gallery',
chooseDocument: 'Choose document',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 5d184677655e..2316a5d09c9f 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -350,7 +350,7 @@ export default {
expensifyDoesntHaveAccessToCamera: 'Expensify no puede tomar fotos sin acceso a la cámara. Haz click en Configuración para actualizar los permisos.',
attachmentError: 'Error al adjuntar archivo',
errorWhileSelectingAttachment: 'Ha ocurrido un error al seleccionar un archivo adjunto. Por favor, inténtalo de nuevo.',
- errorWhileSelectingCorruptedImage: 'Ha ocurrido un error al seleccionar un archivo adjunto corrupto. Por favor, inténtalo con otro archivo.',
+ errorWhileSelectingCorruptedAttachment: 'Ha ocurrido un error al seleccionar un archivo adjunto corrupto. Por favor, inténtalo con otro archivo.',
takePhoto: 'Hacer una foto',
chooseFromGallery: 'Elegir de la galería',
chooseDocument: 'Elegir documento',
diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx
index ede79e009a49..0e74b7c392ae 100644
--- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx
+++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx
@@ -209,7 +209,7 @@ function IOURequestStepScan({
return true;
})
.catch(() => {
- setUploadReceiptError(true, 'attachmentPicker.attachmentError', 'attachmentPicker.errorWhileSelectingCorruptedImage');
+ setUploadReceiptError(true, 'attachmentPicker.attachmentError', 'attachmentPicker.errorWhileSelectingCorruptedAttachment');
return false;
});
}
diff --git a/src/styles/index.ts b/src/styles/index.ts
index bb3c69b47598..718942582801 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -4419,6 +4419,16 @@ const styles = (theme: ThemeColors) =>
maxWidth: 400,
},
+ pdfErrorPlaceholder: {
+ overflow: 'hidden',
+ borderWidth: 2,
+ borderColor: theme.cardBG,
+ borderRadius: variables.componentBorderRadiusLarge,
+ maxWidth: 400,
+ height: '100%',
+ backgroundColor: theme.highlightBG,
+ },
+
moneyRequestAttachReceipt: {
backgroundColor: theme.highlightBG,
borderColor: theme.border,
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index 6f1cac46d729..f81e2ad9fd51 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -190,6 +190,8 @@ export default {
eReceiptBGHeight: 540,
eReceiptBGHWidth: 335,
eReceiptTextContainerWidth: 263,
+ receiptPlaceholderIconWidth: 80,
+ receiptPlaceholderIconHeight: 80,
reportPreviewMaxWidth: 335,
reportActionImagesSingleImageHeight: 147,
reportActionImagesDoubleImageHeight: 138,