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

fix: IOU Scan - In dark mode, the damaged PDF - file is barely visible. #40607

Merged
merged 19 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
12 changes: 12 additions & 0 deletions assets/images/receipt-slash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/AttachmentPicker/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB: If we replace the wording from Image to Attachment for the translate key, should we change the function name to showAttachmentCorruptionAlert ?

Alert.alert(translate('attachmentPicker.attachmentError'), translate('attachmentPicker.errorWhileSelectingCorruptedImage'));
Alert.alert(translate('attachmentPicker.attachmentError'), translate('attachmentPicker.errorWhileSelectingCorruptedAttachment'));
}, [translate]);

/**
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,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';
Expand Down Expand Up @@ -308,6 +309,7 @@ export {
Receipt,
ReceiptPlus,
ReceiptScan,
ReceiptSlash,
RemoveMembers,
ReceiptSearch,
Rotate,
Expand Down
16 changes: 13 additions & 3 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,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)),
Expand Down Expand Up @@ -1098,7 +1099,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);
}}
/>
) : (
<ReceiptImage
Expand Down Expand Up @@ -1127,6 +1135,7 @@ function MoneyRequestConfirmationList({
receiptThumbnail,
fileExtension,
isDistanceRequest,
translate,
],
);

Expand Down Expand Up @@ -1184,11 +1193,11 @@ function MoneyRequestConfirmationList({
)}
<View style={[styles.mb5]}>{shouldShowAllFields && supplementaryFields}</View>
<ConfirmModal
title={translate('attachmentPicker.wrongFileType')}
title={translate('attachmentPicker.attachmentError')}
onConfirm={navigateBack}
onCancel={navigateBack}
isVisible={isAttachmentInvalid}
prompt={translate('attachmentPicker.protectedPDFNotSupported')}
prompt={invalidAttachmentPromt}
confirmText={translate('common.close')}
shouldShowCancelButton={false}
/>
Expand Down Expand Up @@ -1224,6 +1233,7 @@ function MoneyRequestConfirmationList({
transaction,
transactionID,
translate,
invalidAttachmentPromt,
],
);

Expand Down
25 changes: 25 additions & 0 deletions src/components/PDFThumbnail/PDFThumbnailError.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<View style={[styles.justifyContentCenter, styles.pdfErrorPlaceholder, styles.alignItemsCenter]}>
<Icon
src={Expensicons.ReceiptSlash}
width={variables.receiptPlaceholderIconWidth}
height={variables.receiptPlaceholderIconHeight}
fill={theme.icon}
/>
</View>
);
}

export default PDFThumbnailError;
20 changes: 12 additions & 8 deletions src/components/PDFThumbnail/index.native.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<View style={[style, styles.overflowHidden]}>
<View style={[sizeStyles, styles.alignItemsCenter, styles.justifyContentCenter]}>
{enabled && (
<View style={[sizeStyles, !failedToLoad && styles.alignItemsCenter, styles.justifyContentCenter]}>
{enabled && !failedToLoad && (
<Pdf
fitPolicy={0}
trustAllCerts={false}
Expand All @@ -22,16 +24,18 @@ function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, ena
singlePage
style={sizeStyles}
onError={(error) => {
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 && <PDFThumbnailError />}
</View>
</View>
);
Expand Down
25 changes: 20 additions & 5 deletions src/components/PDFThumbnail/index.tsx
Original file line number Diff line number Diff line change
@@ -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(
() => (
Expand All @@ -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}
>
<View pointerEvents="none">
<Thumbnail pageIndex={0} />
</View>
</Document>
),
[isAuthTokenRequired, previewSourceURL, onPassword],
[isAuthTokenRequired, previewSourceURL, onPassword, onLoadError],
);

return (
<View style={[style, styles.overflowHidden]}>
<View style={[styles.w100, styles.h100, styles.alignItemsCenter, styles.justifyContentCenter]}>{enabled && thumbnail}</View>
<View style={[style, styles.overflowHidden, failedToLoad && styles.h100]}>
<View style={[styles.w100, styles.h100, !failedToLoad && {...styles.alignItemsCenter, ...styles.justifyContentCenter}]}>
{enabled && !failedToLoad && thumbnail}
{failedToLoad && <PDFThumbnailError />}
</View>
</View>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/components/PDFThumbnail/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,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',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,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',
Expand Down
2 changes: 1 addition & 1 deletion src/pages/iou/request/step/IOURequestStepScan/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ function IOURequestStepScan({
return true;
})
.catch(() => {
setUploadReceiptError(true, 'attachmentPicker.attachmentError', 'attachmentPicker.errorWhileSelectingCorruptedImage');
setUploadReceiptError(true, 'attachmentPicker.attachmentError', 'attachmentPicker.errorWhileSelectingCorruptedAttachment');
return false;
});
}
Expand Down
10 changes: 10 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4400,6 +4400,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,
Expand Down
2 changes: 2 additions & 0 deletions src/styles/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ export default {
eReceiptBGHeight: 540,
eReceiptBGHWidth: 335,
eReceiptTextContainerWidth: 263,
receiptPlaceholderIconWidth: 80,
receiptPlaceholderIconHeight: 80,
reportPreviewMaxWidth: 335,
reportActionImagesSingleImageHeight: 147,
reportActionImagesDoubleImageHeight: 138,
Expand Down
Loading