fix(08-02): preserve engagement list scroll across profile navigation (D-04)

The mobile shell's <main> is overflow-y-auto, so Next.js's built-in
scrollRestoration (window-only) doesn't restore the list's inner scroll
when returning from /mobile/engagement/[userId]. Persist the scroll
position to sessionStorage on scroll (rAF-throttled) and restore once
after the first users page loads.

Restoration is gated to the first load only, so changing period/sort
doesn't yank the viewport — and uses a single sessionStorage key, so
returning to the list later still lands where you were.
This commit is contained in:
lorentz 2026-05-07 22:10:43 -04:00
parent 81079ad89f
commit 6bdc937861

View file

@ -172,6 +172,38 @@ export default function MobileEngagementPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sortKey]);
// D-04 fallback: the mobile shell's <main> is overflow-y-auto, so Next.js's
// built-in scrollRestoration (which targets window) doesn't help. Persist the
// inner scroll position across navigations via sessionStorage so back from
// /mobile/engagement/[userId] restores the row the user tapped.
const restoredScrollRef = useRef(false);
useEffect(() => {
const main = document.querySelector('main');
if (!main) return;
let raf = 0;
const onScroll = () => {
if (raf) return;
raf = requestAnimationFrame(() => {
raf = 0;
sessionStorage.setItem('mobile-engagement-list-scroll', String(main.scrollTop));
});
};
main.addEventListener('scroll', onScroll, { passive: true });
return () => {
main.removeEventListener('scroll', onScroll);
if (raf) cancelAnimationFrame(raf);
};
}, []);
useEffect(() => {
if (restoredScrollRef.current) return;
if (usersLoading || users.length === 0) return;
const main = document.querySelector('main');
if (!main) return;
const saved = sessionStorage.getItem('mobile-engagement-list-scroll');
if (saved) main.scrollTop = parseInt(saved, 10);
restoredScrollRef.current = true;
}, [usersLoading, users.length]);
// IntersectionObserver — D-18 (mirrors Phase 4/6 pattern, rootMargin '200px')
const sentinelRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {