fix(reader): harden swipe/long-press coexistence and next-chapter prefetch
- A long-press now cancels any pending swipe, so a hold-then-drag gesture opens the action sheet without also turning the page (regression-tested). - The next-chapter prefetch no longer retries on every page flip when the endpoint fails (guard clears only when the next chapter changes), and it clears a previous chapter's warmed images on navigation so stale hidden <img>s don't linger. - Tap zones set touch-action: pan-y so vertical panning stays native while horizontal swipes reach our handler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,10 @@
|
||||
const startY = e.clientY;
|
||||
pressTimer = setTimeout(() => {
|
||||
pressTimer = null;
|
||||
// A long-press consumed the gesture — cancel any swipe so a
|
||||
// subsequent drag-then-lift (finger held, then moved) doesn't
|
||||
// also turn the page on top of opening the action sheet.
|
||||
swipeStart = null;
|
||||
suppressNextClick = true;
|
||||
if (suppressExpiryTimer != null) clearTimeout(suppressExpiryTimer);
|
||||
suppressExpiryTimer = setTimeout(() => {
|
||||
@@ -200,6 +204,9 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
/* Let vertical panning through to the page but keep horizontal
|
||||
gestures for our swipe handler rather than browser scroll. */
|
||||
touch-action: pan-y;
|
||||
/* No visible style — these are interaction surfaces, not
|
||||
controls. Focus-visible is honored so keyboard users get
|
||||
a ring if they tab into one. */
|
||||
|
||||
@@ -973,11 +973,18 @@
|
||||
|
||||
$effect(() => {
|
||||
const nc = nextChapter;
|
||||
// Drop a previous chapter's warmed images once the next chapter
|
||||
// changes (e.g. after navigating), so stale <img>s don't linger.
|
||||
if (preloadedForChapterId !== null && (!nc || preloadedForChapterId !== nc.id)) {
|
||||
preloadedForChapterId = null;
|
||||
nextPreloadUrls = [];
|
||||
}
|
||||
if (!nc || pages.length === 0) return;
|
||||
if (pages.length - furthestPage > PRELOAD_WITHIN_PAGES) return;
|
||||
if (preloadedForChapterId === nc.id) return;
|
||||
// Guard set before the await so overlapping effect runs don't
|
||||
// double-fetch; cleared on failure so a later pass can retry.
|
||||
// Marked before the await and NOT reset on failure, so a broken
|
||||
// endpoint can't re-fetch on every page flip near the end — the
|
||||
// guard clears only when the next chapter itself changes (above).
|
||||
preloadedForChapterId = nc.id;
|
||||
getChapterPages(manga.id, nc.id)
|
||||
.then((ps) => {
|
||||
@@ -987,7 +994,7 @@
|
||||
.map((p) => fileUrl(p.storage_key));
|
||||
})
|
||||
.catch(() => {
|
||||
if (preloadedForChapterId === nc.id) preloadedForChapterId = null;
|
||||
// Best-effort prefetch — swallow and don't retry this chapter.
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user