diff --git a/apps/web/src/components/HistorySwipeNavigation.browser.test.tsx b/apps/web/src/components/HistorySwipeNavigation.browser.test.tsx index b2e6fcaaaf402..c5a3b7b720185 100644 --- a/apps/web/src/components/HistorySwipeNavigation.browser.test.tsx +++ b/apps/web/src/components/HistorySwipeNavigation.browser.test.tsx @@ -58,4 +58,16 @@ describe("HistorySwipeNavigation", () => { expect(navigateHistory).toHaveBeenCalledTimes(navigates ? 1 : 0); }); + + it("navigates once the swipe passes the trigger distance, without waiting for the fingers to lift", async () => { + navigateHistory.mockReset(); + await using screen = await render(); + const panel = screen.container.appendChild(document.createElement("div")); + for (let step = 0; step < 20; step++) { + panel.dispatchEvent(new WheelEvent("wheel", { deltaX: -10, bubbles: true, composed: true })); + } + + expect(navigateHistory).toHaveBeenCalledTimes(1); + expect(navigateHistory).toHaveBeenCalledWith("back"); + }); }); diff --git a/apps/web/src/components/HistorySwipeNavigation.tsx b/apps/web/src/components/HistorySwipeNavigation.tsx index 767b4285c696f..e47a18c13b171 100644 --- a/apps/web/src/components/HistorySwipeNavigation.tsx +++ b/apps/web/src/components/HistorySwipeNavigation.tsx @@ -97,11 +97,8 @@ export function HistorySwipeNavigation() { let gesture: SwipeGesture | null = null; const hideIndicator = () => setSwipe((current) => (current === null ? null : { ...current, tracking: false })); - // The fingers lifted: navigate if the swipe went far enough, then ignore its momentum. + // The swipe ended or was decided: ignore the rest of it, momentum included. const release = () => { - if (gesture?.kind === "tracking" && gesture.distance >= SWIPE_TRIGGER_DISTANCE) { - navigateHistory(gesture.direction); - } gesture = { kind: "ignored" }; hideIndicator(); }; @@ -134,6 +131,12 @@ export function HistorySwipeNavigation() { if (isMomentumTail(gesture.deltas)) return release(); gesture.distance = Math.max(0, gesture.distance + delta); } + // Chromium only ends a trackpad scroll after its momentum stops, so waiting for the + // fingers to lift would delay navigation by the whole momentum tail. + if (gesture.distance >= SWIPE_TRIGGER_DISTANCE) { + navigateHistory(gesture.direction); + return release(); + } setSwipe({ direction: gesture.direction, progress: Math.min(1, gesture.distance / SWIPE_TRIGGER_DISTANCE),