Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 48 additions & 36 deletions apps/web/src/components/pullRequest/PullRequestsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { RightPanelTabs } from "../RightPanelTabs";
import { isCommandPaletteOpen } from "~/commandPaletteBus";
import { isElectron } from "~/env";
import { resolveShortcutCommand, shortcutLabelForCommand } from "~/keybindings";
import { registerCommands } from "~/shortcuts/dispatcher";
import { isTerminalFocused } from "~/lib/terminalFocus";
import { PanelLayoutControls, RightPanelMaximizeControl } from "../chat/PanelLayoutControls";
import { Button } from "../ui/button";
Expand Down Expand Up @@ -286,13 +287,21 @@ export function PullRequestsPage() {
* host forever.
*/
regrown: ReadonlyArray<string>;
}>({ key: filterKey, size: PAGE_SIZE, cursors: null, regrown: [] });
/** The turn refreshes a continuation was read under; a later turn makes its cursors stale. */
turn: string;
}>({ key: filterKey, size: PAGE_SIZE, cursors: null, regrown: [], turn: turnRefreshToken });
const pageSize = page.key === filterKey ? page.size : PAGE_SIZE;
const sentCursors = page.key === filterKey ? page.cursors : null;
const sentRegrown = page.key === filterKey ? page.regrown : [];

if (page.key !== filterKey) {
setPage({ key: filterKey, size: PAGE_SIZE, cursors: null, regrown: [] });
setPage({
key: filterKey,
size: PAGE_SIZE,
cursors: null,
regrown: [],
turn: turnRefreshToken,
});
}

const listTargets = environmentQueries.flatMap(({ environmentId, projectIds }) => {
Expand Down Expand Up @@ -591,6 +600,7 @@ export function PullRequestsPage() {
size: regrown.length === 0 ? pageSize : Math.min(pageSize + PAGE_SIZE, MAX_PAGE_SIZE),
cursors: nextCursors,
regrown,
turn: turnRefreshToken,
});
return;
}
Expand All @@ -599,9 +609,28 @@ export function PullRequestsPage() {
size: Math.min(pageSize + PAGE_SIZE, MAX_PAGE_SIZE),
cursors: null,
regrown: [],
turn: turnRefreshToken,
});
};

const collapsedPage = () => {
const loadedCount =
ordered?.key === filterKey ? largestPullRequestReadCount(ordered.entries) : pageSize;
return {
key: filterKey,
regrown: [],
size: Math.min(
Math.max(pageSize, Math.ceil(loadedCount / PAGE_SIZE) * PAGE_SIZE),
MAX_PAGE_SIZE,
),
cursors: null,
turn: turnRefreshToken,
};
};
// Every list read refetches itself when a turn completes, but a continuation would refetch only
// its later page and strand the rows before it, so the whole loaded list is asked for again.
if (sentCursors !== null && page.turn !== turnRefreshToken) setPage(collapsedPage());

const refreshList = (includeRelated = false, actedEnvironmentId?: EnvironmentId) => {
const related = (
includeRelated
Expand All @@ -627,31 +656,9 @@ export function PullRequestsPage() {
return;
}
if (related.length > 0) listQuery.refresh(related);
const loadedCount =
ordered?.key === filterKey ? largestPullRequestReadCount(ordered.entries) : pageSize;
setPage({
key: filterKey,
regrown: [],
size: Math.min(
Math.max(pageSize, Math.ceil(loadedCount / PAGE_SIZE) * PAGE_SIZE),
MAX_PAGE_SIZE,
),
cursors: null,
});
setPage(collapsedPage());
};

const appliedTurnRefreshToken = useRef("");
const refreshAfterTurn = useEffectEvent(() => {
if (sentCursors !== null) refreshList();
});
useEffect(() => {
if (turnRefreshToken.length === 0 || appliedTurnRefreshToken.current === turnRefreshToken) {
return;
}
appliedTurnRefreshToken.current = turnRefreshToken;
refreshAfterTurn();
}, [turnRefreshToken]);

useLiveRefresh(
() => {
refreshList(true);
Expand Down Expand Up @@ -917,15 +924,6 @@ export function PullRequestsPage() {
// oxlint-disable-next-line react/exhaustive-effect-dependencies -- The pointer sensor lives outside React; a new list under a sweep in progress must end it.
[filterKey, search.q, sort],
);
useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if (event.key !== "Escape" || closeSensorRef.current === null) return;
event.preventDefault();
closeSensorRef.current.cancel();
};
document.addEventListener("keydown", onKeyDown, true);
return () => document.removeEventListener("keydown", onKeyDown, true);
}, []);
const startCloseSweep = useStableCallback(
(entry: EnvironmentPullRequestEntry, event: PointerEvent) => {
closeSensorRef.current?.cancel();
Expand Down Expand Up @@ -957,14 +955,27 @@ export function PullRequestsPage() {
sweptKeys = resolveSidebarSweepKeys(orderedKeys, originKey, key, canClose);
setCloseSweepKeys(new Set(sweptKeys));
};
closeSensorRef.current = new SidebarPointerSensor({
// Only while a sweep runs: Escape ends it, ahead of any other scope that would also take it.
const releaseEscape = registerCommands(
{},
{
capture: true,
onKeyDown: (keyEvent) => {
if (keyEvent.key !== "Escape") return false;
keyEvent.preventDefault();
sensor.cancel();
},
},
);
const sensor = new SidebarPointerSensor({
active: originKey,
event,
options: {
distance: 6,
onAttach: () => {},
onFinish: () => {
closeSensorRef.current = null;
releaseEscape();
if (closeSensorRef.current === sensor) closeSensorRef.current = null;
setCloseSweepKeys(new Set());
},
},
Expand Down Expand Up @@ -992,6 +1003,7 @@ export function PullRequestsPage() {
onCancel: () => {},
onAbort: () => {},
});
closeSensorRef.current = sensor;
},
);
const rightPanelAvailable = selectedPullRequestSurface !== null;
Expand Down
Loading