From a1487fd60c824e0284012eac1fea0dd5dd1e8ed0 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Thu, 4 Dec 2025 13:04:06 +0800 Subject: [PATCH] fix: get rid of the context menu for touch screen or stylus device when selecting text, closes #2579 (#2614) --- .../reader/components/annotator/Annotator.tsx | 11 ++++------- .../src/app/reader/hooks/useTextSelector.ts | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx index 31c60d47..c5485cf1 100644 --- a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx @@ -163,10 +163,12 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { handleScroll, handleTouchStart, handleTouchEnd, + handlePointerdown, handlePointerup, handleSelectionchange, handleShowPopup, handleUpToPopup, + handleContextmenu, } = useTextSelector(bookKey, setSelection, handleDismissPopup); const onLoad = (event: Event) => { @@ -189,6 +191,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { detail.doc?.addEventListener('touchstart', handleTouchStart); detail.doc?.addEventListener('touchmove', handleTouchmove); detail.doc?.addEventListener('touchend', handleTouchEnd); + detail.doc?.addEventListener('pointerdown', handlePointerdown); detail.doc?.addEventListener('pointerup', (ev: PointerEvent) => handlePointerup(doc, index, ev), ); @@ -222,13 +225,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { } // Disable the default context menu on mobile devices (selection handles suffice) - if (appService?.isMobile) { - detail.doc?.addEventListener('contextmenu', (event: Event) => { - event.preventDefault(); - event.stopPropagation(); - return false; - }); - } + detail.doc?.addEventListener('contextmenu', handleContextmenu); }; const onDrawAnnotation = (event: Event) => { diff --git a/apps/readest-app/src/app/reader/hooks/useTextSelector.ts b/apps/readest-app/src/app/reader/hooks/useTextSelector.ts index c1d184e4..62e0bb15 100644 --- a/apps/readest-app/src/app/reader/hooks/useTextSelector.ts +++ b/apps/readest-app/src/app/reader/hooks/useTextSelector.ts @@ -26,6 +26,7 @@ export const useTextSelector = ( const isTextSelected = useRef(false); const isTouchStarted = useRef(false); const selectionPosition = useRef(null); + const lastPointerType = useRef('mouse'); const [textSelected, setTextSelected] = useState(false); const isValidSelection = (sel: Selection) => { @@ -111,6 +112,9 @@ export const useTextSelector = ( } return false; }; + const handlePointerdown = (e: PointerEvent) => { + lastPointerType.current = e.pointerType; + }; const handlePointerup = (doc: Document, index: number, ev: PointerEvent) => { // Available on iOS and Desktop, fired at touchend or mouseup // Note that on Android, pointerup event is fired after an additional touch event @@ -162,6 +166,19 @@ export const useTextSelector = ( isUpToPopup.current = true; }; + const handleContextmenu = (event: Event) => { + if (appService?.isMobile) { + event.preventDefault(); + event.stopPropagation(); + return false; + } else if (lastPointerType.current === 'touch' || lastPointerType.current === 'pen') { + event.preventDefault(); + event.stopPropagation(); + return false; + } + return; + }; + useEffect(() => { if (isTextSelected.current && !selectionPosition.current) { selectionPosition.current = view?.renderer?.start || null; @@ -202,9 +219,11 @@ export const useTextSelector = ( handleScroll, handleTouchStart, handleTouchEnd, + handlePointerdown, handlePointerup, handleSelectionchange, handleShowPopup, handleUpToPopup, + handleContextmenu, }; };