Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import "~/index.css";
import { __setClientSettingsForTests } from "../../hooks/useSettings";
import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings";

const { diffAtom, pagedDiffAtom } = await vi.hoisted(async () => {
const { diffAtom, pagedDiffAtom, allowed, denied } = await vi.hoisted(async () => {
const { AsyncResult, Atom } = await import("effect/reactivity");
const filePatch = (path: string) =>
[
Expand Down Expand Up @@ -53,12 +53,15 @@ const { diffAtom, pagedDiffAtom } = await vi.hoisted(async () => {
pagedDiffAtom: Atom.family((cursor: string) =>
Atom.make(() => AsyncResult.success(pages[cursor]!)),
),
allowed: Atom.make(true),
denied: Atom.make(false),
};
});

const mocks = vi.hoisted(() => ({
paged: false,
diffCommits: [] as Array<string | undefined>,
canMarkViewed: true,
viewedPaths: null as ReadonlySet<string> | null,
viewedListeners: new Set<() => void>(),
}));
Expand Down Expand Up @@ -103,6 +106,10 @@ vi.mock(import("~/state/pullRequests"), async (original) => {
mocks.diffCommits.push(input.commit);
return mocks.paged ? pagedDiffAtom(input.cursor ?? "first") : diffAtom;
}) as never,
setFilesViewed: {
...actual.pullRequestEnvironment.setFilesViewed,
permissionAtom: () => (mocks.canMarkViewed ? allowed : denied),
},
},
};
});
Expand Down Expand Up @@ -162,11 +169,12 @@ const detail: PullRequestDetailView = {
},
};

