import { useEffect } from 'react'; import { useReaderStore } from '@/store/readerStore'; import { debounce } from '@/utils/debounce'; import { ScrollSource } from './usePagination'; export const useMouseEvent = ( bookKey: string, handlePageFlip: (msg: MessageEvent | React.MouseEvent) => void, handleContinuousScroll: (source: ScrollSource, delta: number, threshold: number) => void, ) => { const { hoveredBookKey } = useReaderStore(); const debounceScroll = debounce(handleContinuousScroll, 500); const debounceFlip = debounce(handlePageFlip, 100); const handleMouseEvent = (msg: MessageEvent | React.MouseEvent) => { if (msg instanceof MessageEvent) { if (msg.data && msg.data.bookKey === bookKey) { if (msg.data.type === 'iframe-wheel') { debounceScroll('mouse', -msg.data.deltaY, 0); } if (msg.data.type === 'iframe-wheel') { debounceFlip(msg); } else { handlePageFlip(msg); } } } else if (msg.type === 'wheel') { const event = msg as React.WheelEvent; debounceScroll('mouse', -event.deltaY, 0); } else { handlePageFlip(msg); } }; useEffect(() => { window.addEventListener('message', handleMouseEvent); return () => { window.removeEventListener('message', handleMouseEvent); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [bookKey, hoveredBookKey]); return { onClick: handlePageFlip, onWheel: handleMouseEvent, }; }; interface IframeTouch { clientX: number; clientY: number; screenX: number; screenY: number; } interface IframeTouchEvent { targetTouches: IframeTouch[]; } export const useTouchEvent = ( bookKey: string, handleContinuousScroll: (source: ScrollSource, delta: number, threshold: number) => void, ) => { const { hoveredBookKey, setHoveredBookKey, getViewSettings } = useReaderStore(); let touchStart: IframeTouch | null = null; let touchEnd: IframeTouch | null = null; const onTouchStart = (e: IframeTouchEvent | React.TouchEvent) => { touchEnd = null; const touch = e.targetTouches[0]; if (!touch) return; touchStart = touch; }; const onTouchMove = (e: IframeTouchEvent | React.TouchEvent) => { if (!touchStart) return; const touch = e.targetTouches[0]; if (touch) { touchEnd = touch; } if (hoveredBookKey && touchEnd) { const viewSettings = getViewSettings(bookKey)!; const deltaY = touchEnd.screenY - touchStart.screenY; const deltaX = touchEnd.screenX - touchStart.screenX; if (!viewSettings!.scrolled && !viewSettings!.vertical) { if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 10) { setHoveredBookKey(null); } } else { setHoveredBookKey(null); } } }; const onTouchEnd = (e: IframeTouchEvent | React.TouchEvent) => { if (!touchStart) return; const touch = e.targetTouches[0]; if (touch) { touchEnd = touch; } const windowWidth = window.innerWidth; if (touchEnd) { const viewSettings = getViewSettings(bookKey)!; const deltaY = touchEnd.screenY - touchStart.screenY; const deltaX = touchEnd.screenX - touchStart.screenX; // also check for deltaX to prevent swipe page turn from triggering the toggle if ( deltaY < -10 && Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaX) < windowWidth * 0.3 ) { // swipe up to toggle the header bar and the footer bar, only for horizontal page mode if (!viewSettings!.scrolled && !viewSettings!.vertical) { setHoveredBookKey(hoveredBookKey ? null : bookKey); } } else { if (hoveredBookKey) { setHoveredBookKey(null); } } handleContinuousScroll('touch', deltaY, 30); } touchStart = null; touchEnd = null; }; const handleTouch = (msg: MessageEvent) => { if (msg.data && msg.data.bookKey === bookKey) { if (msg.data.type === 'iframe-touchstart') { onTouchStart(msg.data); } else if (msg.data.type === 'iframe-touchmove') { onTouchMove(msg.data); } else if (msg.data.type === 'iframe-touchend') { onTouchEnd(msg.data); } } }; useEffect(() => { window.addEventListener('message', handleTouch); return () => { window.removeEventListener('message', handleTouch); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [hoveredBookKey]); return { onTouchStart, onTouchMove, onTouchEnd, }; };