diff --git a/app/mobile/engagement/page.tsx b/app/mobile/engagement/page.tsx index 6008c67..80c7093 100644 --- a/app/mobile/engagement/page.tsx +++ b/app/mobile/engagement/page.tsx @@ -172,35 +172,48 @@ export default function MobileEngagementPage() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [sortKey]); - // D-04 fallback: the mobile shell's
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. + // D-04 fallback: scroll restoration via sessionStorage. The actual scroll + // container differs by viewport — `
` has overflow-y-auto, but in some + // configurations the document scrolls instead. Listen on both, save the one + // that's non-zero, and restore both. const restoredScrollRef = useRef(false); useEffect(() => { - const main = document.querySelector('main'); - if (!main) return; let raf = 0; + const save = () => { + const main = document.querySelector('main'); + const mainTop = main?.scrollTop ?? 0; + const winTop = window.scrollY || document.documentElement.scrollTop || 0; + sessionStorage.setItem('mobile-engagement-scroll', JSON.stringify({ main: mainTop, win: winTop })); + }; const onScroll = () => { if (raf) return; - raf = requestAnimationFrame(() => { - raf = 0; - sessionStorage.setItem('mobile-engagement-list-scroll', String(main.scrollTop)); - }); + raf = requestAnimationFrame(() => { raf = 0; save(); }); }; - main.addEventListener('scroll', onScroll, { passive: true }); + window.addEventListener('scroll', onScroll, { passive: true }); + const main = document.querySelector('main'); + main?.addEventListener('scroll', onScroll, { passive: true }); return () => { - main.removeEventListener('scroll', onScroll); + window.removeEventListener('scroll', onScroll); + 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); + const raw = sessionStorage.getItem('mobile-engagement-scroll'); + if (!raw) { restoredScrollRef.current = true; return; } + try { + const { main: mainTop, win: winTop } = JSON.parse(raw) as { main: number; win: number }; + // Defer one frame so layout has settled with the new rows in place. + requestAnimationFrame(() => { + const main = document.querySelector('main'); + if (main && mainTop) main.scrollTop = mainTop; + if (winTop) window.scrollTo(0, winTop); + }); + } catch { + /* corrupt entry — ignore */ + } restoredScrollRef.current = true; }, [usersLoading, users.length]);