diff --git a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx index 7f6a4592a0b4f7f371f93827f7d25b6f0ec67898..1466e726b60fa90b3f5942908bb9e25f85df0f4d 100644 --- a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx +++ b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx @@ -823,7 +823,7 @@ export function PullRequestDetailPanel({ const canMergeSinglePullRequest = allowsSinglePullRequestMerge({ supportsStackActions, hasStack: nativeStack !== null, - stackPending: !nativeStackQuery.isSuccess || nativeStackQuery.isPending, + stackPending: !nativeStackQuery.isFresh, stackError: nativeStackQuery.error, }); const activityPending = activityQuery.isPending && activity === null; diff --git a/apps/web/src/components/pullRequest/pullRequestStackSnapshot.test.ts b/apps/web/src/components/pullRequest/pullRequestStackSnapshot.test.ts index 621ba3124e8e113b3d09309d99914d08906ceee4..4b6f947232b385868cb0d765d27f53860044d16e 100644 --- a/apps/web/src/components/pullRequest/pullRequestStackSnapshot.test.ts +++ b/apps/web/src/components/pullRequest/pullRequestStackSnapshot.test.ts @@ -88,6 +88,10 @@ describe("saved stack navigation", () => { isFresh: true, notice: null, }); - expect(pullRequestStackView({ ...query, isPending: true }, saved).isFresh).toBe(false); + expect(pullRequestStackView({ ...query, isPending: true }, saved).isFresh).toBe(true); + expect(pullRequestStackView({ ...query, data: null, isPending: true }, saved)).toMatchObject({ + data: null, + isFresh: true, + }); }); }); diff --git a/apps/web/src/components/pullRequest/pullRequestStackSnapshot.ts b/apps/web/src/components/pullRequest/pullRequestStackSnapshot.ts index 4e48a2f3705b26bb5c57c9c705ceb4e9732fbf73..a576d52c33ab5615e47089868cbd7322dc33212f 100644 --- a/apps/web/src/components/pullRequest/pullRequestStackSnapshot.ts +++ b/apps/web/src/components/pullRequest/pullRequestStackSnapshot.ts @@ -47,7 +47,11 @@ export function savedPullRequestStack( }; } -/** A fresh absence overrides saved membership; failed refreshes preserve available navigation. */ +/** + * A fresh absence overrides saved membership; failed refreshes preserve available navigation. + * A background refresh keeps the last successful lookup fresh, so stack-dependent actions do + * not blink out every time the detail revalidates. + */ export function pullRequestStackView( query: { data: PullRequestStack | null; @@ -60,7 +64,7 @@ export function pullRequestStackView( const data = query.isSuccess ? query.data : (query.data ?? saved); return { data, - isFresh: query.isSuccess && !query.isPending, + isFresh: query.isSuccess, notice: data === null ? null