diff --git a/src/hooks/useFilesValidation.tsx b/src/hooks/useFilesValidation.tsx index 1b392bcb0edca..c49d49f281138 100644 --- a/src/hooks/useFilesValidation.tsx +++ b/src/hooks/useFilesValidation.tsx @@ -16,39 +16,29 @@ import { validateAttachment, validateImageForCorruption, } from '@libs/fileDownload/FileUtils'; -import type {ValidateAttachmentOptions} from '@libs/fileDownload/FileUtils'; import convertHeicImage from '@libs/fileDownload/heicConverter'; import CONST from '@src/CONST'; import type {FileObject} from '@src/types/utils/Attachment'; import useLocalize from './useLocalize'; import useThemeStyles from './useThemeStyles'; -const DEFAULT_IS_VALIDATING_RECEIPTS = true; - type ErrorObject = { error: ValueOf; fileExtension?: string; }; -type ValidationOptions = { - isValidatingReceipts?: boolean; -}; - const sortFilesByOriginalOrder = (files: FileObject[], orderMap: Map) => { return files.sort((a, b) => (orderMap.get(a.uri ?? '') ?? 0) - (orderMap.get(b.uri ?? '') ?? 0)); }; -function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransferItems: DataTransferItem[]) => void) { +function useFilesValidation(proceedWithFilesAction: (files: FileObject[], dataTransferItems: DataTransferItem[]) => void, isValidatingReceipts = true) { const styles = useThemeStyles(); const {translate} = useLocalize(); - - const [isValidatingReceipts, setIsValidatingReceipts] = useState(DEFAULT_IS_VALIDATING_RECEIPTS); - const [isValidatingMultipleFiles, setIsValidatingMultipleFiles] = useState(false); - const [isErrorModalVisible, setIsErrorModalVisible] = useState(false); const [fileError, setFileError] = useState | null>(null); const [pdfFilesToRender, setPdfFilesToRender] = useState([]); const [validFilesToUpload, setValidFilesToUpload] = useState([] as FileObject[]); + const [isValidatingMultipleFiles, setIsValidatingMultipleFiles] = useState(false); const [invalidFileExtension, setInvalidFileExtension] = useState(''); const [errorQueue, setErrorQueue] = useState([]); const [currentErrorIndex, setCurrentErrorIndex] = useState(0); @@ -111,7 +101,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer setIsErrorModalVisible(true); }; - const isValidFile = (originalFile: FileObject, item: DataTransferItem | undefined, validationOptions: ValidateAttachmentOptions) => { + const isValidFile = (originalFile: FileObject, item: DataTransferItem | undefined, isCheckingMultipleFiles?: boolean) => { if (item && item.kind === 'file' && 'webkitGetAsEntry' in item) { const entry = item.webkitGetAsEntry(); @@ -124,7 +114,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer return normalizeFileObject(originalFile) .then((normalizedFile) => validateImageForCorruption(normalizedFile).then(() => { - const error = validateAttachment(normalizedFile, validationOptions); + const error = validateAttachment(normalizedFile, isCheckingMultipleFiles, isValidatingReceipts); if (error) { const errorData = { error, @@ -180,12 +170,12 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer } } else if (validFiles.current.length > 0) { const sortedFiles = sortFilesByOriginalOrder(validFiles.current, originalFileOrder.current); - onFilesValidated(sortedFiles, dataTransferItemList.current); + proceedWithFilesAction(sortedFiles, dataTransferItemList.current); resetValidationState(); } - }, [deduplicateErrors, pdfFilesToRender.length, onFilesValidated, resetValidationState]); + }, [deduplicateErrors, pdfFilesToRender.length, proceedWithFilesAction, resetValidationState]); - const validateAndResizeFiles = (files: FileObject[], items: DataTransferItem[], validationOptions?: ValidationOptions) => { + const validateAndResizeFiles = (files: FileObject[], items: DataTransferItem[]) => { // Early return for empty files if (files.length === 0) { return; @@ -198,13 +188,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer originalFileOrder.current.set(file.uri ?? '', index); }); - Promise.all( - files.map((file, index) => - isValidFile(file, items.at(index), {isCheckingMultipleFiles: files.length > 1, isValidatingReceipts: validationOptions?.isValidatingReceipts ?? isValidatingReceipts}).then( - (isValid) => (isValid ? file : null), - ), - ), - ) + 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 ?? '')); @@ -274,18 +258,14 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer } } else if (processedFiles.length > 0) { const sortedFiles = sortFilesByOriginalOrder(processedFiles, originalFileOrder.current); - onFilesValidated(sortedFiles, dataTransferItemList.current); + proceedWithFilesAction(sortedFiles, dataTransferItemList.current); resetValidationState(); } } }); }; - const validateFiles = (files: FileObject[], items?: DataTransferItem[], validationOptions?: ValidationOptions) => { - if (validationOptions?.isValidatingReceipts) { - setIsValidatingReceipts(validationOptions.isValidatingReceipts); - } - + const validateFiles = (files: FileObject[], items?: DataTransferItem[]) => { if (files.length > 1) { setIsValidatingMultipleFiles(true); } @@ -296,7 +276,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer } setErrorAndOpenModal(CONST.FILE_VALIDATION_ERRORS.MAX_FILE_LIMIT_EXCEEDED); } else { - validateAndResizeFiles(files, items ?? [], validationOptions); + validateAndResizeFiles(files, items ?? []); } }; @@ -329,13 +309,13 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { if (sortedFiles.length !== 0) { - onFilesValidated(sortedFiles, dataTransferItemList.current); + proceedWithFilesAction(sortedFiles, dataTransferItemList.current); } resetValidationState(); }); } else { if (sortedFiles.length !== 0) { - onFilesValidated(sortedFiles, dataTransferItemList.current); + proceedWithFilesAction(sortedFiles, dataTransferItemList.current); } hideModalAndReset(); } @@ -370,7 +350,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer )) : undefined; - const getModalPrompt = () => { + const getModalPrompt = useCallback(() => { if (!fileError) { return ''; } @@ -384,7 +364,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer ); } return prompt; - }; + }, [fileError, invalidFileExtension, isValidatingReceipts, translate]); const ErrorModal = ( => { }); }; -type ValidateAttachmentOptions = { - isValidatingReceipts?: boolean; - isCheckingMultipleFiles?: boolean; -}; - -const validateAttachment = (file: FileObject, validationOptions?: ValidateAttachmentOptions) => { - const maxFileSize = validationOptions?.isValidatingReceipts ? CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE : CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE; +const validateAttachment = (file: FileObject, isCheckingMultipleFiles?: boolean, isValidatingReceipt?: boolean) => { + const maxFileSize = isValidatingReceipt ? CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE : CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE; - if (validationOptions?.isValidatingReceipts && !isValidReceiptExtension(file)) { - return validationOptions?.isCheckingMultipleFiles ? CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE_MULTIPLE : CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE; + if (isValidatingReceipt && !isValidReceiptExtension(file)) { + return isCheckingMultipleFiles ? CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE_MULTIPLE : CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE; } if (!Str.isImage(file.name ?? '') && !hasHeicOrHeifExtension(file) && (file?.size ?? 0) > maxFileSize) { - return validationOptions?.isCheckingMultipleFiles ? CONST.FILE_VALIDATION_ERRORS.FILE_TOO_LARGE_MULTIPLE : CONST.FILE_VALIDATION_ERRORS.FILE_TOO_LARGE; + return isCheckingMultipleFiles ? CONST.FILE_VALIDATION_ERRORS.FILE_TOO_LARGE_MULTIPLE : CONST.FILE_VALIDATION_ERRORS.FILE_TOO_LARGE; } - if (validationOptions?.isValidatingReceipts && (file?.size ?? 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { + if (isValidatingReceipt && (file?.size ?? 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) { return CONST.FILE_VALIDATION_ERRORS.FILE_TOO_SMALL; } @@ -783,5 +777,3 @@ export { cleanFileObject, cleanFileObjectName, }; - -export type {ValidateAttachmentOptions}; diff --git a/src/pages/home/report/ReportActionCompose/useAttachmentUploadValidation.ts b/src/pages/home/report/ReportActionCompose/useAttachmentUploadValidation.ts index 3add5f32a82a1..2dfe00ac83130 100644 --- a/src/pages/home/report/ReportActionCompose/useAttachmentUploadValidation.ts +++ b/src/pages/home/report/ReportActionCompose/useAttachmentUploadValidation.ts @@ -116,7 +116,7 @@ function useAttachmentUploadValidation({ ); }; - const {validateFiles, PDFValidationComponent, ErrorModal} = useFilesValidation(onFilesValidated); + const {validateFiles, PDFValidationComponent, ErrorModal} = useFilesValidation(onFilesValidated, false); const validateAttachments = useCallback( ({dragEvent, files}: {dragEvent?: DragEvent; files?: FileObject | FileObject[]}) => { @@ -161,7 +161,7 @@ function useAttachmentUploadValidation({ const filteredItems = dataTransferItems && validIndices.length > 0 ? validIndices.map((index) => dataTransferItems.at(index) ?? ({} as DataTransferItem)) : undefined; attachmentUploadType.current = 'attachment'; - validateFiles(fileObjects, filteredItems, {isValidatingReceipts: false}); + validateFiles(fileObjects, filteredItems); }, [isAttachmentPreviewActive, validateFiles], ); @@ -187,7 +187,7 @@ function useAttachmentUploadValidation({ } attachmentUploadType.current = 'receipt'; - validateFiles(files, items, {isValidatingReceipts: true}); + validateFiles(files, items); }, [policy, shouldAddOrReplaceReceipt, transactionID, validateFiles], ); diff --git a/tests/unit/FileUtilsTest.ts b/tests/unit/FileUtilsTest.ts index 45f02f3386085..f1b3e05d47f65 100644 --- a/tests/unit/FileUtilsTest.ts +++ b/tests/unit/FileUtilsTest.ts @@ -47,13 +47,13 @@ describe('FileUtils', () => { describe('validateAttachment', () => { it('should not return FILE_TOO_SMALL when validating small attachment', () => { const file = createMockFile('file.csv', CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE - 1); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: false, isValidatingReceipts: false}); + const error = FileUtils.validateAttachment(file, false, false); expect(error).not.toBe(CONST.FILE_VALIDATION_ERRORS.FILE_TOO_SMALL); }); it('should return FILE_TOO_SMALL when validating small receipt', () => { const file = createMockFile('receipt.jpg', CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE - 1); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: false, isValidatingReceipts: true}); + const error = FileUtils.validateAttachment(file, false, true); expect(error).toBe(CONST.FILE_VALIDATION_ERRORS.FILE_TOO_SMALL); }); @@ -65,31 +65,31 @@ describe('FileUtils', () => { it('should return FILE_TOO_LARGE_MULTIPLE when checking multiple files', () => { const file = createMockFile('file.pdf', CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE + 1); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: true, isValidatingReceipts: false}); + const error = FileUtils.validateAttachment(file, true); expect(error).toBe(CONST.FILE_VALIDATION_ERRORS.FILE_TOO_LARGE_MULTIPLE); }); it('should return WRONG_FILE_TYPE for invalid receipt extension', () => { const file = createMockFile('receipt.exe', CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE - 1); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: false, isValidatingReceipts: true}); + const error = FileUtils.validateAttachment(file, false, true); expect(error).toBe(CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE); }); it('should prioritize WRONG_FILE_TYPE over FILE_TOO_LARGE for receipts', () => { const file = createMockFile('receipt.exe', CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE + 10); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: false, isValidatingReceipts: true}); + const error = FileUtils.validateAttachment(file, false, true); expect(error).toBe(CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE); }); it('should return WRONG_FILE_TYPE_MULTIPLE when checking multiple invalid receipt files', () => { const file = createMockFile('receipt.exe', CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE + 10); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: true, isValidatingReceipts: true}); + const error = FileUtils.validateAttachment(file, true, true); expect(error).toBe(CONST.FILE_VALIDATION_ERRORS.WRONG_FILE_TYPE_MULTIPLE); }); it('should return empty string for valid image receipt', () => { const file = createMockFile('receipt.jpg', CONST.API_ATTACHMENT_VALIDATIONS.RECEIPT_MAX_SIZE - 1); - const error = FileUtils.validateAttachment(file, {isCheckingMultipleFiles: false, isValidatingReceipts: true}); + const error = FileUtils.validateAttachment(file, false, true); expect(error).toBe(''); }); });