On Android, long-press selects text via selectionchange while the finger is still on the screen. The quick action handler was gated by androidTouchEndRef and silently returned, so no popup ever opened. After the user lifted, nothing re-ran the gated action. Track the gated action in a small DeferredActionState ref and flush it from the native touchend handler, so instant copy/dictionary/wikipedia/ search/translate/tts now fire on the first long-press release.
This commit is contained in:
@@ -26,6 +26,12 @@ import { getPopupPosition, getPosition, getTextFromRange } from '@/utils/sel';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { findTocItemBS } from '@/services/nav';
|
||||
import { throttle } from '@/utils/throttle';
|
||||
import {
|
||||
cancelDeferredAction,
|
||||
createDeferredActionState,
|
||||
flushDeferredAction,
|
||||
runOrDeferAction,
|
||||
} from '../../utils/deferredAction';
|
||||
import { runSimpleCC } from '@/utils/simplecc';
|
||||
import { getWordCount } from '@/utils/word';
|
||||
import { getIndexFromCfi, isCfiInLocation } from '@/utils/cfi';
|
||||
@@ -95,6 +101,10 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
settings.globalReadSettings.highlightStyles[selectedStyle],
|
||||
);
|
||||
const androidTouchEndRef = useRef(false);
|
||||
// Holds a quick action that fired while the user is still touching the screen
|
||||
// (Android long-press selects text via selectionchange before touchend). The
|
||||
// pending action runs on touchend so popups don't open under an active touch.
|
||||
const deferredQuickActionRef = useRef(createDeferredActionState());
|
||||
|
||||
const showingPopup =
|
||||
showAnnotPopup ||
|
||||
@@ -256,11 +266,13 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const ev = event.detail as NativeTouchEventType;
|
||||
if (ev.type === 'touchstart') {
|
||||
androidTouchEndRef.current = false;
|
||||
cancelDeferredAction(deferredQuickActionRef.current);
|
||||
handleTouchStart();
|
||||
} else if (ev.type === 'touchend') {
|
||||
androidTouchEndRef.current = true;
|
||||
handleTouchEnd();
|
||||
handlePointerUp(doc, index);
|
||||
flushDeferredAction(deferredQuickActionRef.current);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -489,32 +501,41 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
const handleQuickAction = () => {
|
||||
const action = viewSettings.annotationQuickAction;
|
||||
if (appService?.isAndroidApp && !androidTouchEndRef.current) return;
|
||||
switch (action) {
|
||||
case 'copy':
|
||||
handleCopy(false);
|
||||
handleDismissPopupAndSelection();
|
||||
break;
|
||||
case 'highlight':
|
||||
// highlight is already applied in instant annotating
|
||||
handleDismissPopupAndSelection();
|
||||
break;
|
||||
case 'search':
|
||||
handleSearch();
|
||||
break;
|
||||
case 'dictionary':
|
||||
handleDictionary();
|
||||
break;
|
||||
case 'wikipedia':
|
||||
handleWikipedia();
|
||||
break;
|
||||
case 'translate':
|
||||
handleTranslation();
|
||||
break;
|
||||
case 'tts':
|
||||
handleSpeakText(true);
|
||||
break;
|
||||
}
|
||||
const runAction = () => {
|
||||
switch (action) {
|
||||
case 'copy':
|
||||
handleCopy(false);
|
||||
handleDismissPopupAndSelection();
|
||||
break;
|
||||
case 'highlight':
|
||||
// highlight is already applied in instant annotating
|
||||
handleDismissPopupAndSelection();
|
||||
break;
|
||||
case 'search':
|
||||
handleSearch();
|
||||
break;
|
||||
case 'dictionary':
|
||||
handleDictionary();
|
||||
break;
|
||||
case 'wikipedia':
|
||||
handleWikipedia();
|
||||
break;
|
||||
case 'translate':
|
||||
handleTranslation();
|
||||
break;
|
||||
case 'tts':
|
||||
handleSpeakText(true);
|
||||
break;
|
||||
}
|
||||
};
|
||||
// On Android, a long-press fires selectionchange (and this handler) while
|
||||
// the finger is still down. Defer until touchend so popups aren't dismissed
|
||||
// by the in-progress touch (closes #3935).
|
||||
runOrDeferAction(
|
||||
deferredQuickActionRef.current,
|
||||
!!appService?.isAndroidApp && !androidTouchEndRef.current,
|
||||
runAction,
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user