From 95135942a2bdac902666440708f4ea9930ab9b4d Mon Sep 17 00:00:00 2001 From: yiekheng Date: Wed, 15 Apr 2026 22:29:14 +0800 Subject: [PATCH] Reader: treat browser refresh as implicit resume Refreshing the chapter page was landing the user back at the top, because only ?resume=1 triggered the saved-position restore. Added isPageReload() helper (checks performance navigation entry type === 'reload') and OR'd it with the resume flag. Refresh now restores to the last scroll position; drawer/list clicks still go to top as intended. Co-Authored-By: Claude Opus 4.6 (1M context) --- components/PageReader.tsx | 12 ++++++++---- lib/progress.ts | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/components/PageReader.tsx b/components/PageReader.tsx index a510875..58ef609 100644 --- a/components/PageReader.tsx +++ b/components/PageReader.tsx @@ -15,6 +15,7 @@ import { readProgress, writeProgress, } from "@/components/ReadingProgressButton"; +import { isPageReload } from "@/lib/progress"; import { LoadingLogo } from "@/components/LoadingLogo"; import { calcScrollRatio, @@ -70,7 +71,8 @@ export function PageReader({ const [currentChapterNum, setCurrentChapterNum] = useState(startChapterNumber); const [currentPageNum, setCurrentPageNum] = useState(() => { - if (typeof window === "undefined" || !resume) return 1; + if (typeof window === "undefined") return 1; + if (!resume && !isPageReload()) return 1; const p = readProgress(mangaSlug); if (p && p.chapter === startChapterNumber && p.page > 1) return p.page; return 1; @@ -322,15 +324,17 @@ export function PageReader({ // All reader Links use scroll={false} to preserve scroll during in-reader // nav (natural scroll between chapters updates URL without remount). On - // a fresh mount we must actively position the scroll: resume-to-saved - // if ?resume=1 AND the saved chapter matches; otherwise top. + // a fresh mount we position scroll: resume-to-saved if ?resume=1 (from + // 继续阅读) OR a page reload (so browser refresh preserves position). + // Plain chapter-link clicks from drawer / list go to top. const resumeDoneRef = useRef(false); useLayoutEffect(() => { if (resumeDoneRef.current) return; resumeDoneRef.current = true; const instantTop = (top: number) => window.scrollTo({ top, behavior: "instant" as ScrollBehavior }); - if (!resume) { + const shouldResume = resume || isPageReload(); + if (!shouldResume) { instantTop(0); return; } diff --git a/lib/progress.ts b/lib/progress.ts index 63ad6d4..eae80d0 100644 --- a/lib/progress.ts +++ b/lib/progress.ts @@ -66,3 +66,13 @@ export function writeProgress( function defaultStorage(): StorageLike | null { return typeof window === "undefined" ? null : window.localStorage; } + +export function isPageReload(): boolean { + if (typeof window === "undefined") return false; + if (typeof performance === "undefined") return false; + const entries = performance.getEntriesByType("navigation"); + if (entries.length === 0) return false; + return ( + (entries[0] as PerformanceNavigationTiming).type === "reload" + ); +}