function setup({ paged }: { paged: boolean }) {
function setup({ paged, canMarkViewed = true }: { paged: boolean; canMarkViewed?: boolean }) {
window.localStorage.removeItem("t3code.diffHideTestFiles");
__setClientSettingsForTests(DEFAULT_CLIENT_SETTINGS);
mocks.paged = paged;
mocks.diffCommits = [];
mocks.canMarkViewed = canMarkViewed;
mocks.viewedPaths = null;
mocks.viewedListeners.clear();
}
Expand All @@ -176,10 +184,12 @@ const tab = (
revealRequest: { readonly path: string; readonly nonce: number } | null = null,
revealUrl?: string,
onRevealSettled?: (url: string) => void,
environmentConnected = true,
) => (
<PullRequestCodeTab
scrollerRef={() => {}}
environmentId={EnvironmentId.make("environment")}
environmentConnected={environmentConnected}
reference={detail}
detail={detail}
selectedCommitOid={null}
Expand Down Expand Up @@ -297,6 +307,7 @@ it("never requests the diff of a selected commit the pull request no longer has"
<PullRequestCodeTab
scrollerRef={() => {}}
environmentId={EnvironmentId.make("environment")}
environmentConnected
reference={detail}
detail={{
...detail,
Expand All @@ -322,6 +333,30 @@ it("never requests the diff of a selected commit the pull request no longer has"
expect(mocks.diffCommits).not.toContain("gone");
});

it("disables the Viewed checkbox on a connection that cannot mark files viewed", async () => {
setup({ paged: false, canMarkViewed: false });
mocks.viewedPaths = new Set();
const container = document.createElement("div");
container.className = "flex h-[700px] w-[900px]";
await using screen = await render(tab(0), { container });

await expect.element(screen.getByRole("checkbox", { name: "Viewed" })).toBeDisabled();
});

it("disables the Viewed checkbox while the environment is disconnected, then enables it on reconnect", async () => {
setup({ paged: false });
mocks.viewedPaths = new Set();
const container = document.createElement("div");
container.className = "flex h-[700px] w-[900px]";
await using screen = await render(tab(0, null, undefined, undefined, false), { container });

await expect.element(screen.getByRole("checkbox", { name: "Viewed" })).toBeDisabled();

await screen.rerender(tab(0, null, undefined, undefined, true));

await expect.element(screen.getByRole("checkbox", { name: "Viewed" })).toBeEnabled();
});

it("answers a commit's diff link only once that commit's diff is shown", async () => {
setup({ paged: false });
const container = document.createElement("div");
Expand All @@ -337,6 +372,7 @@ it("answers a commit's diff link only once that commit's diff is shown", async (
<PullRequestCodeTab
scrollerRef={() => {}}
environmentId={EnvironmentId.make("environment")}
environmentConnected
reference={detail}
detail={withCommit}
selectedCommitOid={selectedCommitOid}
Expand Down Expand Up @@ -376,6 +412,7 @@ it("hides a resolved conversation behind avatars on its line until they are clic
<PullRequestCodeTab
scrollerRef={() => {}}
environmentId={EnvironmentId.make("environment")}
environmentConnected
reference={detail}
detail={{
...detail,
Expand Down
11 changes: 10 additions & 1 deletion apps/web/src/components/pullRequest/PullRequestCodeTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ function reviewThreadsByPath(threads: ReadonlyArray<PullRequestReviewThread>) {

interface FileViewedToggleState {
readonly enabled: boolean;
readonly canMarkViewed: boolean;
readonly isViewed: (path: string) => boolean;
readonly isStale: (path: string) => boolean;
readonly setFileViewed: (fileKey: string, viewed: boolean, extendRange: boolean) => void;
Expand All @@ -322,12 +323,13 @@ function FileViewedToggle({
{stat}
<label
data-viewed-toggle=""
className="flex h-8 w-18 shrink-0 cursor-pointer select-none items-center gap-1.5 text-2xs text-muted-foreground"
className="flex h-8 w-18 shrink-0 cursor-pointer select-none items-center gap-1.5 text-2xs text-muted-foreground has-disabled:cursor-default"
onClick={(event) => event.stopPropagation()}
>
<Checkbox
aria-label={stale ? "Changed" : "Viewed"}
checked={viewed}
disabled={!viewedFiles.canMarkViewed}
onCheckedChange={(next, details) =>
viewedFiles.setFileViewed(
fileKey,
Expand Down Expand Up @@ -390,6 +392,7 @@ function isRemovedLine(fileDiff: FileDiffMetadata, oldLine: number): boolean {
function PullRequestCodeTab({
scrollerRef,
environmentId,
environmentConnected,
reference,
detail,
selectedCommitOid,
Expand All @@ -408,6 +411,7 @@ function PullRequestCodeTab({
}: {
scrollerRef: (node: HTMLDivElement | null) => void;
environmentId: EnvironmentId;
environmentConnected: boolean;
reference: PullRequestRef;
detail: PullRequestDetailView;
selectedCommitOid: string | null;
Expand Down Expand Up @@ -581,6 +585,10 @@ function PullRequestCodeTab({
reference,
enabled: viewedFilesStore !== undefined,
});
// The grant outlives a dropped connection, and a write without a session fails.
const canMarkViewed =
useAtomValue(pullRequestEnvironment.setFilesViewed.permissionAtom(environmentId)) &&
Comment thread
only21mil marked this conversation as resolved.
Resolved
environmentConnected;
const {
setViewed,
refresh: refreshFilesViewed,
Expand Down Expand Up @@ -1012,6 +1020,7 @@ function PullRequestCodeTab({
};
const fileViewedToggle = {
enabled: filesViewedEnabled,
canMarkViewed,
isViewed: isFileViewed,
isStale: isFileViewedStale,
setFileViewed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1964,6 +1964,7 @@ export function PullRequestDetailPanel({
scrollerRef={scrollerRefs.code}
onAddToAgentSelection={addSelectionToAgent}
environmentId={environmentId}
environmentConnected={environmentConnected}
reference={reference}
detail={detail}
selectedCommitOid={selectedCodeCommitOid}
Expand Down
Loading