diff --git a/src/features/github/diff/index.ts b/src/features/github/diff/index.ts index 86b408e51..c5ad01384 100644 --- a/src/features/github/diff/index.ts +++ b/src/features/github/diff/index.ts @@ -3,14 +3,11 @@ * (opened by `openPullRequestFileDiff` / the details panel's commit diffs, context * `PULL_REQUEST_DIFF`) get their review threads as inlays, the gutter "+" with single- and * multi-line comments, Start Review / Add Single Comment, the review toolbar and the viewed - * toggle, through the vcs diff viewer provider point. Also registers the editor actions and - * Settings › Version Control › GitHub. The editor actions also serve review mode's editors. + * toggle, through the vcs diff viewer provider point. The editor actions also serve review mode's editors. */ import { registerDiffViewerProvider } from "@/features/vcs/diffViewerProviders"; import { registerActions } from "@/platform/actions"; import { getContext } from "@/platform/context"; -import { lazyComponent } from "@/ui"; -import { registerSettingsPage } from "@/workbench/contributions"; import { useGhRepository } from "../data/repository"; import { activeReviewEditor } from "../reviewMode/editor"; import { clearNewComments } from "./newComments"; @@ -82,13 +79,6 @@ export function activateReviewDiff(): () => void { run: () => reviewTarget()?.refresh(), }, ]), - registerSettingsPage({ - id: "vcs.github", - title: "GitHub", - order: 9, - keywords: ["pull request", "viewed", "review"], - component: lazyComponent(() => import("./GithubSettingsPage").then((m) => m.GithubSettingsPage)), - }), useGhRepository.subscribe((s, prev) => { const a = s.connection?.coordinates; const b = prev.connection?.coordinates; diff --git a/src/features/github/diff/settings.ts b/src/features/github/diff/settings.ts index 418804c2e..10013362d 100644 --- a/src/features/github/diff/settings.ts +++ b/src/features/github/diff/settings.ts @@ -46,6 +46,10 @@ function load(): ReviewDiffSettings { export const useReviewDiffSettings = create(load); +globalThis.addEventListener?.("storage", (event: StorageEvent) => { + if (event.key === KEY || event.key === null) useReviewDiffSettings.setState(load(), true); +}); + export function setReviewDiffSettings(patch: Partial) { useReviewDiffSettings.setState(patch); try { diff --git a/src/features/github/index.ts b/src/features/github/index.ts index 234e71f20..37a0da302 100644 --- a/src/features/github/index.ts +++ b/src/features/github/index.ts @@ -22,6 +22,9 @@ import { import { useGithubShell } from "./shell"; import { githubHandler, handlerContextKey } from "./toolwindow/handlers"; import { GithubDialogHost } from "./ui/dialogs"; +import { activateSettingsPages } from "./settingsPages"; + +export { activateSettingsPages } from "./settingsPages"; const WINDOW = "pullRequests"; const TIMELINE_KIND = "github.timeline"; @@ -255,6 +258,7 @@ export function activate(): () => void { active = true; setContext("githubPrTab", "list"); const disposers = [ + activateSettingsPages(), registerCloneSource({ id: "github", label: "GitHub", diff --git a/src/features/github/settingsPages.test.tsx b/src/features/github/settingsPages.test.tsx new file mode 100644 index 000000000..9b81e7eb4 --- /dev/null +++ b/src/features/github/settingsPages.test.tsx @@ -0,0 +1,52 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import { act, fireEvent, render, screen } from "@testing-library/react"; +import { useSettingsPages } from "@/workbench/contributions"; +import { activateSettingsPages } from "./settingsPages"; +import { GithubSettingsPage } from "./diff/GithubSettingsPage"; +import { setReviewDiffSettings, useReviewDiffSettings } from "./diff/settings"; + +const KEY = "openidea.github.reviewDiff.v1"; + +beforeEach(() => { + localStorage.removeItem(KEY); + window.dispatchEvent(new StorageEvent("storage", { key: KEY })); +}); + +describe("GitHub settings pages", () => { + it("registers independently of the pull request runtime", () => { + const dispose = activateSettingsPages(); + expect(useSettingsPages.getState().pages.get("vcs.github")).toMatchObject({ title: "GitHub" }); + expect(useSettingsPages.getState().pages.get("vcs.github")?.component).toBeDefined(); + dispose(); + expect(useSettingsPages.getState().pages.has("vcs.github")).toBe(false); + }); + + it("updates an open page when another window saves or clears review settings", () => { + render(); + const checkbox = screen.getByRole("checkbox", { name: "Automatically mark opened files as viewed" }); + expect(checkbox).not.toBeChecked(); + act(() => { + localStorage.setItem(KEY, JSON.stringify({ autoMarkViewed: true, threadsView: "all" })); + window.dispatchEvent(new StorageEvent("storage", { key: KEY })); + }); + expect(checkbox).toBeChecked(); + expect(useReviewDiffSettings.getState().threadsView).toBe("all"); + act(() => { + localStorage.clear(); + window.dispatchEvent(new StorageEvent("storage", { key: null })); + }); + expect(checkbox).not.toBeChecked(); + fireEvent.click(checkbox); + expect(JSON.parse(localStorage.getItem(KEY)!)).toMatchObject({ autoMarkViewed: true }); + }); + + it("ignores unrelated storage changes and recovers from invalid saved settings", () => { + setReviewDiffSettings({ autoMarkViewed: true }); + localStorage.removeItem(KEY); + window.dispatchEvent(new StorageEvent("storage", { key: "other" })); + expect(useReviewDiffSettings.getState().autoMarkViewed).toBe(true); + localStorage.setItem(KEY, "invalid JSON"); + window.dispatchEvent(new StorageEvent("storage", { key: KEY })); + expect(useReviewDiffSettings.getState().autoMarkViewed).toBe(false); + }); +}); diff --git a/src/features/github/settingsPages.ts b/src/features/github/settingsPages.ts new file mode 100644 index 000000000..cb07ca6fe --- /dev/null +++ b/src/features/github/settingsPages.ts @@ -0,0 +1,12 @@ +import { lazyComponent } from "@/ui"; +import { registerSettingsPage } from "@/workbench/contributions"; + +export function activateSettingsPages(): () => void { + return registerSettingsPage({ + id: "vcs.github", + title: "GitHub", + order: 9, + keywords: ["pull request", "viewed", "review"], + component: lazyComponent(() => import("./diff/GithubSettingsPage").then((m) => m.GithubSettingsPage)), + }); +} diff --git a/src/features/index.ts b/src/features/index.ts index cd58ab5b9..1616fb21c 100644 --- a/src/features/index.ts +++ b/src/features/index.ts @@ -43,7 +43,7 @@ import { activate as notebook } from "./notebook"; import { activate as web } from "./web"; import { activate as analysis } from "./analysis"; import { activate as services } from "./services"; -import { activate as github } from "./github"; +import { activate as github, activateSettingsPages as githubPages } from "./github"; import { activate as httpClient } from "./http-client"; import { activate as database } from "./database"; import { activate as graphql } from "./graphql"; @@ -166,6 +166,7 @@ export function activateSettingsWindowFeatures(parent: string): () => void { ["agents", agentsPages], ["vim", vimPages], ["vcs", vcsPages], + ["github", githubPages], ]; return activateAll("settings pages", pages); }