From 7fb72800ab60537534b95ea70fab609ce1707188 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 8 Sep 2026 18:58:02 -0500 Subject: [PATCH] fix: show loading while retrying cached file previews Co-Authored-By: Paperclip --- .../WorkFolderBrowser.selection.test.tsx | 23 ++++++++++++++++++- ui/src/components/WorkFolderBrowser.tsx | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ui/src/components/WorkFolderBrowser.selection.test.tsx b/ui/src/components/WorkFolderBrowser.selection.test.tsx index 7f1904cf2d..ab5355ebe2 100644 --- a/ui/src/components/WorkFolderBrowser.selection.test.tsx +++ b/ui/src/components/WorkFolderBrowser.selection.test.tsx @@ -6,8 +6,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { TooltipProvider } from "@/components/ui/tooltip"; import { WorkFolderBrowser } from "./WorkFolderBrowser"; -const api = vi.hoisted(() => ({ list: vi.fn(), sync: vi.fn(), operation: vi.fn() })); +const api = vi.hoisted(() => ({ list: vi.fn(), sync: vi.fn(), operation: vi.fn(), preview: vi.fn(), downloadUrl: vi.fn() })); vi.mock("@/api/work-folders", () => ({ workFoldersApi: api })); +vi.mock("@/components/FileViewerSheet", () => ({ FileContentViewer: ({ content }: { content: { content: { data: string } } }) =>
{content.content.data}
})); const owner = { companyId: "company", scope: "task" as const, ownerId: "task" }; const a = { id: "a", path: "a.txt", kind: "file" }; const b = { id: "b", path: "b.txt", kind: "file" }; @@ -29,6 +30,8 @@ beforeEach(async () => { active = [a, b]; deleted = []; api.list.mockImplementation(async (_owner, trash) => ({ files: [...(trash ? deleted : active)] })); api.sync.mockResolvedValue([]); + api.preview.mockImplementation(async (_owner, file) => ({ content: { data: `contents of ${file.path}` } })); + api.downloadUrl.mockReturnValue("/download"); api.operation.mockImplementation(async (_owner, operation) => { if (operation.action === "delete") { deleted.push(...active.filter((file) => file.path === operation.path)); @@ -49,6 +52,24 @@ afterEach(async () => { await act(async () => root.unmount()); client.clear(); container.remove(); vi.clearAllMocks(); }); describe("cached file selection and retained trash", () => { + it("does not flash an old preview error while retrying a cached file", async () => { + const row = (path: string) => container.querySelector(`[data-file-tree-path="${path}"]`)!; + await click(row("a.txt")); + expect(container.textContent).toContain("contents of a.txt"); + api.preview.mockRejectedValueOnce(new Error("File preview could not be loaded")); + await act(async () => { await client.invalidateQueries({ queryKey: ["work-folders", owner.companyId, owner.scope, owner.ownerId, "preview"] }); }); + await settle(); + expect(container.textContent).toContain("File preview could not be loaded"); + await click(row("b.txt")); + let resolvePreview!: (value: { content: { data: string } }) => void; + api.preview.mockImplementationOnce(() => new Promise((resolve) => { resolvePreview = resolve; })); + await click(row("a.txt")); + expect(container.textContent).toContain("Loading preview…"); + expect(container.textContent).not.toContain("File preview could not be loaded"); + await act(async () => { resolvePreview({ content: { data: "restored file contents" } }); }); + await settle(); + expect(container.textContent).toContain("restored file contents"); + }); it("only shows trash action for checked files and restores from the Trash tab", async () => { expect(button("to trash")).toBeUndefined(); await click(checkbox("a.txt")); diff --git a/ui/src/components/WorkFolderBrowser.tsx b/ui/src/components/WorkFolderBrowser.tsx index 36e0de7c5b..5345d1d8f5 100644 --- a/ui/src/components/WorkFolderBrowser.tsx +++ b/ui/src/components/WorkFolderBrowser.tsx @@ -143,7 +143,7 @@ export function WorkFolderBrowser({ owner, exampleFiles, readOnly = false, fillH {selected &&
{selected.path} {selected.kind === "file" && !exampleFiles && }
} - {preview.isLoading ?

Loading preview…

: preview.error ?

{preview.error.message}

: preview.data ? + {preview.isLoading || (preview.isFetching && preview.isError) ?

Loading preview…

: preview.error ?

{preview.error.message}

: preview.data ?
:

Select a file to preview it.

} }