From b2d8610490c996e2632e914cd94bc6cb94d815d7 Mon Sep 17 00:00:00 2001 From: shawn Date: Thu, 30 Jul 2026 11:40:30 +0800 Subject: [PATCH] fix: prevent repeated pr list loading shifts --- src/lib/info-row-skeleton.test.ts | 9 ++++++++ src/lib/info-row-skeleton.ts | 34 ++++++++++++++++++++++++------- src/service-worker.test.ts | 1 + src/service-worker.ts | 4 +++- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/lib/info-row-skeleton.test.ts b/src/lib/info-row-skeleton.test.ts index b8ecb2e..f2c6f10 100644 --- a/src/lib/info-row-skeleton.test.ts +++ b/src/lib/info-row-skeleton.test.ts @@ -40,6 +40,15 @@ describe("reserveInfoRowSkeletons", () => { clearSkeletons("branch"); expect(document.querySelector(".bg-skeleton-pill--branch")).toBeNull(); expect(document.querySelector(".bg-skeleton-pill--pr-diff")).not.toBeNull(); + + clearSkeletons("prDiff"); + expect(document.querySelector(".better-github-info-row")).toBeNull(); + reserveInfoRowSkeletons({ + "feature-pr-branch-names": true, + "feature-pr-diff-stats": true, + }); + expect(document.querySelector(".bg-skeleton-pill--branch")).toBeNull(); + expect(document.querySelector(".bg-skeleton-pill--pr-diff")).toBeNull(); }); it("reserves commit diff skeletons on commits list pages", () => { diff --git a/src/lib/info-row-skeleton.ts b/src/lib/info-row-skeleton.ts index 41a066a..dea2288 100644 --- a/src/lib/info-row-skeleton.ts +++ b/src/lib/info-row-skeleton.ts @@ -1,6 +1,6 @@ import { isPRListPage, isCommitsListPage, getRepoInfo } from "./page-detect"; import { collectCommitRows, MAIN_CONTENT_INNER_SELECTOR } from "./commit-dom"; -import { insertInfoRowItem } from "./info-row"; +import { INFO_ROW_CLASS, insertInfoRowItem } from "./info-row"; export type SkeletonKind = "branch" | "prDiff" | "commitDiff"; @@ -14,6 +14,10 @@ const SKELETONS: Record = { }; const SKELETON_BASE_CLASS = "bg-skeleton-pill"; +const reservedPRSkeletons = { + branch: new WeakSet(), + prDiff: new WeakSet(), +}; export interface SkeletonFlags { "feature-pr-branch-names"?: boolean; @@ -53,16 +57,28 @@ function reservePRListSkeletons(flags: SkeletonFlags): void { .map((c) => `.${c}`) .join(", "); - for (const row of document.querySelectorAll("[id^='issue_']")) { + for (const row of document.querySelectorAll("[id^='issue_']:not([id$='_link'])")) { const present = new Set( [...row.querySelectorAll(probeSelector)].flatMap((el) => [...el.classList]), ); - const needBranch = wantBranch && !present.has(branch.real) && !present.has(branch.skeleton); - const needDiff = wantDiff && !present.has(prDiff.real) && !present.has(prDiff.skeleton); + const needBranch = + wantBranch && + !reservedPRSkeletons.branch.has(row) && + !present.has(branch.real) && + !present.has(branch.skeleton); + const needDiff = + wantDiff && + !reservedPRSkeletons.prDiff.has(row) && + !present.has(prDiff.real) && + !present.has(prDiff.skeleton); if (!needBranch && !needDiff) continue; - if (needBranch) insertInfoRowItem(row, "branch", buildPill(branch.skeleton)); - if (needDiff) insertInfoRowItem(row, "diff", buildPill(prDiff.skeleton)); + if (needBranch && insertInfoRowItem(row, "branch", buildPill(branch.skeleton))) { + reservedPRSkeletons.branch.add(row); + } + if (needDiff && insertInfoRowItem(row, "diff", buildPill(prDiff.skeleton))) { + reservedPRSkeletons.prDiff.add(row); + } } } @@ -82,5 +98,9 @@ function reserveCommitsListSkeletons(flags: SkeletonFlags): void { } export function clearSkeletons(kind: SkeletonKind): void { - document.querySelectorAll(`.${SKELETONS[kind].skeleton}`).forEach((el) => el.remove()); + for (const skeleton of document.querySelectorAll(`.${SKELETONS[kind].skeleton}`)) { + const infoRow = skeleton.closest(`.${INFO_ROW_CLASS}`); + skeleton.remove(); + if (infoRow?.childElementCount === 0) infoRow.remove(); + } } diff --git a/src/service-worker.test.ts b/src/service-worker.test.ts index 7458f62..632b48d 100644 --- a/src/service-worker.test.ts +++ b/src/service-worker.test.ts @@ -232,6 +232,7 @@ describe("service worker", () => { }); expect(fetch).toHaveBeenCalledTimes(1); expect(vi.mocked(fetch).mock.calls[0][0]).toBe("https://api.github.com/graphql"); + expect(chrome.storage.local.get).toHaveBeenCalledTimes(1); }); it("falls back to exact REST requests for public filtered lists", async () => { diff --git a/src/service-worker.ts b/src/service-worker.ts index 77ef8c1..16b85d0 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -98,6 +98,7 @@ async function fetchPRBranches( owner, repo, keys: requested, + token, aliasFor: (number) => `pr_${number}`, buildNodeQuery: (number) => `pullRequest(number: ${number}) { headRefName @@ -153,6 +154,7 @@ interface GraphQLBatchSpec { owner: string; repo: string; keys: K[]; + token?: string; aliasFor: (k: K) => string; buildNodeQuery: (k: K) => string; parseNode: (k: K, node: Record) => V | null; @@ -163,7 +165,7 @@ async function fetchGraphQLBatch( ): Promise { if (spec.keys.length === 0) return []; - const token = await getToken(); + const token = spec.token ?? (await getToken()); if (!token) return []; const cacheKey = `cache:${spec.cachePrefix}:${spec.owner}/${spec.repo}:${spec.keys.join(",")}`;