fix: mark failed uploads in store, surface errors, validate extensions on drop

- uploadFile now catches errors and sets status to 'failed' with error message
- Both create page and drag-drop check uploadFiles results for failures and
  show error notifications
- Drag-drop validates file extensions against bucket allowedFileExtensions
  before uploading, rejecting disallowed files with a notification
This commit is contained in:
Prem Palanisamy
2026-04-09 12:08:00 +01:00
parent b9334abf18
commit b06e45f281
3 changed files with 62 additions and 20 deletions
+23 -16
View File
@@ -82,22 +82,29 @@ const createUploader = () => {
n.files.unshift(newFile);
return n;
});
const uploadedFile = await temporaryStorage(region, projectId).createFile({
bucketId,
fileId: id ?? ID.unique(),
file,
permissions,
onProgress: (progress) => {
newFile.$id = progress.$id;
newFile.progress = progress.progress;
newFile.status = progress.progress === 100 ? 'success' : 'pending';
updateFile(progress.$id, newFile);
}
});
newFile.$id = uploadedFile.$id;
newFile.progress = 100;
newFile.status = 'success';
updateFile(newFile.$id, newFile);
try {
const uploadedFile = await temporaryStorage(region, projectId).createFile({
bucketId,
fileId: id ?? ID.unique(),
file,
permissions,
onProgress: (progress) => {
newFile.$id = progress.$id;
newFile.progress = progress.progress;
newFile.status = progress.progress === 100 ? 'success' : 'pending';
updateFile(progress.$id, newFile);
}
});
newFile.$id = uploadedFile.$id;
newFile.progress = 100;
newFile.status = 'success';
updateFile(newFile.$id, newFile);
} catch (e) {
newFile.status = 'failed';
newFile.error = e?.message ?? 'Upload failed';
updateFile(newFile.$id, newFile);
throw e;
}
};
const uploadFiles = async (
@@ -105,9 +105,30 @@
isDragging = false;
if (!event.dataTransfer?.files?.length) return;
const allowedExtensions: string[] = data.bucket.allowedFileExtensions ?? [];
const droppedFiles = Array.from(event.dataTransfer.files);
const count = droppedFiles.length;
const validFiles: File[] = [];
const rejectedFiles: File[] = [];
for (const file of droppedFiles) {
const ext = file.name.split('.').pop()?.toLowerCase();
if (allowedExtensions.length && (!ext || !allowedExtensions.includes(ext))) {
rejectedFiles.push(file);
} else {
validFiles.push(file);
}
}
if (rejectedFiles.length) {
addNotification({
type: 'error',
message: `${rejectedFiles.length} file(s) rejected — only ${allowedExtensions.join(', ')} allowed`
});
}
if (!validFiles.length) return;
const count = validFiles.length;
addNotification({
type: 'success',
message: count === 1 ? 'File upload in progress' : `${count} file uploads in progress`
@@ -115,18 +136,25 @@
trackEvent(Submit.FileCreate, { customId: false });
const filesToUpload = droppedFiles.map((file) => ({
const filesToUpload = validFiles.map((file) => ({
id: ID.unique(),
file
}));
await uploader.uploadFiles(
const results = await uploader.uploadFiles(
page.params.region,
page.params.project,
page.params.bucket,
filesToUpload,
[]
);
const failures = results.filter((r) => r.status === 'rejected');
if (failures.length) {
addNotification({
type: 'error',
message: `${failures.length} file(s) failed to upload`
});
}
invalidate(Dependencies.FILES);
}
@@ -69,7 +69,14 @@
trackEvent(Submit.FileCreate, {
customId: !!id
});
await promise;
const results = await promise;
const failures = results.filter((r) => r.status === 'rejected');
if (failures.length) {
addNotification({
type: 'error',
message: `${failures.length} file(s) failed to upload`
});
}
invalidate(Dependencies.FILES);
} catch (e) {
addNotification({