diff --git a/src/components/AttachmentComposerModal.tsx b/src/components/AttachmentComposerModal.tsx index ef80838bf26c..8825746efdb7 100644 --- a/src/components/AttachmentComposerModal.tsx +++ b/src/components/AttachmentComposerModal.tsx @@ -1,15 +1,13 @@ import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; import type {View} from 'react-native'; -import {InteractionManager} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import type {OnyxEntry} from 'react-native-onyx'; import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated'; -import type {ValueOf} from 'type-fest'; import useFilesValidation from '@hooks/useFilesValidation'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import {cleanFileName, getFileValidationErrorText} from '@libs/fileDownload/FileUtils'; +import {cleanFileName} from '@libs/fileDownload/FileUtils'; import CONST from '@src/CONST'; import type * as OnyxTypes from '@src/types/onyx'; import type ModalType from '@src/types/utils/ModalType'; @@ -19,7 +17,6 @@ import useCarouselArrows from './Attachments/AttachmentCarousel/useCarouselArrow import useAttachmentErrors from './Attachments/AttachmentView/useAttachmentErrors'; import type {Attachment} from './Attachments/types'; import Button from './Button'; -import ConfirmModal from './ConfirmModal'; import HeaderGap from './HeaderGap'; import HeaderWithBackButton from './HeaderWithBackButton'; import Modal from './Modal'; @@ -37,7 +34,7 @@ type ImagePickerResponse = { type FileObject = Partial; type ChildrenProps = { - displayFilesInModal: (data: FileObject[]) => void; + displayFilesInModal: (data: FileObject[], items?: DataTransferItem[]) => void; show: () => void; }; @@ -72,15 +69,12 @@ function AttachmentComposerModal({onConfirm, onModalShow = () => {}, onModalHide const {shouldShowArrows, setShouldShowArrows, autoHideArrows, cancelAutoHideArrows} = useCarouselArrows(); const [isModalOpen, setIsModalOpen] = useState(false); - const [fileError, setFileError] = useState | null>(null); - const [isFileErrorModalVisible, setIsFileErrorModalVisible] = useState(false); const [modalType, setModalType] = useState(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE); const [validFilesToUpload, setValidFilesToUpload] = useState([]); const [attachments, setAttachments] = useState([]); const [page, setPage] = useState(0); const [currentAttachment, setCurrentAttachment] = useState(null); - // TODO: remove unnecessary logic, ideally in a follow-up PR to avoid breaking changes/regressions /** * If our attachment is a PDF, return the unswipeable Modal type. */ @@ -112,20 +106,6 @@ function AttachmentComposerModal({onConfirm, onModalShow = () => {}, onModalHide setIsModalOpen(false); }, [isModalOpen, onConfirm, validFilesToUpload, currentAttachment]); - const closeConfirmModal = useCallback(() => { - setIsFileErrorModalVisible(false); - }, []); - - // TODO: Check if this function is still needed, as it doesn't work. - const isDirectoryCheck = useCallback((data: FileObject) => { - if ('webkitGetAsEntry' in data && (data as DataTransferItem).webkitGetAsEntry()?.isDirectory) { - setFileError(CONST.FILE_VALIDATION_ERRORS.FOLDER_NOT_ALLOWED); - setIsFileErrorModalVisible(true); - return false; - } - return true; - }, []); - /** * Sanitizes file names and ensures proper URI references for file system compatibility */ @@ -159,7 +139,7 @@ function AttachmentComposerModal({onConfirm, onModalShow = () => {}, onModalHide return; } - if (validFilesToUpload.length > 0 && !fileError) { + if (validFilesToUpload.length > 0) { // Convert all files to attachments const newAttachments = validFilesToUpload.map((fileObject) => { const source = fileObject.uri ?? ''; @@ -178,57 +158,47 @@ function AttachmentComposerModal({onConfirm, onModalShow = () => {}, onModalHide setIsModalOpen(true); } - }, [fileError, validFilesToUpload, convertFileToAttachment, getModalType]); + }, [validFilesToUpload, convertFileToAttachment, getModalType]); const {ErrorModal, validateFiles, PDFValidationComponent} = useFilesValidation(setValidFilesToUpload, false); - const confirmAndContinue = () => { - if (fileError === CONST.FILE_VALIDATION_ERRORS.MAX_FILE_LIMIT_EXCEEDED) { - validateFiles(validFilesToUpload); - } - setIsFileErrorModalVisible(false); - InteractionManager.runAfterInteractions(() => { - setFileError(null); - }); - }; - const validateAndDisplayMultipleFilesToUpload = useCallback( - (data: FileObject[]) => { + (data: FileObject[], items?: DataTransferItem[]) => { if (!data?.length) { return; } + const validIndices: number[] = []; const fileObjects = data - .map((item) => { + .map((item, index) => { let fileObject = item; if ('getAsFile' in item && typeof item.getAsFile === 'function') { fileObject = item.getAsFile() as FileObject; } - return cleanFileObjectName(fileObject); + const cleanedFileObject = cleanFileObjectName(fileObject); + if (cleanedFileObject !== null) { + validIndices.push(index); + } + return cleanedFileObject; }) .filter((fileObject): fileObject is FileObject => fileObject !== null); - if (!fileObjects.length || fileObjects.some((fileObject) => !isDirectoryCheck(fileObject))) { + if (!fileObjects.length) { return; } - validateFiles(fileObjects); + + // Create a filtered items array that matches the fileObjects + const filteredItems = items && validIndices.length > 0 ? validIndices.map((index) => items.at(index) ?? ({} as DataTransferItem)) : undefined; + + validateFiles(fileObjects, filteredItems); }, - [cleanFileObjectName, isDirectoryCheck, validateFiles], + [cleanFileObjectName, validateFiles], ); const closeModal = useCallback(() => { setIsModalOpen(false); }, []); - const closeAndResetModal = useCallback(() => { - closeConfirmModal(); - closeModal(); - InteractionManager.runAfterInteractions(() => { - setFileError(null); - setValidFilesToUpload([]); - }); - }, [closeConfirmModal, closeModal]); - const openModal = useCallback(() => { setIsModalOpen(true); }, []); @@ -318,16 +288,6 @@ function AttachmentComposerModal({onConfirm, onModalShow = () => {}, onModalHide - {children?.({ displayFilesInModal: validateAndDisplayMultipleFilesToUpload, show: openModal, diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index cb1590b0dffa..188c4ab4a00a 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -1,4 +1,3 @@ -import {Str} from 'expensify-common'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, Keyboard, View} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; @@ -14,7 +13,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import attachmentModalHandler from '@libs/AttachmentModalHandler'; import fileDownload from '@libs/fileDownload'; -import {cleanFileName, getFileName, validateImageForCorruption} from '@libs/fileDownload/FileUtils'; +import {getFileName} from '@libs/fileDownload/FileUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {hasEReceipt, hasMissingSmartscanFields, hasReceipt, hasReceiptSource, isReceiptBeingScanned} from '@libs/TransactionUtils'; @@ -28,7 +27,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import type ModalType from '@src/types/utils/ModalType'; import viewRef from '@src/types/utils/viewRef'; import AttachmentCarousel from './Attachments/AttachmentCarousel'; import AttachmentCarouselPagerContext from './Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; @@ -63,7 +61,6 @@ type ImagePickerResponse = { type FileObject = Partial; type ChildrenProps = { - displayFileInModal: (data: FileObject) => void; show: () => void; }; @@ -202,7 +199,6 @@ function AttachmentModal({ const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState(null); const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null); const [sourceState, setSourceState] = useState(() => source); - const [modalType, setModalType] = useState(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE); const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false); const [isDownloadButtonReadyToBeShown, setIsDownloadButtonReadyToBeShown] = React.useState(true); const isPDFLoadError = useRef(false); @@ -219,8 +215,6 @@ function AttachmentModal({ const [currentAttachmentLink, setCurrentAttachmentLink] = useState(attachmentLink); const {setAttachmentError, isErrorInAttachment, clearAttachmentErrors} = useAttachmentErrors(); - // TODO: Remove the logic for validating files, ideally in a follow-up to avoid breaking changes/regressions - const [file, setFile] = useState( originalFileName ? { @@ -251,17 +245,6 @@ function AttachmentModal({ [onCarouselAttachmentChange], ); - /** - * If our attachment is a PDF, return the unswipeable Modal type. - */ - const getModalType = useCallback( - (sourceURL: string, fileObject: FileObject) => - sourceURL && (Str.isPDF(sourceURL) || (fileObject && Str.isPDF(fileObject.name ?? translate('attachmentView.unknownFilename')))) - ? CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE - : CONST.MODAL.MODAL_TYPE.CENTERED, - [translate], - ); - const setDownloadButtonVisibility = useCallback( (isButtonVisible: boolean) => { if (isDownloadButtonReadyToBeShown === isButtonVisible) { @@ -326,91 +309,6 @@ function AttachmentModal({ Navigation.goBack(); }, [transaction]); - const isValidFile = useCallback( - (fileObject: FileObject) => - 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; - } - - 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) { - setIsAttachmentInvalid(true); - setAttachmentInvalidReasonTitle('attachmentPicker.attachmentError'); - setAttachmentInvalidReason('attachmentPicker.folderNotAllowedMessage'); - return false; - } - return true; - }, []); - - const validateAndDisplayFileToUpload = useCallback( - (data: FileObject) => { - if (!data || !isDirectoryCheck(data)) { - return; - } - let fileObject = data; - if ('getAsFile' in data && typeof data.getAsFile === 'function') { - fileObject = data.getAsFile() as FileObject; - } - if (!fileObject) { - return; - } - - isValidFile(fileObject).then((isValid) => { - if (!isValid) { - 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 = 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], - ); - /** * Closes the modal. * @param {boolean} [shouldCallDirectly] If true, directly calls `onModalClose`. @@ -520,7 +418,7 @@ function AttachmentModal({ return ( <> { @@ -701,10 +599,7 @@ function AttachmentModal({ /> )} - {children?.({ - displayFileInModal: validateAndDisplayFileToUpload, - show: openModal, - })} + {children?.({show: openModal})} ); } diff --git a/src/hooks/useFilesValidation.tsx b/src/hooks/useFilesValidation.tsx index 9411a73684b9..d047edbb5031 100644 --- a/src/hooks/useFilesValidation.tsx +++ b/src/hooks/useFilesValidation.tsx @@ -27,6 +27,10 @@ type ErrorObject = { fileExtension?: string; }; +const sortFilesByOriginalOrder = (files: FileObject[], orderMap: Map) => { + return files.sort((a, b) => (orderMap.get(a.uri ?? '') ?? 0) - (orderMap.get(b.uri ?? '') ?? 0)); +}; + function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => void, isValidatingReceipts = true) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -43,7 +47,16 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi const validatedPDFs = useRef([]); const validFiles = useRef([]); const filesToValidate = useRef([]); + const dataTransferItemList = useRef([]); const collectedErrors = useRef([]); + const originalFileOrder = useRef>(new Map()); + + const updateFileOrderMapping = useCallback((oldFile: FileObject | undefined, newFile: FileObject) => { + const originalIndex = originalFileOrder.current.get(oldFile?.uri ?? ''); + if (originalIndex !== undefined) { + originalFileOrder.current.set(newFile.uri ?? '', originalIndex); + } + }, []); const deduplicateErrors = useCallback((errors: ErrorObject[]) => { const uniqueErrors = new Set(); @@ -70,7 +83,9 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi validatedPDFs.current = []; validFiles.current = []; filesToValidate.current = []; + dataTransferItemList.current = []; collectedErrors.current = []; + originalFileOrder.current.clear(); }, [setIsLoaderVisible]); const hideModalAndReset = useCallback(() => { @@ -85,7 +100,16 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi setIsErrorModalVisible(true); }; - const isValidFile = (originalFile: FileObject, isCheckingMultipleFiles?: boolean) => { + const isValidFile = (originalFile: FileObject, item: DataTransferItem | undefined, isCheckingMultipleFiles?: boolean) => { + if (item && item.kind === 'file' && 'webkitGetAsEntry' in item) { + const entry = item.webkitGetAsEntry(); + + if (entry?.isDirectory) { + collectedErrors.current.push({error: CONST.FILE_VALIDATION_ERRORS.FOLDER_NOT_ALLOWED}); + return Promise.resolve(false); + } + } + return normalizeFileObject(originalFile) .then((normalizedFile) => validateImageForCorruption(normalizedFile).then(() => { @@ -144,12 +168,13 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi setIsErrorModalVisible(true); } } else if (validFiles.current.length > 0) { - proceedWithFilesAction(validFiles.current); + const sortedFiles = sortFilesByOriginalOrder(validFiles.current, originalFileOrder.current); + proceedWithFilesAction(sortedFiles); resetValidationState(); } }, [deduplicateErrors, pdfFilesToRender.length, proceedWithFilesAction, resetValidationState]); - const validateAndResizeFiles = (files: FileObject[]) => { + const validateAndResizeFiles = (files: FileObject[], items: DataTransferItem[]) => { // Early return for empty files if (files.length === 0) { return; @@ -158,7 +183,11 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi // Reset collected errors for new validation collectedErrors.current = []; - Promise.all(files.map((file) => isValidFile(file, files.length > 1).then((isValid) => (isValid ? file : null)))) + files.forEach((file, index) => { + originalFileOrder.current.set(file.uri ?? '', index); + }); + + Promise.all(files.map((file, index) => isValidFile(file, items.at(index), files.length > 1).then((isValid) => (isValid ? file : null)))) .then((validationResults) => { const filteredResults = validationResults.filter((result): result is FileObject => result !== null); const pdfsToLoad = filteredResults.filter((file) => Str.isPDF(file.name ?? '')); @@ -169,9 +198,16 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi setIsLoaderVisible(true); return Promise.all(otherFiles.map((file) => convertHeicImageToJpegPromise(file))).then((convertedImages) => { + convertedImages.forEach((convertedFile, index) => { + updateFileOrderMapping(otherFiles.at(index), convertedFile); + }); + // Check if we need to resize images if (convertedImages.some((file) => (file.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE)) { return Promise.all(convertedImages.map((file) => resizeImageIfNeeded(file))).then((processedFiles) => { + processedFiles.forEach((resizedFile, index) => { + updateFileOrderMapping(convertedImages.at(index), resizedFile); + }); setIsLoaderVisible(false); return Promise.resolve({processedFiles, pdfsToLoad}); }); @@ -187,6 +223,9 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi if (otherFiles.some((file) => (file.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE)) { setIsLoaderVisible(true); return Promise.all(otherFiles.map((file) => resizeImageIfNeeded(file))).then((processedFiles) => { + processedFiles.forEach((resizedFile, index) => { + updateFileOrderMapping(otherFiles.at(index), resizedFile); + }); setIsLoaderVisible(false); return Promise.resolve({processedFiles, pdfsToLoad}); }); @@ -217,29 +256,33 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi setIsErrorModalVisible(true); } } else if (processedFiles.length > 0) { - proceedWithFilesAction(processedFiles); + const sortedFiles = sortFilesByOriginalOrder(processedFiles, originalFileOrder.current); + proceedWithFilesAction(sortedFiles); resetValidationState(); } } }); }; - const validateFiles = (files: FileObject[]) => { + const validateFiles = (files: FileObject[], items?: DataTransferItem[]) => { if (files.length > 1) { setIsValidatingMultipleFiles(true); } if (files.length > CONST.API_ATTACHMENT_VALIDATIONS.MAX_FILE_LIMIT) { filesToValidate.current = files.slice(0, CONST.API_ATTACHMENT_VALIDATIONS.MAX_FILE_LIMIT); + if (items) { + dataTransferItemList.current = items.slice(0, CONST.API_ATTACHMENT_VALIDATIONS.MAX_FILE_LIMIT); + } setErrorAndOpenModal(CONST.FILE_VALIDATION_ERRORS.MAX_FILE_LIMIT_EXCEEDED); } else { - validateAndResizeFiles(files); + validateAndResizeFiles(files, items ?? []); } }; const onConfirm = () => { if (fileError === CONST.FILE_VALIDATION_ERRORS.MAX_FILE_LIMIT_EXCEEDED) { setIsErrorModalVisible(false); - validateAndResizeFiles(filesToValidate.current); + validateAndResizeFiles(filesToValidate.current, dataTransferItemList.current); return; } @@ -247,7 +290,7 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi const nextIndex = currentErrorIndex + 1; const nextError = errorQueue.at(nextIndex); if (nextError) { - if (validFiles.current.length === 0) { + if (isValidatingMultipleFiles && currentErrorIndex === errorQueue.length - 2 && validFilesToUpload.length === 0) { setIsValidatingMultipleFiles(false); } setCurrentErrorIndex(nextIndex); @@ -257,16 +300,21 @@ function useFilesValidation(proceedWithFilesAction: (files: FileObject[]) => voi } } + const sortedFiles = sortFilesByOriginalOrder(validFilesToUpload, originalFileOrder.current); // If we're validating attachments we need to use InteractionManager to ensure // the error modal is dismissed before opening the attachment modal if (!isValidatingReceipts && fileError) { setIsErrorModalVisible(false); InteractionManager.runAfterInteractions(() => { - proceedWithFilesAction(validFilesToUpload); + if (sortedFiles.length !== 0) { + proceedWithFilesAction(sortedFiles); + } resetValidationState(); }); } else { - proceedWithFilesAction(validFilesToUpload); + if (sortedFiles.length !== 0) { + proceedWithFilesAction(sortedFiles); + } hideModalAndReset(); } }; diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts index 5524fdda39db..4dd555406fa0 100644 --- a/src/libs/fileDownload/FileUtils.ts +++ b/src/libs/fileDownload/FileUtils.ts @@ -466,6 +466,7 @@ const getFileValidationErrorText = ( reason: '', }; } + const maxSize = isValidatingReceipt ? CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE : CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE; switch (validationError) { case CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE: return { @@ -490,7 +491,7 @@ const getFileValidationErrorText = ( return { title: translateLocal('attachmentPicker.someFilesCantBeUploaded'), reason: translateLocal('attachmentPicker.sizeLimitExceeded', { - maxUploadSizeInMB: additionalData.maxUploadSizeInMB ?? CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE / 1024 / 1024, + maxUploadSizeInMB: additionalData.maxUploadSizeInMB ?? maxSize / 1024 / 1024, }), }; case CONST.FILE_VALIDATION_ERRORS.FILE_TOO_SMALL: diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 4320683f8518..1e6c258c067e 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -442,7 +442,7 @@ function SearchPage({route}: SearchPageProps) { file.uri = URL.createObjectURL(file); }); - validateFiles(files); + validateFiles(files, Array.from(e.dataTransfer?.items ?? [])); }; const createExportAll = useCallback(() => { diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 84cbafd38c96..aae3713d7431 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -534,7 +534,7 @@ function ReportActionCompose({ const file = e?.dataTransfer?.files?.[0]; if (file) { file.uri = URL.createObjectURL(file); - validateFiles([file]); + validateFiles([file], Array.from(e.dataTransfer?.items ?? [])); return; } } @@ -548,7 +548,7 @@ function ReportActionCompose({ file.uri = URL.createObjectURL(file); }); - validateFiles(files); + validateFiles(files, Array.from(e.dataTransfer?.items ?? [])); }; return ( @@ -596,14 +596,14 @@ function ReportActionCompose({ file.uri = URL.createObjectURL(file); return file; }); - displayFilesInModal(files); + displayFilesInModal(files, Array.from(event.dataTransfer?.items ?? [])); return; } const data = event.dataTransfer?.files[0]; if (data) { data.uri = URL.createObjectURL(data); - displayFilesInModal([data]); + displayFilesInModal([data], Array.from(event.dataTransfer?.items ?? [])); } }; diff --git a/src/pages/home/report/ReportDropUI.tsx b/src/pages/home/report/ReportDropUI.tsx deleted file mode 100644 index fad58d60bbfa..000000000000 --- a/src/pages/home/report/ReportDropUI.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import DragAndDropConsumer from '@components/DragAndDrop/Consumer'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import Text from '@components/Text'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; - -type ReportDropUIProps = { - /** Callback to execute when a file is dropped. */ - onDrop: (event: DragEvent) => void; -}; -function ReportDropUI({onDrop}: ReportDropUIProps) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - return ( - - - - - - {translate('reportActionCompose.dropToUpload')} - - - ); -} - -ReportDropUI.displayName = 'ReportDropUI'; - -export default ReportDropUI; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 01a035a11bb9..f795ae547b0d 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -974,7 +974,7 @@ function IOURequestStepConfirmation({ const file = e?.dataTransfer?.files[0]; if (file) { file.uri = URL.createObjectURL(file); - validateFiles([file]); + validateFiles([file], Array.from(e.dataTransfer?.items)); } }; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 50d3f9b8d3e2..53268458251f 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -841,6 +841,7 @@ function IOURequestStepScan({ setIsLoaderVisible(true)} fileLimit={shouldAcceptMultipleFiles ? CONST.API_ATTACHMENT_VALIDATIONS.MAX_FILE_LIMIT : 1} + shouldValidateImage={false} > {({openPicker}) => (