fix(reader): harden swipe/long-press coexistence and next-chapter prefetch
All checks were successful
deploy / test-backend (push) Successful in 32m21s
deploy / test-frontend (push) Successful in 10m33s
deploy / build-and-push (push) Successful in 11m40s
deploy / deploy (push) Successful in 18s

- 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:
MechaCat02
2026-07-04 21:57:36 +02:00
parent bfaa166e0a
commit 9c4a93c058
8 changed files with 72 additions and 9 deletions

View File

@@ -51,6 +51,36 @@ async function swipe(page: Page, dx: number, dy: number) {
);
}
// Dispatch pointerdown, hold (so long-press fires), then swipe+lift.
async function holdThenSwipe(page: Page, holdMs: number, dx: number) {
await page.evaluate(() => {
const el = document.querySelector('[data-testid="reader-tap-center"]');
if (!el) throw new Error('tap zone not found');
el.dispatchEvent(new PointerEvent('pointerdown', { clientX: 300, clientY: 500, pointerType: 'touch', bubbles: true, cancelable: true }));
});
await page.waitForTimeout(holdMs);
await page.evaluate(({ dx }) => {
const el = document.querySelector('[data-testid="reader-tap-center"]');
if (!el) throw new Error('tap zone not found');
el.dispatchEvent(new PointerEvent('pointermove', { clientX: 300 + dx, clientY: 505, pointerType: 'touch', bubbles: true, cancelable: true }));
el.dispatchEvent(new PointerEvent('pointerup', { clientX: 300 + dx, clientY: 505, pointerType: 'touch', bubbles: true, cancelable: true }));
}, { dx });
}
test('a long-press then swipe does not also turn the page', async ({ page }) => {
await mockReader(page);
await page.setViewportSize({ width: 390, height: 844 });
await page.goto(`/manga/${MANGA_ID}/chapter/${CHAPTER_ID}`);
const img = page.getByTestId('reader-page');
await expect(img).toHaveAttribute('src', /k\/1/);
// Hold past the 450ms long-press threshold, then swipe left before lifting.
await holdThenSwipe(page, 550, -200);
// The long-press consumed the gesture — the page must not have turned.
await expect(img).toHaveAttribute('src', /k\/1/);
});
test('horizontal swipe turns the page; vertical drag does not', async ({ page }) => {
await mockReader(page);
await page.setViewportSize({ width: 390, height: 844 }); // mobile → tap zones render