fix: prevent possible race condition on upload flows/folders (#4114)

*  (create-file-upload.ts): improve file upload functionality by adding cleanup logic and handling edge cases for resolving file selection

* 🐛 (create-file-upload.ts): fix removing input element from the DOM by checking if it is contained in the document body before removal
💡 (create-file-upload.ts): add a comment to clarify the purpose of the setTimeout function for a fallback timeout of 1 minute

*  (create-file-upload.ts): change createFileUpload function to be asynchronous to support Promise return type for better handling of file upload operations

* 📝 (create-file-upload.ts): improve error handling when removing input element from the DOM
📝 (create-file-upload.ts): remove unnecessary comment about timeout value in the code
This commit is contained in:
Cristhian Zanforlin Lousa 2024-10-11 13:17:20 -03:00 committed by GitHub
commit 6bad987f38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,34 +2,59 @@ export async function createFileUpload(props?: {
accept?: string;
multiple?: boolean;
}): Promise<File[]> {
let lock = false;
return new Promise((resolve) => {
const input = document.createElement("input");
input.type = "file";
input.accept = props?.accept ?? ".json";
input.multiple = props?.multiple ?? true;
input.style.display = "none";
// add a change event listener to the file input
input.onchange = async (e: Event) => {
lock = true;
resolve(Array.from((e.target as HTMLInputElement).files!));
document.body.removeChild(input);
let isResolved = false;
const cleanup = () => {
// Check if the input element still exists in the DOM before attempting to remove it
if (input && document.body.contains(input)) {
try {
document.body.removeChild(input);
} catch (error) {
console.warn("Error removing input element:", error);
}
}
window.removeEventListener("focus", handleFocus);
};
window.addEventListener(
"focus",
() => {
setTimeout(() => {
if (!lock) {
resolve([]);
document.body.removeChild(input);
}
}, 300);
},
{ once: true },
);
// add the input element to the body to ensure it is part of the DOM
const handleChange = (e: Event) => {
if (!isResolved) {
isResolved = true;
const files = Array.from((e.target as HTMLInputElement).files!);
cleanup();
resolve(files);
}
};
const handleFocus = () => {
setTimeout(() => {
if (!isResolved) {
isResolved = true;
cleanup();
resolve([]);
}
}, 300);
};
input.addEventListener("change", handleChange);
window.addEventListener("focus", handleFocus);
document.body.appendChild(input);
// trigger the file input click event to open the file dialog
input.click();
// Fallback timeout to ensure resolution
setTimeout(() => {
if (!isResolved) {
isResolved = true;
cleanup();
resolve([]);
}
}, 60000);
});
}