From 5980384219b7a92fa91f81387bb267618cf25b30 Mon Sep 17 00:00:00 2001 From: AtAFork Date: Wed, 4 Nov 2026 15:51:37 -0600 Subject: [PATCH 1/8] feat(web): hide viewed files in the pull request Code tab --- .../PullRequestCodeTab.browser.test.tsx | 37 +++++++++++-- .../pullRequest/PullRequestCodeTab.tsx | 54 ++++++++++++++++--- docs/user/source-control.md | 2 + 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index ce6795cc8f4ff..3cb64607b0917 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -4,12 +4,13 @@ import { SourceControlProviderKind, type PullRequestDetailView, type PullRequestDiffResult, + type PullRequestFilesViewedResult, } from "@t3tools/contracts"; import { expect, it, vi } from "vite-plus/test"; import "~/index.css"; -const { diffAtom, pagedDiffAtom } = await vi.hoisted(async () => { +const { diffAtom, pagedDiffAtom, filesViewedAtom } = await vi.hoisted(async () => { const { AsyncResult, Atom } = await import("effect/reactivity"); const filePatch = (path: string) => [ @@ -47,6 +48,15 @@ const { diffAtom, pagedDiffAtom } = await vi.hoisted(async () => { pagedDiffAtom: Atom.family((cursor: string) => Atom.make(() => AsyncResult.success(pages[cursor]!)), ), + filesViewedAtom: Atom.make(() => + AsyncResult.success({ + files: [ + { path: "early-0.ts", state: "viewed" }, + { path: "early-1.ts", state: "dismissed" }, + ], + truncated: false, + }), + ), }; }); @@ -66,6 +76,7 @@ vi.mock(import("~/state/pullRequests"), async (original) => { mocks.diffCommits.push(input.commit); return mocks.paged ? pagedDiffAtom(input.cursor ?? "first") : diffAtom; }) as never, + filesViewed: (() => filesViewedAtom) as never, }, }; }); @@ -128,17 +139,19 @@ const detail: PullRequestDetailView = { function setup({ paged }: { paged: boolean }) { mocks.paged = paged; mocks.diffCommits = []; + localStorage.clear(); } const tab = ( refreshToken: number, revealRequest: { readonly path: string; readonly nonce: number } | null = null, + tabDetail: PullRequestDetailView = detail, ) => ( {}} environmentId={EnvironmentId.make("environment")} - reference={detail} - detail={detail} + reference={tabDetail} + detail={tabDetail} selectedCommitOid={null} onSelectedCommitChange={() => {}} onRefresh={() => {}} @@ -203,6 +216,24 @@ it("pages forward to a file asked for that is not loaded yet, then scrolls to it await expectFileHeaderShown(screen, container, "late.ts"); }); +it("hides viewed files but keeps one pushed to since it was viewed", async () => { + setup({ paged: true }); + const viewedDetail = { + ...detail, + capabilities: { ...detail.capabilities, viewedFiles: "host" as const }, + }; + await using screen = await render(tab(0, null, viewedDetail), { container: pagedContainer() }); + await expect.element(screen.getByText("early-0.ts", { exact: true }).first()).toBeVisible(); + const hideViewed = screen.getByRole("button", { name: "Hide viewed files" }); + + await hideViewed.click(); + + await expect.element(screen.getByText("early-0.ts", { exact: true })).not.toBeInTheDocument(); + await expect.element(screen.getByText("early-1.ts", { exact: true }).first()).toBeVisible(); + await hideViewed.click(); + await expect.element(screen.getByText("early-0.ts", { exact: true }).first()).toBeVisible(); +}); + it("never requests the diff of a selected commit the pull request no longer has", async () => { setup({ paged: false }); const container = document.createElement("div"); diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index 8c551ee7c7858..7b887b485b617 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -19,6 +19,7 @@ import { ChevronDownIcon, ChevronRightIcon, Columns2Icon, + EyeOffIcon, FlaskConicalOffIcon, FolderTreeIcon, InfoIcon, @@ -138,6 +139,7 @@ type ReviewAnnotation = DiffLineAnnotation; const COMMIT_PAGE_SIZE = 10; const PULL_REQUEST_FILE_TREE_STORAGE_KEY = "t3code.pullRequestFileTreeOpen"; +const PULL_REQUEST_HIDE_VIEWED_FILES_STORAGE_KEY = "t3code.pullRequestHideViewedFiles"; interface DiffSlice { readonly source: PullRequestDiffResult; @@ -382,6 +384,11 @@ function PullRequestCodeTab({ Schema.Boolean, ); const [hideTestFiles, setHideTestFiles] = useHideTestFiles(); + const [hideViewedFiles, setHideViewedFiles] = useLocalStorage( + PULL_REQUEST_HIDE_VIEWED_FILES_STORAGE_KEY, + false, + Schema.Boolean, + ); const [selectedLines, setSelectedLines] = useState<{ id: string; range: SelectedLineRange; @@ -569,15 +576,24 @@ function PullRequestCodeTab({ const testFileCount = loadedFiles.filter((file) => isTestChangedFile(resolveFileDiffPath(file)), ).length; - // A requested file stays visible so a jump to a hidden test still lands. - const files = hideTestFiles + // A requested file stays visible so a jump to a hidden file still lands. + const isRevealTarget = (path: string) => path === revealRequest?.path; + const reviewFiles = hideTestFiles ? loadedFiles.filter((file) => { const path = resolveFileDiffPath(file); - return path === revealRequest?.path || !isTestChangedFile(path); + return isRevealTarget(path) || !isTestChangedFile(path); }) : loadedFiles; - const hiddenTestFileCount = loadedFiles.length - files.length; - const filePaths = files.map((file) => resolveFileDiffPath(file)); + const hiddenTestFileCount = loadedFiles.length - reviewFiles.length; + const viewedFileCount = reviewFiles.filter((file) => + isFileViewed(resolveFileDiffPath(file)), + ).length; + const files = hideViewedFiles + ? reviewFiles.filter((file) => { + const path = resolveFileDiffPath(file); + return isRevealTarget(path) || !isFileViewed(path); + }) + : reviewFiles; const omittedFileStats = new Map( loadedSlices.flatMap((slice) => slice.omittedFileStats.map((file) => [file.path, file] as const), @@ -1136,10 +1152,10 @@ function PullRequestCodeTab({ {hiddenTestFileCount} test {hiddenTestFileCount === 1 ? "file" : "files"} hidden ) : null} - {filesViewed.enabled && files.length > 0 ? ( + {filesViewed.enabled && reviewFiles.length > 0 ? ( - {filePaths.filter(isFileViewed).length} / {files.length} + {viewedFileCount} / {reviewFiles.length} {viewedFilesStore === "environment" ? `viewed in ${APP_BASE_NAME}` : "viewed"} @@ -1368,6 +1384,26 @@ function PullRequestCodeTab({ ) : null} + {viewedFileCount > 0 ? ( + + setHideViewedFiles(Boolean(pressed))} + /> + } + > + + + + {hideViewedFiles ? "Show viewed files" : "Hide viewed files"} + + + ) : null} {fileKeys.length > 0 ? ( - {commit === null ? `This ${term} has no file changes.` : "This commit has no file changes."} + {loadedFiles.length > 0 ? "All files are hidden." : noFilesMessage}

, ); } diff --git a/docs/user/source-control.md b/docs/user/source-control.md index 9eb2751f9315f..5da3344f524e3 100644 --- a/docs/user/source-control.md +++ b/docs/user/source-control.md @@ -252,6 +252,8 @@ ordering, not the guide's layout or which files it covers. Tick a file off in the **Code** tab once you have read it and it collapses; the toolbar keeps a running count. A tick belongs to the pull request rather than to a commit, so scoping the tab to a single commit keeps them. A file pushed to after you cleared it comes back marked **Changed**. +To take read files out of the list and file tree, turn on **Hide viewed files** in the toolbar; +a **Changed** file stays in view. On GitHub these are GitHub's own viewed marks, so a review carries between T3 Code and github.com in either direction. Forgejo, GitLab, Bitbucket, and Azure DevOps expose no record T3 Code can read, so the From aa788a2019c2326e4e53e4e88fe342a522bae23c Mon Sep 17 00:00:00 2001 From: AtAFork Date: Wed, 4 Nov 2026 16:13:31 -0600 Subject: [PATCH 2/8] fix(web): keep Shift-click ranges and comment drafts working while viewed files are hidden --- .../PullRequestCodeTab.browser.test.tsx | 22 +++++++++++++++++++ .../pullRequest/PullRequestCodeTab.tsx | 19 ++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index 3cb64607b0917..cfd5de8e1371a 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -234,6 +234,28 @@ it("hides viewed files but keeps one pushed to since it was viewed", async () => await expect.element(screen.getByText("early-0.ts", { exact: true }).first()).toBeVisible(); }); +it("marks a Shift-clicked range viewed after the first file of it was hidden", async () => { + setup({ paged: true }); + const viewedDetail = { + ...detail, + capabilities: { ...detail.capabilities, viewedFiles: "host" as const }, + }; + const container = document.createElement("div"); + container.className = "h-[500px] w-[400px]"; + await using screen = await render(tab(0, null, viewedDetail), { container }); + await screen.getByRole("button", { name: "Hide viewed files" }).click(); + await expect.element(screen.getByText("early-10.ts", { exact: true }).first()).toBeVisible(); + + await screen.getByRole("checkbox", { name: "Viewed" }).first().click(); + await expect.element(screen.getByText("early-10.ts", { exact: true })).not.toBeInTheDocument(); + await screen + .getByRole("checkbox", { name: "Viewed" }) + .nth(1) + .click({ modifiers: ["Shift"] }); + + await expect.element(screen.getByText("early-11.ts", { exact: true })).not.toBeInTheDocument(); +}); + it("never requests the diff of a selected commit the pull request no longer has", async () => { setup({ paged: false }); const container = document.createElement("div"); diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index 7b887b485b617..c1a2a6616982e 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -576,12 +576,12 @@ function PullRequestCodeTab({ const testFileCount = loadedFiles.filter((file) => isTestChangedFile(resolveFileDiffPath(file)), ).length; - // A requested file stays visible so a jump to a hidden file still lands. - const isRevealTarget = (path: string) => path === revealRequest?.path; + // A filter never hides the file being jumped to or the one holding the open comment draft. + const isPinned = (path: string) => path === revealRequest?.path || path === draft?.path; const reviewFiles = hideTestFiles ? loadedFiles.filter((file) => { const path = resolveFileDiffPath(file); - return isRevealTarget(path) || !isTestChangedFile(path); + return isPinned(path) || !isTestChangedFile(path); }) : loadedFiles; const hiddenTestFileCount = loadedFiles.length - reviewFiles.length; @@ -591,7 +591,7 @@ function PullRequestCodeTab({ const files = hideViewedFiles ? reviewFiles.filter((file) => { const path = resolveFileDiffPath(file); - return isRevealTarget(path) || !isFileViewed(path); + return isPinned(path) || !isFileViewed(path); }) : reviewFiles; const omittedFileStats = new Map( @@ -775,11 +775,12 @@ function PullRequestCodeTab({ }); const setFileViewed = (fileKey: string, viewed: boolean, extendRange: boolean) => { - const range = getFileViewedRange( - annotatedFiles, - fileKey, - extendRange ? lastViewedFileKey : null, - ); + // Include files hidden as viewed: the Shift-click anchor is usually one of them. + const rangeFiles = reviewFiles.map((file) => ({ + fileKey: buildFileDiffRenderKey(file), + path: resolveFileDiffPath(file), + })); + const range = getFileViewedRange(rangeFiles, fileKey, extendRange ? lastViewedFileKey : null); setLastViewedFileKey(fileKey); for (const file of range) setViewed(file.path, viewed); setToggledFiles((current) => From 88df5856f2fcfa51a16bb45b27c52b69536f6431 Mon Sep 17 00:00:00 2001 From: AtAFork Date: Wed, 4 Nov 2026 16:28:32 -0600 Subject: [PATCH 3/8] fix(web): keep unsent review comments and conversations reachable while files are hidden --- .../PullRequestCodeTab.browser.test.tsx | 27 ++++ .../pullRequest/PullRequestCodeTab.tsx | 136 +++++++++--------- 2 files changed, 98 insertions(+), 65 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index cfd5de8e1371a..cca5d01b58cb3 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -256,6 +256,33 @@ it("marks a Shift-clicked range viewed after the first file of it was hidden", a await expect.element(screen.getByText("early-11.ts", { exact: true })).not.toBeInTheDocument(); }); +it("keeps conversations reachable when every file is hidden", async () => { + setup({ paged: false }); + const threadDetail: PullRequestDetailView = { + ...detail, + capabilities: { ...detail.capabilities, viewedFiles: "host" }, + reviewThreads: [ + { + id: "thread", + path: "a.ts", + line: 1, + side: "right", + isResolved: false, + isOutdated: false, + comments: [], + }, + ], + }; + const container = document.createElement("div"); + container.className = "h-[500px] w-[400px]"; + await using screen = await render(tab(0, null, threadDetail), { container }); + await screen.getByRole("checkbox", { name: "Viewed" }).click(); + await screen.getByRole("button", { name: "Hide viewed files" }).click(); + await expect.element(screen.getByText("All files are hidden.")).toBeVisible(); + + await expect.element(screen.getByText("Conversations not on the current diff")).toBeVisible(); +}); + it("never requests the diff of a selected commit the pull request no longer has", async () => { setup({ paged: false }); const container = document.createElement("div"); diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index c1a2a6616982e..4fbfebfa913ab 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -576,8 +576,11 @@ function PullRequestCodeTab({ const testFileCount = loadedFiles.filter((file) => isTestChangedFile(resolveFileDiffPath(file)), ).length; - // A filter never hides the file being jumped to or the one holding the open comment draft. - const isPinned = (path: string) => path === revealRequest?.path || path === draft?.path; + // A filter never hides the file being jumped to or one holding unsent review comments. + const isPinned = (path: string) => + path === revealRequest?.path || + path === draft?.path || + pendingComments.some((comment) => comment.path === path); const reviewFiles = hideTestFiles ? loadedFiles.filter((file) => { const path = resolveFileDiffPath(file); @@ -1474,16 +1477,6 @@ function PullRequestCodeTab({ return withToolbar(rawSlicesView); } - if (items.length === 0 && nextCursor === null) { - const noFilesMessage = - commit === null ? `This ${term} has no file changes.` : "This commit has no file changes."; - return withToolbar( -

- {loadedFiles.length > 0 ? "All files are hidden." : noFilesMessage} -

, - ); - } - const orphanThreads = detail.reviewThreads.filter((thread) => !placedThreadIds.has(thread.id)); const orphanFiles = new Map(); for (const thread of orphanThreads) { @@ -1491,63 +1484,76 @@ function PullRequestCodeTab({ if (existing) existing.push(thread); else orphanFiles.set(thread.path, [thread]); } + const orphanConversations = + orphanFiles.size > 0 ? ( +
+ +

+ + + {nextCursor === null + ? "Conversations not on the current diff" + : "Conversations not on the diff loaded so far"} + + + + {orphanThreads.length} + + + {orphanThreads.length === 1 + ? "1 conversation" + : `${orphanThreads.length} conversations`} + + +

+ +
+ {[...orphanFiles].map(([path, threads]) => ( +
+ + {path}

} + /> + {path} +
+
+ {threads.map((thread) => ( +
+ {thread.line === null ? null : ( +

Line {thread.line}

+ )} + {renderThreadCard(thread)} +
+ ))} +
+
+ ))} +
+
+
+
+ ) : null; + + if (items.length === 0 && nextCursor === null) { + const noFilesMessage = + commit === null ? `This ${term} has no file changes.` : "This commit has no file changes."; + return withToolbar( + <> + {orphanConversations} +

+ {loadedFiles.length > 0 ? "All files are hidden." : noFilesMessage} +

+ , + ); + } return (
{toolbar} - {orphanFiles.size > 0 ? ( -
- -

- - - {nextCursor === null - ? "Conversations not on the current diff" - : "Conversations not on the diff loaded so far"} - - - - {orphanThreads.length} - - - {orphanThreads.length === 1 - ? "1 conversation" - : `${orphanThreads.length} conversations`} - - -

- -
- {[...orphanFiles].map(([path, threads]) => ( -
- - {path}

- } - /> - {path} -
-
- {threads.map((thread) => ( -
- {thread.line === null ? null : ( -

Line {thread.line}

- )} - {renderThreadCard(thread)} -
- ))} -
-
- ))} -
-
-
-
- ) : null} + {orphanConversations}
Date: Wed, 11 Nov 2026 05:17:21 +0000 Subject: [PATCH 4/8] test(web): point the dismissed-file case at a fixture file main still has --- .../pullRequest/PullRequestCodeTab.browser.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index 4a5dd067a7f4b..3565b3c91f051 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -58,7 +58,7 @@ const { diffAtom, pagedDiffAtom, filesViewedAtom } = await vi.hoisted(async () = AsyncResult.success({ files: [ { path: "early-0.ts", state: "viewed" }, - { path: "early-1.ts", state: "dismissed" }, + { path: "early-2.ts", state: "dismissed" }, ], truncated: false, }), @@ -257,7 +257,7 @@ it("hides viewed files but keeps one pushed to since it was viewed", async () => await hideViewed.click(); await expect.element(screen.getByText("early-0.ts", { exact: true })).not.toBeInTheDocument(); - await expect.element(screen.getByText("early-1.ts", { exact: true }).first()).toBeVisible(); + await expect.element(screen.getByText("early-2.ts", { exact: true }).first()).toBeVisible(); await hideViewed.click(); await expect.element(screen.getByText("early-0.ts", { exact: true }).first()).toBeVisible(); }); From eacefe95c06ac5c486bdd827f87fcd60d5fac2cb Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Wed, 11 Nov 2026 05:30:50 +0000 Subject: [PATCH 5/8] test(web): seed the viewed file instead of racing the viewed-press flush --- .../components/pullRequest/PullRequestCodeTab.browser.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index 3565b3c91f051..b37dd5a0d1a8e 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -59,6 +59,7 @@ const { diffAtom, pagedDiffAtom, filesViewedAtom } = await vi.hoisted(async () = files: [ { path: "early-0.ts", state: "viewed" }, { path: "early-2.ts", state: "dismissed" }, + { path: "a.ts", state: "viewed" }, ], truncated: false, }), @@ -308,7 +309,6 @@ it("keeps conversations reachable when every file is hidden", async () => { await using screen = await render(tab(0, null, undefined, undefined, threadDetail), { container, }); - await screen.getByRole("checkbox", { name: "Viewed" }).click(); await screen.getByRole("button", { name: "Hide viewed files" }).click(); await expect.element(screen.getByText("All files are hidden.")).toBeVisible(); From b6d06b1c99a36bcbd5e9ad938e03a00f88b47c65 Mon Sep 17 00:00:00 2001 From: AtAFork Date: Tue, 10 Nov 2026 23:34:42 -0600 Subject: [PATCH 6/8] fix(web): keep the Hide viewed files toggle reachable while its filter is on Unmarking the last viewed file hid the toggle while the saved filter stayed on, so the next file marked viewed vanished with no visible way to turn the filter off. --- apps/web/src/components/pullRequest/PullRequestCodeTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index f9afb1ae51512..d6d6e1f83d849 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -1441,7 +1441,7 @@ function PullRequestCodeTab({ ) : null} - {viewedFileCount > 0 ? ( + {viewedFileCount > 0 || hideViewedFiles ? ( Date: Tue, 10 Nov 2026 23:49:39 -0600 Subject: [PATCH 7/8] test(web): cover turning off a saved Hide viewed files filter with nothing viewed --- .../PullRequestCodeTab.browser.test.tsx | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index b37dd5a0d1a8e..3c9715983721d 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -67,7 +67,18 @@ const { diffAtom, pagedDiffAtom, filesViewedAtom } = await vi.hoisted(async () = }; }); -const mocks = vi.hoisted(() => ({ paged: false, diffCommits: [] as Array })); +const noFilesViewedAtom = await vi.hoisted(async () => { + const { AsyncResult, Atom } = await import("effect/reactivity"); + return Atom.make(() => + AsyncResult.success({ files: [], truncated: false }), + ); +}); + +const mocks = vi.hoisted(() => ({ + paged: false, + noFilesViewed: false, + diffCommits: [] as Array, +})); vi.mock(import("~/state/use-atom-command"), async (original) => ({ ...(await original()), @@ -83,7 +94,7 @@ vi.mock(import("~/state/pullRequests"), async (original) => { mocks.diffCommits.push(input.commit); return mocks.paged ? pagedDiffAtom(input.cursor ?? "first") : diffAtom; }) as never, - filesViewed: (() => filesViewedAtom) as never, + filesViewed: (() => (mocks.noFilesViewed ? noFilesViewedAtom : filesViewedAtom)) as never, }, }; }); @@ -143,9 +154,10 @@ const detail: PullRequestDetailView = { }, }; -function setup({ paged }: { paged: boolean }) { +function setup({ paged, noFilesViewed = false }: { paged: boolean; noFilesViewed?: boolean }) { __setClientSettingsForTests(DEFAULT_CLIENT_SETTINGS); mocks.paged = paged; + mocks.noFilesViewed = noFilesViewed; mocks.diffCommits = []; localStorage.clear(); } @@ -263,6 +275,23 @@ it("hides viewed files but keeps one pushed to since it was viewed", async () => await expect.element(screen.getByText("early-0.ts", { exact: true }).first()).toBeVisible(); }); +it("keeps the Hide viewed files toggle while its saved filter is on and nothing is viewed", async () => { + setup({ paged: true, noFilesViewed: true }); + localStorage.setItem("t3code.pullRequestHideViewedFiles", "true"); + const viewedDetail = { + ...detail, + capabilities: { ...detail.capabilities, viewedFiles: "host" as const }, + }; + await using screen = await render(tab(0, null, undefined, undefined, viewedDetail), { + container: pagedContainer(), + }); + const hideViewed = screen.getByRole("button", { name: "Hide viewed files" }); + + await hideViewed.click(); + + await expect.element(hideViewed).toHaveAttribute("aria-pressed", "false"); +}); + it("marks a Shift-clicked range viewed after the first file of it was hidden", async () => { setup({ paged: true }); const viewedDetail = { From 775f8f229f5418be653abb8b68643df53f3e8a03 Mon Sep 17 00:00:00 2001 From: AtAFork Date: Tue, 10 Nov 2026 23:59:02 -0600 Subject: [PATCH 8/8] test(web): assert the saved Hide viewed files filter turns off instead of the unmounted toggle --- .../pullRequest/PullRequestCodeTab.browser.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index 3c9715983721d..828ed80d7635a 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -285,11 +285,11 @@ it("keeps the Hide viewed files toggle while its saved filter is on and nothing await using screen = await render(tab(0, null, undefined, undefined, viewedDetail), { container: pagedContainer(), }); - const hideViewed = screen.getByRole("button", { name: "Hide viewed files" }); - - await hideViewed.click(); + await screen.getByRole("button", { name: "Hide viewed files" }).click(); - await expect.element(hideViewed).toHaveAttribute("aria-pressed", "false"); + await vi.waitFor(() => + expect(localStorage.getItem("t3code.pullRequestHideViewedFiles")).toBe("false"), + ); }); it("marks a Shift-clicked range viewed after the first file of it was hidden", async () => {