From 8a31aaf4e1a7f6def9616fa41267d576edb14364 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 30 Jul 2026 06:50:32 -0700 Subject: [PATCH] fix(tasks): key pagination resets on repo selection, not array identity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repos store installs a fresh array on every repos:changed event, so the pagination-reset effect fired on background refreshes and bumped the request generation, silently discarding any in-flight page navigation — clicking an unloaded page did nothing whenever a repo refresh landed during the fetch. Key the effect on the stable selection string instead. --- src/renderer/src/components/TaskPage.tsx | 25 ++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/renderer/src/components/TaskPage.tsx b/src/renderer/src/components/TaskPage.tsx index 0aaede31648..96ece16349a 100644 --- a/src/renderer/src/components/TaskPage.tsx +++ b/src/renderer/src/components/TaskPage.tsx @@ -3205,6 +3205,17 @@ export default function TaskPage(): React.JSX.Element { [eligibleRepos, repoSelection] ) + // Why: stable string key for selectedRepos — the repos store installs a fresh + // array on every repos:changed event, so array-identity deps re-fire even when + // the selection is unchanged. + const selectedReposKey = useMemo( + () => + selectedRepos + .map((r) => `${r.id}|${r.path}|${r.connectionId ?? ''}|${r.executionHostId ?? ''}`) + .join(','), + [selectedRepos] + ) + // Why: many affordances need *a* repo; use the first selected as default, while cross-repo dialogs still let the user override per-action. const primaryRepo = selectedRepos[0] ?? null const linearWorkspaces = linearStatus.workspaces ?? [] @@ -3810,11 +3821,14 @@ export default function TaskPage(): React.JSX.Element { const fetchWorkItemsNextPage = useAppStore((s) => s.fetchWorkItemsNextPage) const countWorkItemsAcrossRepos = useAppStore((s) => s.countWorkItemsAcrossRepos) + // Why: keyed on selectedReposKey, not the selectedRepos array — a background + // repos:changed refresh mid-flight would otherwise bump the generation and + // silently discard the user's page navigation (#11485). useEffect(() => { paginationGenerationRef.current += 1 setPaginationLoading(false) setLoadingTargetPage(null) - }, [selectedRepos, appliedTaskSearch, workItemsInvalidationNonce]) + }, [selectedReposKey, appliedTaskSearch, workItemsInvalidationNonce]) // Why: the dialog's "Use" button routes through the same direct-launch flow as the row-level "Use" CTA so behavior is consistent regardless of entry point. const githubTaskDrawerWorkItem = useAppStore((s) => s.githubTaskDrawerWorkItem) @@ -4829,15 +4843,6 @@ export default function TaskPage(): React.JSX.Element { jiraTaskSourceContext ]) - // Why: stable string key for selectedRepos so the GitLab effect doesn't re-run on every parent render from a new array ref. - const selectedReposKey = useMemo( - () => - selectedRepos - .map((r) => `${r.id}|${r.path}|${r.connectionId ?? ''}|${r.executionHostId ?? ''}`) - .join(','), - [selectedRepos] - ) - // Why: fetch GitLab Issues and MRs separately so errors stay isolated per tab (mirrors GitHub's split endpoints). useEffect(() => { if (taskSource !== 'gitlab') {