Skip to content

Commit 0712398

Browse files
refactor(web): pull request list remembers each answer for the next start (#25364)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent cc0b86d commit 0712398

5 files changed

Lines changed: 459 additions & 208 deletions

File tree

‎apps/web/src/components/pullRequest/PullRequestsPage.tsx‎

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ import {
2929
pullRequestDiffStatKey,
3030
pullRequestEntryKey,
3131
rankPullRequestMatches,
32-
readPullRequestListSnapshot,
33-
writePullRequestListSnapshot,
3432
pullRequestStatsRefreshBatches,
3533
pullRequestStatsRequestBatches,
3634
retainVisiblePullRequestStatsBatches,
@@ -103,6 +101,7 @@ import {
103101
pullRequestEnvironment,
104102
usePullRequestList,
105103
usePullRequestListStats,
104+
restoredPullRequestList,
106105
usePullRequestTurnRefreshes,
107106
useSharedPullRequestSummary,
108107
type EnvironmentQueryTarget,
@@ -450,20 +449,25 @@ export function PullRequestsPage() {
450449
if (environmentKey.length > 0 && snapshotEnvironmentKey !== environmentKey) {
451450
setSnapshotEnvironmentKey(environmentKey);
452451
if (loaded?.environmentKey !== environmentKey) {
453-
const snapshot = readPullRequestListSnapshot(
454-
typeof window === "undefined" ? undefined : window.localStorage,
455-
environmentKey,
456-
);
452+
const restored = restoredPullRequestList(baselineTargets);
453+
const restoredPartition = (involvement: PullRequestInvolvement) =>
454+
restoredPullRequestList(
455+
baselineTargets.map((target) => ({ ...target, input: { ...target.input, involvement } })),
456+
)?.entries;
457+
const authored = partitionsWanted ? restoredPartition("authored") : undefined;
458+
const reviewing = partitionsWanted ? restoredPartition("reviewing") : undefined;
457459
setLoaded(
458-
snapshot === null
460+
restored === null
459461
? null
460462
: {
461463
environmentKey,
462-
scope: snapshot.scope,
464+
scope: scopeKey,
463465
query: "",
464466
source: null,
465-
data: snapshot.data,
466-
...(snapshot.partitions === undefined ? {} : { partitions: snapshot.partitions }),
467+
data: restored,
468+
...(authored === undefined || reviewing === undefined
469+
? {}
470+
: { partitions: { authored, reviewing } }),
467471
},
468472
);
469473
}
@@ -507,33 +511,6 @@ export function PullRequestsPage() {
507511
...(nextPartitions === undefined ? {} : { partitions: nextPartitions }),
508512
});
509513
}
510-
const snapshotToWrite =
511-
loaded !== null &&
512-
loaded.source !== null &&
513-
loaded.environmentKey.length > 0 &&
514-
loaded.query.length === 0
515-
? loaded
516-
: null;
517-
const snapshotViewers = baselineQuery.data?.viewers;
518-
const snapshotProviders = baselineQuery.data?.providers;
519-
useEffect(() => {
520-
if (snapshotToWrite === null) return;
521-
writePullRequestListSnapshot(
522-
typeof window === "undefined" ? undefined : window.localStorage,
523-
snapshotToWrite.environmentKey,
524-
{
525-
scope: snapshotToWrite.scope,
526-
data: {
527-
...snapshotToWrite.data,
528-
viewers: snapshotViewers ?? snapshotToWrite.data.viewers,
529-
providers: snapshotProviders ?? snapshotToWrite.data.providers,
530-
},
531-
...(snapshotToWrite.partitions === undefined
532-
? {}
533-
: { partitions: snapshotToWrite.partitions }),
534-
},
535-
);
536-
}, [snapshotToWrite, snapshotViewers, snapshotProviders]);
537514
const narrowed = (() => {
538515
if (loaded === null || loaded.environmentKey !== environmentKey || loaded.scope === scopeKey) {
539516
return null;

‎apps/web/src/components/pullRequest/pullRequestList.logic.test.ts‎

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import {
2222
pullRequestStatsRefreshBatches,
2323
mergePullRequestDiffStats,
2424
partitionPullRequestsWithPriority,
25-
readPullRequestListSnapshot,
26-
writePullRequestListSnapshot,
2725
pullRequestAuthorChoices,
2826
selectedPullRequestAuthor,
2927
pullRequestLabelChoices,
@@ -350,69 +348,6 @@ describe("partitioning with the hosts' own priority reads", () => {
350348
});
351349
});
352350

353-
describe("the list snapshot across a reload", () => {
354-
const makeStorage = () => {
355-
const held = new Map<string, string>();
356-
return {
357-
getItem: (key: string) => held.get(key) ?? null,
358-
setItem: (key: string, value: string) => void held.set(key, value),
359-
};
360-
};
361-
const data = {
362-
entries: [entry({ number: 1 })],
363-
viewers: { "github.com": "Bilal" },
364-
providers: [],
365-
errors: [{ projectId: "project-1", message: "boom" }],
366-
truncated: true,
367-
nextCursors: { "pingdotgg/t3code": "cursor-1" },
368-
} as never;
369-
370-
it("carries neither stale failures nor stale cursors", () => {
371-
const storage = makeStorage();
372-
writePullRequestListSnapshot(storage, "env-1", { scope: "s", data });
373-
const snapshot = readPullRequestListSnapshot(storage, "env-1");
374-
expect(snapshot?.data.errors).toEqual([]);
375-
expect(snapshot?.data.nextCursors).toEqual({});
376-
});
377-
378-
it("caps the accumulated rows but keeps the whole host set", () => {
379-
const storage = makeStorage();
380-
const manyEntries = Array.from({ length: 120 }, (_, index) => entry({ number: index + 1 }));
381-
const viewers = { "github.com": "Bilal", "gitlab.com": "Bilal" };
382-
const providers = [
383-
{
384-
host: "github.com",
385-
kind: "github",
386-
searchesOnHost: true,
387-
projectCount: 1,
388-
configured: true,
389-
detail: null,
390-
},
391-
{
392-
host: "gitlab.com",
393-
kind: "gitlab",
394-
searchesOnHost: true,
395-
projectCount: 1,
396-
configured: true,
397-
detail: null,
398-
},
399-
];
400-
const accumulated = {
401-
entries: manyEntries,
402-
viewers,
403-
providers,
404-
errors: [],
405-
truncated: true,
406-
nextCursors: {},
407-
} as never;
408-
writePullRequestListSnapshot(storage, "env-1", { scope: "s", data: accumulated });
409-
const snapshot = readPullRequestListSnapshot(storage, "env-1");
410-
expect(snapshot?.data.entries).toHaveLength(99);
411-
expect(snapshot?.data.viewers).toEqual(viewers);
412-
expect(snapshot?.data.providers).toEqual(providers);
413-
});
414-
});
415-
416351
describe("remembered pull request list controls", () => {
417352
const makeStorage = () => {
418353
const held = new Map<string, string>();

‎apps/web/src/components/pullRequest/pullRequestList.logic.ts‎

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { sourceControlClients } from "@t3tools/client-runtime/source-control-clients";
2-
import * as Schema from "effect/Schema";
32

4-
import {
3+
import { resolvePullRequestAuthorFilter } from "@t3tools/contracts";
4+
import type {
55
EnvironmentId,
66
PullRequestListEntry,
77
PullRequestListProjectError,
88
PullRequestListResult,
9-
resolvePullRequestAuthorFilter,
10-
} from "@t3tools/contracts";
11-
import type {
129
ProjectId,
1310
RepositoryIdentity,
1411
PullRequestAction,
@@ -575,100 +572,14 @@ export function mergePullRequestLists(
575572
};
576573
}
577574

578-
const SNAPSHOT_MAX_ENTRIES = 99;
579-
580-
type SnapshotStorage = Pick<Storage, "getItem" | "setItem">;
581-
582575
export const pullRequestEnvironmentSetKey = (environmentIds: ReadonlyArray<string>): string =>
583576
[...environmentIds].sort((left, right) => left.localeCompare(right)).join(",");
584577

585-
const snapshotStorageKey = (environmentSetKey: string) =>
586-
`t3.pullRequests.list:${environmentSetKey}`;
587-
588578
export interface PullRequestPartitionsSnapshot {
589579
readonly authored: ReadonlyArray<EnvironmentPullRequestEntry>;
590580
readonly reviewing: ReadonlyArray<EnvironmentPullRequestEntry>;
591581
}
592582

593-
export interface PullRequestListSnapshot {
594-
readonly scope: string;
595-
readonly data: MergedPullRequestList;
596-
readonly partitions?: PullRequestPartitionsSnapshot | undefined;
597-
}
598-
599-
const EnvironmentPullRequestEntrySchema = Schema.Struct({
600-
...PullRequestListEntry.fields,
601-
environmentId: EnvironmentId,
602-
});
603-
604-
const EnvironmentPullRequestErrorSchema = Schema.Struct({
605-
...PullRequestListProjectError.fields,
606-
environmentId: EnvironmentId,
607-
});
608-
609-
const decodeSnapshot = Schema.decodeUnknownOption(
610-
Schema.Struct({
611-
scope: Schema.String,
612-
data: Schema.Struct({
613-
...PullRequestListResult.fields,
614-
entries: Schema.Array(EnvironmentPullRequestEntrySchema),
615-
errors: Schema.Array(EnvironmentPullRequestErrorSchema),
616-
nextCursors: Schema.Record(Schema.String, PullRequestListResult.fields.nextCursors),
617-
truncatedEnvironments: Schema.Array(Schema.String),
618-
}),
619-
partitions: Schema.optional(
620-
Schema.Struct({
621-
authored: Schema.Array(EnvironmentPullRequestEntrySchema),
622-
reviewing: Schema.Array(EnvironmentPullRequestEntrySchema),
623-
}),
624-
),
625-
}),
626-
);
627-
628-
export function readPullRequestListSnapshot(
629-
storage: SnapshotStorage | undefined,
630-
environmentSetKey: string,
631-
): PullRequestListSnapshot | null {
632-
try {
633-
const raw = storage?.getItem(snapshotStorageKey(environmentSetKey));
634-
if (!raw) return null;
635-
const decoded = decodeSnapshot(JSON.parse(raw));
636-
return decoded._tag === "Some" ? decoded.value : null;
637-
} catch {
638-
return null;
639-
}
640-
}
641-
642-
export function writePullRequestListSnapshot(
643-
storage: SnapshotStorage | undefined,
644-
environmentSetKey: string,
645-
snapshot: PullRequestListSnapshot,
646-
): void {
647-
try {
648-
storage?.setItem(
649-
snapshotStorageKey(environmentSetKey),
650-
JSON.stringify({
651-
scope: snapshot.scope,
652-
data: {
653-
...snapshot.data,
654-
entries: snapshot.data.entries.slice(0, SNAPSHOT_MAX_ENTRIES),
655-
errors: [],
656-
nextCursors: {},
657-
truncatedEnvironments: [],
658-
},
659-
...(snapshot.partitions === undefined
660-
? {}
661-
: {
662-
partitions: {
663-
authored: snapshot.partitions.authored.slice(0, SNAPSHOT_MAX_ENTRIES),
664-
reviewing: snapshot.partitions.reviewing.slice(0, SNAPSHOT_MAX_ENTRIES),
665-
},
666-
}),
667-
}),
668-
);
669-
} catch {}
670-
}
671-
672583
/** Retain project scope during reconnect until projects arrive. */
673584
export function resolveProjectScope<Id extends string>(
674585
projectId: Id | undefined,

0 commit comments

Comments
 (0)