From b8124b8546524e2fd92521f7e5ccd47bf24dc493 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 22 Aug 2024 11:20:08 +0800 Subject: [PATCH] show attachment error when file is corrupted --- src/components/AttachmentModal.tsx | 107 +++++++++++------- .../Attachments/AttachmentView/index.tsx | 5 + .../ReportActionCompose.tsx | 7 +- 3 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index 28f44aabb068..14b7ac6f2313 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -179,6 +179,7 @@ function AttachmentModal({ const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false); const [confirmButtonFadeAnimation] = useState(() => new Animated.Value(1)); const [isDownloadButtonReadyToBeShown, setIsDownloadButtonReadyToBeShown] = React.useState(true); + const isPDFLoadError = useRef(false); const {windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const nope = useSharedValue(false); @@ -288,23 +289,34 @@ function AttachmentModal({ Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1')); }, [transaction, report]); - const isValidFile = useCallback((fileObject: FileObject) => { - if (fileObject.size && fileObject.size > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { - setIsAttachmentInvalid(true); - setAttachmentInvalidReasonTitle('attachmentPicker.attachmentTooLarge'); - setAttachmentInvalidReason('attachmentPicker.sizeExceeded'); - return false; - } + const isValidFile = useCallback( + (fileObject: FileObject) => + FileUtils.validateImageForCorruption(fileObject) + .then(() => { + if (fileObject.size && fileObject.size > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { + setIsAttachmentInvalid(true); + setAttachmentInvalidReasonTitle('attachmentPicker.attachmentTooLarge'); + setAttachmentInvalidReason('attachmentPicker.sizeExceeded'); + return false; + } - if (fileObject.size && fileObject.size < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { - setIsAttachmentInvalid(true); - setAttachmentInvalidReasonTitle('attachmentPicker.attachmentTooSmall'); - setAttachmentInvalidReason('attachmentPicker.sizeNotMet'); - return false; - } + if (fileObject.size && fileObject.size < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { + setIsAttachmentInvalid(true); + setAttachmentInvalidReasonTitle('attachmentPicker.attachmentTooSmall'); + setAttachmentInvalidReason('attachmentPicker.sizeNotMet'); + return false; + } - return true; - }, []); + return true; + }) + .catch(() => { + setIsAttachmentInvalid(true); + setAttachmentInvalidReasonTitle('attachmentPicker.attachmentError'); + setAttachmentInvalidReason('attachmentPicker.errorWhileSelectingCorruptedAttachment'); + return false; + }), + [], + ); const isDirectoryCheck = useCallback((data: FileObject) => { if ('webkitGetAsEntry' in data && (data as DataTransferItem).webkitGetAsEntry()?.isDirectory) { @@ -329,34 +341,35 @@ function AttachmentModal({ return; } - if (!isValidFile(fileObject)) { - return; - } - - if (fileObject instanceof File) { - /** - * Cleaning file name, done here so that it covers all cases: - * upload, drag and drop, copy-paste - */ - let updatedFile = fileObject; - const cleanName = FileUtils.cleanFileName(updatedFile.name); - if (updatedFile.name !== cleanName) { - updatedFile = new File([updatedFile], cleanName, {type: updatedFile.type}); + isValidFile(fileObject).then((isValid) => { + if (!isValid) { + return; } - const inputSource = URL.createObjectURL(updatedFile); - updatedFile.uri = inputSource; - const inputModalType = getModalType(inputSource, updatedFile); - setIsModalOpen(true); - setSourceState(inputSource); - setFile(updatedFile); - setModalType(inputModalType); - } else if (fileObject.uri) { - const inputModalType = getModalType(fileObject.uri, fileObject); - setIsModalOpen(true); - setSourceState(fileObject.uri); - setFile(fileObject); - setModalType(inputModalType); - } + if (fileObject instanceof File) { + /** + * Cleaning file name, done here so that it covers all cases: + * upload, drag and drop, copy-paste + */ + let updatedFile = fileObject; + const cleanName = FileUtils.cleanFileName(updatedFile.name); + if (updatedFile.name !== cleanName) { + updatedFile = new File([updatedFile], cleanName, {type: updatedFile.type}); + } + const inputSource = URL.createObjectURL(updatedFile); + updatedFile.uri = inputSource; + const inputModalType = getModalType(inputSource, updatedFile); + setIsModalOpen(true); + setSourceState(inputSource); + setFile(updatedFile); + setModalType(inputModalType); + } else if (fileObject.uri) { + const inputModalType = getModalType(fileObject.uri, fileObject); + setIsModalOpen(true); + setSourceState(fileObject.uri); + setFile(fileObject); + setModalType(inputModalType); + } + }); }, [isValidFile, getModalType, isDirectoryCheck], ); @@ -482,6 +495,12 @@ function AttachmentModal({ onModalHide={() => { onModalHide(); setShouldLoadAttachment(false); + if (isPDFLoadError.current) { + isPDFLoadError.current = false; + setIsAttachmentInvalid(true); + setAttachmentInvalidReasonTitle('attachmentPicker.attachmentError'); + setAttachmentInvalidReason('attachmentPicker.errorWhileSelectingCorruptedAttachment'); + } }} propagateSwipe initialFocus={() => { @@ -543,6 +562,10 @@ function AttachmentModal({ isAuthTokenRequired={isAuthTokenRequiredState} file={file} onToggleKeyboard={setIsConfirmButtonDisabled} + onPDFLoadError={() => { + isPDFLoadError.current = true; + setIsModalOpen(false); + }} isWorkspaceAvatar={isWorkspaceAvatar} maybeIcon={maybeIcon} fallbackSource={fallbackSource} diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx index 39c25706bbfe..b5eb3891d27f 100644 --- a/src/components/Attachments/AttachmentView/index.tsx +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -56,6 +56,9 @@ type AttachmentViewProps = AttachmentViewOnyxProps & /** Notify parent that the UI should be modified to accommodate keyboard */ onToggleKeyboard?: (shouldFadeOut: boolean) => void; + /** A callback when the PDF fails to load */ + onPDFLoadError?: () => void; + /** Extra styles to pass to View wrapper */ containerStyles?: StyleProp; @@ -88,6 +91,7 @@ function AttachmentView({ shouldShowDownloadIcon, containerStyles, onToggleKeyboard, + onPDFLoadError: onPDFLoadErrorProp = () => {}, isFocused, isUsedInCarousel, isUsedInAttachmentModal, @@ -182,6 +186,7 @@ function AttachmentView({ const onPDFLoadError = () => { setHasPDFFailedToLoad(true); + onPDFLoadErrorProp(); }; // We need the following View component on android native diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 005824fa949f..ae5ee9cc2f96 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -537,8 +537,11 @@ function ReportActionCompose({ if (isAttachmentPreviewActive) { return; } - const data = event.dataTransfer?.items[0]; - displayFileInModal(data as unknown as FileObject); + const data = event.dataTransfer?.files[0]; + if (data) { + data.uri = URL.createObjectURL(data); + displayFileInModal(data); + } }} />