Files
cinny/src/app/hooks/useRecentTouch.ts
T

37 lines
1.5 KiB
TypeScript
Raw Normal View History

import { RefObject, useCallback, useEffect, useRef } from 'react';
import { useMediaQuery } from './useMediaQuery';
const RECENT_MS = 700;
/**
* [Gitea #147] Tells whether a click was produced by a finger. Records the
* last touch inside `ref`; `wasTouch()` is true for a short window after it.
* Keyboard activation and screen-reader activation (TalkBack/VoiceOver send a
* bare click, no touch events) report false, so they keep one-step behaviour.
* Always false on devices without a coarse pointer.
*/
export function useRecentTouch(ref: RefObject<HTMLElement | null>) {
const coarse = useMediaQuery('(pointer: coarse)');
const last = useRef(0);
// Listen on the document (the ref'd element may be remounted, e.g. on a tab
// switch) and only count touches that land inside it.
useEffect(() => {
if (!coarse) return undefined;
const mark = (evt: TouchEvent) => {
const el = ref.current;
if (el && evt.target instanceof Node && !el.contains(evt.target)) return;
last.current = Date.now();
};
document.addEventListener('touchstart', mark, { passive: true, capture: true });
document.addEventListener('touchend', mark, { passive: true, capture: true });
return () => {
document.removeEventListener('touchstart', mark, { capture: true });
document.removeEventListener('touchend', mark, { capture: true });
};
}, [ref, coarse]);
const wasTouch = useCallback(() => coarse && Date.now() - last.current < RECENT_MS, [coarse]);
return { coarse, wasTouch };
}