Files
multica/apps
Bohan Jiang b9bf2653be fix(desktop): retry tab scroll restore until virtualized content rehydrates (#3214)
Follow-up to #3196. Switching tabs and back on a long issue still landed at
scrollTop=0 because issue-detail uses Virtuoso with customScrollParent —
Virtuoso wires its scroll/resize observers in a passive useEffect, which
fires *after* useLayoutEffect. So at the moment the restore hook ran, the
spacer that gives the scroll container its tall scrollHeight hadn't been
re-established yet (scrollHeight === clientHeight), and the browser
silently clamped `scrollTop = saved` down to 0.

Diagnostic console output confirmed this:
  marker key=true saved=10356.5 currentScrollTop=0 scrollHeight=750 clientHeight=750
  → set scrollTop to 10356.5 actually now 0

Fix: keep the synchronous set as the fast path, then if the assignment was
clamped, retry across rAF frames for up to ~500ms (30 frames at 60fps).
That gives Virtuoso's passive effect time to re-establish the spacer, after
which the next tick succeeds. Cancel any in-flight retry when the effect
tears down (Activity hidden again or component unmount).

Existing 4 tests in use-tab-scroll-restore.test.tsx still pass — the
synchronous fast path covers the simple-content case they exercise. A
jsdom regression for the Virtuoso scenario didn't reproduce reliably (the
clamp + rAF interplay needs a real browser), so this relies on manual
verification: open issue-detail, scroll deep into comments, switch tabs,
switch back — scroll position now holds.
2026-05-25 16:59:06 +08:00
..