Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ name: Compile Backend

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ name: Compile Frontend

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
Expand Down
47 changes: 24 additions & 23 deletions frontend/src/pages/Collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,20 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
};

// Concurrency-aware scheduler state
let activeUploads = 0;
const [activeUploadsCount, setActiveUploadsCount] = createSignal(0);
const MAX_CONCURRENT_UPLOADS = 3;

// Debounced queue pumping to avoid microtask storms when many files are pending
let pumpScheduled = false;
const requestPump = () => {
if (pumpScheduled) return;
pumpScheduled = true;
queueMicrotask(() => {
pumpScheduled = false;
pumpQueue();
});
};

const waitWhilePaused = async () => {
while (isPaused()) {
await new Promise(r => setTimeout(r, 200));
Expand Down Expand Up @@ -67,7 +78,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
});
});

activeUploads++;
setActiveUploadsCount(c => c + 1);
try {
await uploadFileInChunks(
sf,
Expand Down Expand Up @@ -95,40 +106,29 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
return ({ ...prev, [sf.uniqueId]: { ...prev[sf.uniqueId], status: 'error', errorMessage: error?.message || 'Upload failed' } });
});
} finally {
activeUploads--;
queueMicrotask(() => pumpQueue());
setActiveUploadsCount(c => Math.max(0, c - 1));
requestPump();
}
};

const pumpQueue = () => {
if (!open()) return;
if (isPaused()) return;

// Ensure progress map entries exist
setUploadProgressMap(prev => {
const updated = { ...prev };
selectedUploadFiles().forEach(sf => {
if (!updated[sf.uniqueId]) {
updated[sf.uniqueId] = { id: sf.uniqueId, name: sf.file.name, progress: 0, status: 'pending' };
}
});
return updated;
});

const currentMap = uploadProgressMap();
const available = Math.max(0, MAX_CONCURRENT_UPLOADS - activeUploads);
const available = Math.max(0, MAX_CONCURRENT_UPLOADS - activeUploadsCount());
if (available === 0) {
setIsUploading(true);
return;
}

const candidates = selectedUploadFiles().filter(sf => {
const st = currentMap[sf.uniqueId]?.status;
const st = currentMap[sf.uniqueId]?.status ?? 'pending';
return (st === 'pending' || st === 'error') && !cancelledFiles.has(sf.uniqueId);
}).slice(0, available);

if (candidates.length === 0) {
if (activeUploads === 0) setIsUploading(false);
if (activeUploadsCount() === 0) setIsUploading(false);
return;
}

Expand Down Expand Up @@ -183,7 +183,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
});
return updatedMap;
});
if (!isPaused()) queueMicrotask(() => pumpQueue());
if (!isPaused()) requestPump();
};

const handleFileChange = (event: Event) => {
Expand All @@ -194,7 +194,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
if (input) input.value = '';
};

const addDroppedFiles = (files: FileList | File[]) => { addFiles(files); if (open() && !isPaused()) queueMicrotask(() => pumpQueue()); };
const addDroppedFiles = (files: FileList | File[]) => { addFiles(files); if (open() && !isPaused()) requestPump(); };

onMount(() => {
const handler = (e: Event) => {
Expand Down Expand Up @@ -241,7 +241,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
return status === 'pending' || status === 'error';
}).length;
if (pendingCount > 0 && !isPaused()) {
queueMicrotask(() => pumpQueue());
requestPump();
}
}
});
Expand All @@ -263,7 +263,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
}));
}
} else if (modifying() === "new") {
queueMicrotask(() => pumpQueue());
requestPump();
}
setSelectedExistingFiles([]);
// Don't close automatically for uploads, let user see progress.
Expand Down Expand Up @@ -296,6 +296,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
activeControllers.forEach(c => c.abort());
activeControllers.clear();
cancelledFiles.clear();
pumpScheduled = false;
}
}}>
<Dialog.Trigger class={`cursor-pointer hover:text-gray-300 text-white flex justify-center items-center bg-blue-600 hover:bg-blue-800 p-[0.2vh] px-[1vh] rounded-[1vh] font-bold ${!props.isMobile && 'translate-y-[4vh]'}`}>
Expand Down Expand Up @@ -383,7 +384,7 @@ const AddFilePopup: Component<{collectionId: string, isMobile?: boolean}> = (pro
activeControllers.forEach(c => c.abort());
} else {
// Resume: pump queue immediately
queueMicrotask(() => pumpQueue());
requestPump();
}
}}
>
Expand Down