Files
orca/src/shared/work-items.ts
T
fsdwenandJinjing 78d2b958bf fix(issues): replace cursor-based pagination with page-number Search API (#8680)
* fix(issues): replace cursor-based pagination with page-number Search API

Problem
=======
Issue pagination (#8649) had two bugs:
1. Pages 6-16 were unreachable — clicking page 16 highlighted page 5;
   clicking 6/7 did nothing. The old cursor-based approach
   (updated:<CURSOR) broke with Search API's relevance sorting —
   pages after the first few returned no items even though more
   issues existed.
2. Issue numbers appeared out of order on loaded pages (e.g. #1082
   between #1308 and #1499), because client-side sort used
   updatedAt instead of issue number.

Root Cause
==========
The pagination used two separate GitHub API strategies:
- Initial page 0 load: REST endpoints (repos/:owner/:repo/issues,
  repos/:owner/:repo/pulls) sorted by updatedAt
- Subsequent pages: Search API with cursor (updated:<DATE)

These two sources returned items in different orders, causing items
to go missing or appear on wrong pages across page boundaries.

Solution
========
1. Unified on GitHub Search API for all pages — initial load and
   pagination both use search/issues?q=...&page=N, eliminating the
   REST-vs-Search inconsistency.
2. Changed from cursor-based (update:<DATE) to page-number-based
   pagination (page=N), which the Search API supports natively.
3. Switched client-side sort from updatedAt to issue number
   (sortWorkItemsByNumber), matching GitHub's default Issues view.
4. Parallelized page fetches in handleLoadNextPage — clicking page
   16 now fetches all intermediate pages concurrently (~2s) instead
   of sequentially (~30s).
5. Cleaned up dead legacy gh issue list / gh pr list code path,
   extracted quoteForSearch helper, shortened overlong comments.

Files changed: 11 files, +140/-127 lines

Closes #8649

* chore: remove unrelated merge formatting

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-15 18:52:37 -07:00

45 lines
1.8 KiB
TypeScript

// Why: per-repo fetch budget for gh CLI calls. Kept in shared/ so the renderer's
// prefetch sites (SidebarNav, ui.ts openTaskPage) and the TaskPage all use the
// same value for cache-key alignment.
export const PER_REPO_FETCH_LIMIT = 36
// Why: how many items to show after cross-repo merge. Decoupled from the per-repo
// fetch limit so changing the display cap doesn't invalidate cache keys.
export const CROSS_REPO_DISPLAY_LIMIT = 100
export const GITHUB_WORK_ITEMS_SSH_REMOTE_REQUIRED_MESSAGE =
'GitHub work items require a GitHub remote for SSH repositories'
export function isGitHubWorkItemsSshRemoteRequiredError(error: unknown): boolean {
const message =
error instanceof Error
? error.message
: typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof error.message === 'string'
? error.message
: typeof error === 'string'
? error
: ''
return message.includes(GITHUB_WORK_ITEMS_SSH_REMOTE_REQUIRED_MESSAGE)
}
// Why: generic over item shape for the same cross-caller reasons as
// sortWorkItemsByUpdatedAt. Sorting by number descending matches GitHub's
// default Issues view (newest issue number first).
export function sortWorkItemsByNumber<T extends { number: number }>(items: T[]): T[] {
return [...items].sort((left, right) => right.number - left.number)
}
// Why: generic over the item shape because main-process callers emit items
// without repoId (stamped by the renderer after IPC), while renderer callers
// carry the full GitHubWorkItem. Both share only the updatedAt field needed
// here.
export function sortWorkItemsByUpdatedAt<T extends { updatedAt: string }>(items: T[]): T[] {
return [...items].sort((left, right) => {
return new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime()
})
}