Skip to content

Commit 416dfe0

Browse files
authored
Merge pull request Expensify#87848 from nkdengineer/fix/87605
2 parents fba471a + 5fd7988 commit 416dfe0

1 file changed

Lines changed: 71 additions & 25 deletions

File tree

src/hooks/useFilesValidation.tsx

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Str} from 'expensify-common';
2-
import React, {useRef, useState} from 'react';
2+
import React, {useEffect, useRef, useState} from 'react';
33
import {InteractionManager} from 'react-native';
44
import type {ValueOf} from 'type-fest';
55
import ConfirmModal from '@components/ConfirmModal';
@@ -18,6 +18,7 @@ import useLocalize from './useLocalize';
1818
import useThemeStyles from './useThemeStyles';
1919

2020
const DEFAULT_IS_VALIDATING_RECEIPTS = true;
21+
const MIN_LOADER_VISIBLE_DURATION_MS = 200;
2122

2223
type ValidationOptions = {
2324
isValidatingReceipts?: boolean;
@@ -56,6 +57,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer
5657
const dataTransferItemList = useRef<DataTransferItem[]>([]);
5758
const collectedErrors = useRef<FileValidationError[]>([]);
5859
const originalFileOrder = useRef<Map<string, number>>(new Map());
60+
const loaderTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
5961

6062
const updateFileOrderMapping = (oldFile: FileObject | undefined, newFile: FileObject) => {
6163
const originalIndex = originalFileOrder.current.get(oldFile?.uri ?? '');
@@ -76,6 +78,16 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer
7678
});
7779
};
7880

81+
useEffect(() => {
82+
return () => {
83+
if (!loaderTimeoutRef.current) {
84+
return;
85+
}
86+
clearTimeout(loaderTimeoutRef.current);
87+
loaderTimeoutRef.current = undefined;
88+
};
89+
}, []);
90+
7991
const reset = () => {
8092
setIsValidatingFiles(false);
8193
setIsValidatingReceipts(undefined);
@@ -141,6 +153,14 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer
141153
return;
142154
}
143155

156+
let loaderStartTime: number | undefined;
157+
const showLoader = () => {
158+
if (loaderStartTime === undefined) {
159+
loaderStartTime = Date.now();
160+
}
161+
setIsLoaderVisible(true);
162+
};
163+
144164
// Reset collected errors for new validation
145165
collectedErrors.current = [];
146166

@@ -186,7 +206,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer
186206
);
187207

188208
if (filesToConvert.length > 0) {
189-
setIsLoaderVisible(true);
209+
showLoader();
190210

191211
const convertedFilesToResize: FileObject[] = [];
192212
const convertedFiles: FileObject[] = [];
@@ -233,7 +253,7 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer
233253
}
234254

235255
if (filesToResize.length > 0) {
236-
setIsLoaderVisible(true);
256+
showLoader();
237257

238258
const toResizeResults = await Promise.allSettled(filesToResize.map((file) => resizeImageIfNeeded(file)));
239259

@@ -253,32 +273,58 @@ function useFilesValidation(onFilesValidated: (files: FileObject[], dataTransfer
253273
}
254274
}
255275

256-
setIsLoaderVisible(false);
276+
const handleNext = () => {
277+
if (pdfsToLoad.length) {
278+
validFiles.current = validNonPdfFiles;
279+
setPdfFilesToRender(pdfsToLoad);
280+
return;
281+
}
257282

258-
if (pdfsToLoad.length) {
259-
validFiles.current = validNonPdfFiles;
260-
setPdfFilesToRender(pdfsToLoad);
261-
return;
262-
}
283+
if (validNonPdfFiles.length > 0) {
284+
setValidFilesToUpload(validNonPdfFiles);
285+
}
263286

264-
if (validNonPdfFiles.length > 0) {
265-
setValidFilesToUpload(validNonPdfFiles);
266-
}
287+
if (collectedErrors.current.length > 0) {
288+
const uniqueErrors = deduplicateErrors(collectedErrors.current);
289+
setErrorQueue(uniqueErrors);
290+
setCurrentErrorIndex(0);
291+
const firstError = uniqueErrors.at(0);
292+
if (firstError) {
293+
setFileError(firstError);
294+
setIsErrorModalVisible(true);
295+
}
296+
} else if (validNonPdfFiles.length > 0) {
297+
const sortedFiles = sortFilesByOriginalOrder(validNonPdfFiles, originalFileOrder.current);
298+
onFilesValidated(sortedFiles, dataTransferItemList.current);
299+
reset();
300+
}
301+
};
267302

268-
if (collectedErrors.current.length > 0) {
269-
const uniqueErrors = Array.from(new Set(collectedErrors.current.map((error) => JSON.stringify(error)))).map((errorStr) => JSON.parse(errorStr) as FileValidationError);
270-
setErrorQueue(uniqueErrors);
271-
setCurrentErrorIndex(0);
272-
const firstError = uniqueErrors.at(0);
273-
if (firstError) {
274-
setFileError(firstError);
275-
setIsErrorModalVisible(true);
303+
const hideLoaderAndHandleNext = () => {
304+
setIsLoaderVisible(false);
305+
handleNext();
306+
};
307+
308+
const extendLoaderIfNeeded = () => {
309+
if (loaderStartTime === undefined) {
310+
hideLoaderAndHandleNext();
311+
return;
276312
}
277-
} else if (validNonPdfFiles.length > 0) {
278-
const sortedFiles = sortFilesByOriginalOrder(validNonPdfFiles, originalFileOrder.current);
279-
onFilesValidated(sortedFiles, dataTransferItemList.current);
280-
reset();
281-
}
313+
314+
const elapsedTime = Date.now() - loaderStartTime;
315+
const shouldDelayHide = collectedErrors.current.length > 0 && elapsedTime < MIN_LOADER_VISIBLE_DURATION_MS;
316+
317+
if (!shouldDelayHide) {
318+
hideLoaderAndHandleNext();
319+
return;
320+
}
321+
322+
loaderTimeoutRef.current = setTimeout(() => {
323+
hideLoaderAndHandleNext();
324+
}, MIN_LOADER_VISIBLE_DURATION_MS - elapsedTime);
325+
};
326+
327+
extendLoaderIfNeeded();
282328
}
283329

284330
const validateFiles = (files: FileObject[], items?: DataTransferItem[], validationOptions?: ValidationOptions) => {

0 commit comments

Comments
 (0)