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

feat: add PDFThumbnail to preview PDF receipt #35255

Merged
merged 13 commits into from
Mar 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({

const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false);

const hideRecieptModal = () => {
const navigateBack = () => {
Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, transaction.transactionID, reportID));
};

Expand Down Expand Up @@ -868,6 +868,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
// We don't support scaning password protected PDF receipt
enabled={!isAttachmentInvalid}
onPassword={() => setIsAttachmentInvalid(true)}
isClickable={false}
/>
) : (
<Image
Expand Down Expand Up @@ -939,10 +940,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
{shouldShowAllFields && supplementaryFields}
<ConfirmModal
title={translate('attachmentPicker.wrongFileType')}
onConfirm={hideRecieptModal}
onCancel={hideRecieptModal}
onConfirm={navigateBack}
onCancel={navigateBack}
isVisible={isAttachmentInvalid}
prompt={translate('receipt.protectedPDFNotSupportedError')}
prompt={translate('attachmentPicker.protectedPDFNotSupported')}
confirmText={translate('common.close')}
shouldShowCancelButton={false}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/components/PDFThumbnail/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* These style overrides are necessary so that the PDF thumbnail shows default pointer when it's not clickable */
.react-pdf__Thumbnail--notClickable {
cursor: default;
}
29 changes: 17 additions & 12 deletions src/components/PDFThumbnail/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import React, {useMemo} from 'react';
// @ts-expect-error - We use the same method as PDFView to import the worker
import pdfWorkerSource from 'pdfjs-dist/legacy/build/pdf.worker';
import React, {useEffect, useMemo} from 'react';
eh2077 marked this conversation as resolved.
Show resolved Hide resolved
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 './index.css';
import type PDFThumbnailProps from './types';

if (!pdfjs.GlobalWorkerOptions.workerSrc) {
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/legacy/build/pdf.worker.min.js',
// @ts-expect-error - It is a recommended step for import worker - https://github.com/wojtekmaj/react-pdf/blob/main/packages/react-pdf/README.md#import-worker-recommended
import.meta.url,
).toString();
}

function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword = () => {}}: PDFThumbnailProps) {
function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword = () => {}, isClickable = true}: PDFThumbnailProps) {
const styles = useThemeStyles();

useEffect(() => {
const workerURL = URL.createObjectURL(new Blob([pdfWorkerSource], {type: 'text/javascript'}));
if (pdfjs.GlobalWorkerOptions.workerSrc !== workerURL) {
pdfjs.GlobalWorkerOptions.workerSrc = workerURL;
}
}, []);

eh2077 marked this conversation as resolved.
Show resolved Hide resolved
const thumbnail = useMemo(
() => (
<Document
Expand All @@ -31,10 +33,13 @@ function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, ena
onPassword();
}}
>
<Thumbnail pageIndex={0} />
<Thumbnail
pageIndex={0}
className={isClickable ? '' : 'react-pdf__Thumbnail--notClickable'}
/>
eh2077 marked this conversation as resolved.
Show resolved Hide resolved
</Document>
),
[isAuthTokenRequired, previewSourceURL, onPassword],
[isAuthTokenRequired, previewSourceURL, onPassword, isClickable],
);

return (
Expand Down
5 changes: 4 additions & 1 deletion src/components/PDFThumbnail/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ type PDFThumbnailProps = {
/** Any additional styles to apply */
style?: StyleProp<ViewStyle>;

/** Whether the image requires an authToken */
/** Whether the PDF thumbnail requires an authToken */
isAuthTokenRequired?: boolean;

/** Whether the PDF thumbnail can be loaded */
enabled?: boolean;

/** Callback to call if PDF is password protected */
onPassword?: () => void;

/** Whether the PDF thumbnail is clickable */
isClickable?: boolean;
eh2077 marked this conversation as resolved.
Show resolved Hide resolved
};

export default PDFThumbnailProps;
4 changes: 2 additions & 2 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,9 @@ export default {
attachmentTooSmall: 'Attachment too small',
sizeNotMet: 'Attachment size must be greater than 240 bytes.',
wrongFileType: 'Invalid file type',
notAllowedExtension: 'This file type is not allowed.',
notAllowedExtension: 'This file type is not allowed',
folderNotAllowedMessage: 'Uploading a folder is not allowed. Try a different file.',
protectedPDFNotSupported: 'Password-protected PDF is not supported',
},
avatarCropModal: {
title: 'Edit photo',
Expand Down Expand Up @@ -572,7 +573,6 @@ export default {
deleteReceipt: 'Delete receipt',
deleteConfirmation: 'Are you sure you want to delete this receipt?',
addReceipt: 'Add receipt',
protectedPDFNotSupportedError: 'Password-protected PDF is not supported.',
},
iou: {
amount: 'Amount',
Expand Down
4 changes: 2 additions & 2 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,9 @@ export default {
attachmentTooSmall: 'Archivo adjunto demasiado pequeño',
sizeNotMet: 'El archivo adjunto debe ser más grande que 240 bytes.',
wrongFileType: 'Tipo de archivo inválido',
notAllowedExtension: 'Este tipo de archivo no es compatible.',
notAllowedExtension: 'Este tipo de archivo no es compatible',
folderNotAllowedMessage: 'Subir una carpeta no está permitido. Prueba con otro archivo.',
protectedPDFNotSupported: 'Los PDFs con password no son compatibles',
eh2077 marked this conversation as resolved.
Show resolved Hide resolved
},
avatarCropModal: {
title: 'Editar foto',
Expand Down Expand Up @@ -565,7 +566,6 @@ export default {
deleteReceipt: 'Eliminar recibo',
deleteConfirmation: '¿Estás seguro de que quieres borrar este recibo?',
addReceipt: 'Añadir recibo',
protectedPDFNotSupportedError: 'Los PDFs con password no son compatibles.',
},
iou: {
amount: 'Importe',
Expand Down
Loading