Skip to content

fix(desktop): cache external-session catalog per selection so switching sources shows loaded rows instantly - #3905

Open
liuxiaocs7 wants to merge 3 commits into
apache:mainfrom
liuxiaocs7:fix/import-tasks-source-switch-loading
Open

fix(desktop): cache external-session catalog per selection so switching sources shows loaded rows instantly#3905
liuxiaocs7 wants to merge 3 commits into
apache:mainfrom
liuxiaocs7:fix/import-tasks-source-switch-loading

Conversation

@liuxiaocs7

@liuxiaocs7 liuxiaocs7 commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

Switching the import source — or toggling the archived filter / editing the search, and including returning to a source already loaded — blanked the list and flashed the full-width "Reading external conversations…" spinner on every switch, because loadCatalog unconditionally reset the catalog and loading flag with no per-selection cache.

This caches the last loaded CatalogState per (adapterId, includeArchived, search): a cache hit renders the rows immediately and refreshes in the background, while a miss keeps the existing spinner. The background import poll now observes the request generation instead of claiming a new one, so it can never strand an in-flight load's catalogLoading reset.

Two follow-ups from review: a cache hit now also clears any stale catalogLoading / loadingMore left by a superseded search or pagination request (so the spinner or a disabled Load More can't strand over otherwise-complete cached rows), and it refreshes through readCatalogWindow seeded with the cached page count, so a multi-page selection keeps every loaded page instead of snapping back to page one when the refresh lands.

Fixes #3904

Verification

  • tsc for tsconfig.preload.json, tsconfig.main.json, tsconfig.renderer.json — all clean.
  • biome check on both changed files — clean.
  • node --test dist/main/__tests__/import-tasks-settings-page.test.js — 22/22 pass (15 existing + 7 source-switching cases).
  • Fails without the change: reverting each fix in isolation turns its guarding tests red — the per-selection cache (shows a previously-loaded source instantly with no spinner, does not let a stale background refresh overwrite a newer source selection), the stale-loading reset (clears the reading spinner when a pending search returns to a cached term, clears a pending Load More lock when switching back to a cached source), and the full-window refresh (keeps every loaded page when a revisited multi-page source refreshes); restoring each returns the suite to green (22/22).
  • Not run: repo-wide format:check / knip, full npm run build, and the cross-workspace suite. tsconfig.storybook.json reports one pre-existing, unrelated error (UsageRequestLog.sessionName in stories/settings/settings-pages.stories.tsx) that exists on the base commit and this PR does not touch.

Review focus (UI evidence)

I could not capture a headless screenshot of the Electron window in my environment, so the before/after is shown via the deterministic test output above instead of an image. To see it live: run the desktop app, open Settings → Activity → Import Tasks with ≥2 sources, and switch back and forth — a revisited source now appears instantly with no spinner flash (first-time loads still show the spinner).

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code (Claude Opus) — root-cause investigation, the per-selection cache implementation, the two review-driven follow-ups (stale-loading reset and full-window refresh), and the added tests. Reviewed and submitted by the human contributor of record; commit carries a Generated-by: Claude Code trailer.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

…ng sources shows loaded rows instantly

Switching import source (or toggling the archived filter / search) blanked
the list and flashed the full-width 'reading external conversations' spinner
on every switch, including returning to a source already loaded, because
loadCatalog unconditionally reset the catalog and loading flag with no
per-selection caching.

Cache the last loaded CatalogState per (adapterId, includeArchived, search):
a cache hit renders instantly and refreshes in the background instead of
blanking; misses keep the spinner. The poll now observes rather than claims
the request generation so it can never strand an in-flight load's spinner.

Adds source-switching tests (harness now supports multiple adapters).

Generated-by: Claude Code
@M4n5ter
M4n5ter force-pushed the fix/import-tasks-source-switch-loading branch from 3d9b5c9 to fbf120f Compare August 26, 2026 09:58
@github-actions github-actions Bot added the effort/M Under 500 readable lines label Aug 27, 2026

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the repeated blank-loading state when switching external-session sources. Keeping the Host as the catalog authority while caching only the renderer projection is the right boundary, and the selection key plus request generation model is appropriately scoped.

I left one inline P2 for a recoverable stale-loading case. It does not risk catalog correctness or persisted data, so I am approving the overall direction, but clearing the obsolete loading state would make the cache behavior complete.

Reviewed with Codex and an independent @Reviewer agent. I verified the exact head, cache ownership, selection and request generations, switching and import-recovery paths, and passing CI.

中文对照

谢谢你处理切换外部 Session 来源时反复出现空白加载状态的问题。Host 仍然是 catalog authority,renderer 只缓存展示 projection,这个边界是正确的;selection key 和 request generation 的范围也比较合适。

我留了一条 P2 行内评论:切回缓存 selection 时,旧的 loading 状态可能没有被清理。它不会影响 catalog 正确性或持久化数据,因此我认可整体方向;不过清理过期 loading 状态可以让缓存行为真正闭环。

本次审查使用了 Codex 和一个独立的 @Reviewer 子代理;我核对了精确 head、缓存职责、selection 与 request generation、切换和导入恢复路径,以及 CI 结果。

Comment thread apps/desktop/src/renderer/settings/import-tasks-settings-page.tsx Outdated
… hit

Restoring a cached catalog only reset the rows and the recovery banner, so a
search or pagination request still in flight from the previous selection kept
its loading flag. That older generation can never reach its own `finally`
reset once a newer request supersedes it, and this hit's background refresh may
not have landed yet, so the full-page spinner or a disabled Load More could
strand over otherwise-complete cached rows indefinitely.

Clear both `catalogLoading` and `loadingMore` when publishing a non-append
cache hit so the instant, no-spinner contract holds regardless of a pending
prior request or a slow background refresh.

Adds two source-switching tests: a pending search returning to a cached term
(reachable via the search box, the only control not disabled during a load) and
a pending Load More when switching to a cached source. Both fail without the
change (19/21) and pass with it (21/21).

Addresses the P2 review comment on apache#3905.

Generated-by: Claude Code
…d selection

Revisiting a cached selection restored its full CatalogState — which can be
several pages deep after Load More — but the background refresh that followed
requested only the first page (no cursor) and then replaced the cache and the
view with that single page. So a multi-page source, revisited, flashed all its
pages and then snapped back to page one once the refresh landed, silently
dropping everything the user had paged in.

Refresh a cache hit through the existing `readCatalogWindow`, seeded with the
cached `sessions.length`, so it re-reads the entire loaded window instead of
just page one. The uncached first-page load and the Load More append paths are
unchanged.

Adds a source-switching test: a two-page source, revisited, keeps both pages
after the refresh (re-reading every page). It fails without the change (21/22)
and passes with it (22/22).

Addresses the P2 pagination-shrink review comment on apache#3905.

Generated-by: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/M Under 500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Import Tasks: switching source blanks the list and re-flashes the loading spinner every time

2 participants