Skip to content

Commit

Permalink
fix: prevent possible race condition on upload flows/folders (#4114)
Browse files Browse the repository at this point in the history
* ✨ (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
  • Loading branch information
Cristhianzl authored Oct 11, 2024
1 parent 8516f31 commit 6bad987
Showing 1 changed file with 45 additions and 20 deletions.
65 changes: 45 additions & 20 deletions src/frontend/src/helpers/create-file-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}

0 comments on commit 6bad987

Please sign in to comment.