Continue independent cached file deletions after a failure

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-09-11 15:23:26 -05:00
parent 814f853359
commit eb44b68f58
2 changed files with 11 additions and 5 deletions

View File

@ -99,13 +99,13 @@ describe("cached file selection and retained trash", () => {
expect(checkbox("a.txt")).not.toBeNull();
expect(deleted).toEqual([]);
});
it("refreshes partial successes and leaves only failed files selected for retry", async () => {
it.each([["a.txt", "b.txt"], ["b.txt", "a.txt"]])("refreshes partial successes when selected in order %s, %s", async (first, second) => {
const operate = api.operation.getMockImplementation()!;
api.operation.mockImplementation(async (target, operation) => {
if (operation.path === "b.txt") throw new Error("Storage unavailable");
return operate(target, operation);
});
await click(checkbox("a.txt")); await click(checkbox("b.txt"));
await click(checkbox(first)); await click(checkbox(second));
await click(button("Move 2 files to trash")!);
expect(checkbox("a.txt")).toBeNull();
expect(checkbox("b.txt").checked).toBe(true);

View File

@ -69,11 +69,17 @@ export function WorkFolderBrowser({ owner, exampleFiles, readOnly = false, fillH
if (action.type === "upload") for (const file of action.files) await workFoldersApi.upload(owner, file, directory ? `${directory}/${file.name}` : file.name, crypto.randomUUID());
else if (action.type === "mkdir") { await workFoldersApi.operation(owner, { action: "mkdir", path: directory }, crypto.randomUUID()); setExpanded((before) => new Set([...before, directory])); }
else if (action.type === "deleteSelected") {
const failures: string[] = [];
for (const path of action.paths) {
await workFoldersApi.operation(owner, { action: "delete", path }, crypto.randomUUID());
setCheckedFiles((before) => new Set([...before].filter((candidate) => candidate !== path && !candidate.startsWith(`${path}/`))));
setSelectedPath((before) => before === path || before?.startsWith(`${path}/`) ? null : before);
try {
await workFoldersApi.operation(owner, { action: "delete", path }, crypto.randomUUID());
setCheckedFiles((before) => new Set([...before].filter((candidate) => candidate !== path && !candidate.startsWith(`${path}/`))));
setSelectedPath((before) => before === path || before?.startsWith(`${path}/`) ? null : before);
} catch (error) {
failures.push(`${path}: ${error instanceof Error ? error.message : "Could not move to trash"}`);
}
}
if (failures.length > 0) throw new Error(failures.join("; "));
}
else if (action.type === "restore" || action.type === "purge") await workFoldersApi.operation(owner, { action: action.type, fileId: action.fileId }, crypto.randomUUID());
else for (const run of (syncQuery.data ?? []).filter((run) => run.active)) await workFoldersApi.refresh(owner, run.runId);