Files
readest/apps/readest-app/src/app/reader/hooks/useTouchInterceptor.ts
T
Huang Xin 1e259e87b2 refactor(reader): introduce priority-based touch interceptor for gesture handling (#3809)
Replace the inline touch-swipe event dispatching with a module-level
interceptor registry. This lets the reading ruler (priority 10) claim
drag gestures before the swipe-to-flip handler (priority 0), preventing
conflicts when dragging the ruler on touch devices.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:48:21 +02:00

59 lines
1.7 KiB
TypeScript

import { useEffect, useRef } from 'react';
export interface TouchDetail {
phase: 'start' | 'move' | 'end';
touch: { screenX: number; screenY: number };
touchStart: { screenX: number; screenY: number };
deltaX: number;
deltaY: number;
deltaT: number;
}
export type TouchInterceptorFn = (bookKey: string, detail: TouchDetail) => boolean;
interface TouchInterceptorEntry {
handler: TouchInterceptorFn;
priority: number;
}
// Module-level registry — interceptors are called in descending priority order.
// The first interceptor that returns true consumes the gesture.
const interceptors = new Map<string, TouchInterceptorEntry>();
export const registerTouchInterceptor = (
id: string,
handler: TouchInterceptorFn,
priority = 0,
): (() => void) => {
interceptors.set(id, { handler, priority });
return () => {
interceptors.delete(id);
};
};
export const dispatchTouchInterceptors = (bookKey: string, detail: TouchDetail): boolean => {
const sorted = [...interceptors.values()].sort((a, b) => b.priority - a.priority);
for (const { handler } of sorted) {
if (handler(bookKey, detail)) return true;
}
return false;
};
/**
* React hook for registering a touch interceptor with automatic cleanup.
* The handler ref is updated on every render so the interceptor always
* calls the latest closure without re-registering.
*/
export const useTouchInterceptor = (id: string, handler: TouchInterceptorFn, priority = 0) => {
const handlerRef = useRef(handler);
handlerRef.current = handler;
useEffect(() => {
return registerTouchInterceptor(
id,
(bookKey, detail) => handlerRef.current(bookKey, detail),
priority,
);
}, [id, priority]);
};