diff --git a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx index 70533f06488..4081d9760f2 100644 --- a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx +++ b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx @@ -707,22 +707,27 @@ export function PullRequestDetailPanel({ state: resolvedCoreDetail.state, }); }, [onStateChange, resolvedCoreDetail]); + // Both reads below go around the server's cache rather than through it. An ordinary read is + // answered from what the server already holds while it refreshes behind the answer, which is + // right for a reopen and wrong for a poll: the point of a poll is what that refresh brings back, + // and nothing pushes it to a panel already told the old answer, so a poll served from the hold + // shows the previous poll's data for good. The invalidation goes first so the re-reads miss + // that cache; if it fails, the reads still run and at worst answer from it. + const invalidate = useAtomCommand(pullRequestEnvironment.invalidate, { reportFailure: false }); // Core detail is cheap enough to re-read while this stays open. Activity is heavier, so the // revision effect above reads it only after this same pull request reports a change. Keyed by // the pull request rather than by the panel, because this one panel shows a different pull // request every time it is opened. - useLiveRefresh( - () => { - detailQuery.refresh(); - setRefreshToken((token) => token + 1); - }, - { key: `pull-request:${reference.projectId}:${reference.repository}#${reference.number}` }, - ); - // The button, on the other hand, goes around the server's cache rather than through it: it is - // the answer for a reader who can see that what they are looking at is behind. The - // invalidation goes first so the re-reads miss that cache; if it fails, the reads still run - // and at worst answer from it. - const invalidate = useAtomCommand(pullRequestEnvironment.invalidate, { reportFailure: false }); + const refreshDetailFromHost = useCallback(async () => { + await invalidate({ environmentId, input: { reference } }); + detailQuery.refresh(); + setRefreshToken((token) => token + 1); + }, [detailQuery.refresh, environmentId, invalidate, reference]); + useLiveRefresh(() => void refreshDetailFromHost(), { + key: `pull-request:${reference.projectId}:${reference.repository}#${reference.number}`, + }); + // The button is the answer for a reader who can see that what they are looking at is behind, + // so it reads everything: the detail, the activity, and through the refresh token, the diff. const refreshFromHost = useCallback(async () => { await invalidate({ environmentId, input: { reference } }); refreshDetail();