From 609ddb776b3cc9b65ad2c3c1c738e6d0e99bdd0f Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Sun, 9 Feb 2025 21:36:08 +0100 Subject: [PATCH] ux: disable double click timeout on mobile devices for more performant page turning (#339) --- .../src/app/reader/hooks/useIframeEvents.ts | 7 +- .../app/reader/utils/iframeEventHandlers.ts | 71 ++++++++++--------- apps/readest-app/src/services/constants.ts | 3 + 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/apps/readest-app/src/app/reader/hooks/useIframeEvents.ts b/apps/readest-app/src/app/reader/hooks/useIframeEvents.ts index c2159959..9aedabd5 100644 --- a/apps/readest-app/src/app/reader/hooks/useIframeEvents.ts +++ b/apps/readest-app/src/app/reader/hooks/useIframeEvents.ts @@ -15,9 +15,6 @@ export const useClickEvent = ( if (msg.data && msg.data.bookKey === bookKey) { const viewSettings = getViewSettings(bookKey)!; if (msg.data.type === 'iframe-single-click') { - if (viewSettings.disableClick!) { - return; - } const viewElement = containerRef.current; if (viewElement) { const rect = viewElement.getBoundingClientRect(); @@ -32,9 +29,9 @@ export const useClickEvent = ( if (screenX >= centerStartX && screenX <= centerEndX) { // toggle visibility of the header bar and the footer bar setHoveredBookKey(hoveredBookKey ? null : bookKey); - } else if (screenX >= rect.left + rect.width / 2) { + } else if (!viewSettings.disableClick! && screenX >= rect.left + rect.width / 2) { viewRef.current?.goRight(); - } else if (screenX < rect.left + rect.width / 2) { + } else if (!viewSettings.disableClick! && screenX < rect.left + rect.width / 2) { viewRef.current?.goLeft(); } } diff --git a/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts b/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts index ecb7415a..34fdc326 100644 --- a/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts +++ b/apps/readest-app/src/app/reader/utils/iframeEventHandlers.ts @@ -1,7 +1,12 @@ -const doubleClickThreshold = 250; -const longHoldThreshold = 500; +import { + DISABLE_DOUBLE_CLICK_ON_MOBILE, + DOUBLE_CLICK_INTERVAL_THRESHOLD_MS, +} from '@/services/constants'; +import { getOSPlatform } from '@/utils/misc'; + let lastClickTime = 0; -let longHoldTimeout: ReturnType | null = null; +const doubleClickEnabled = + !DISABLE_DOUBLE_CLICK_ON_MOBILE || !['android', 'ios'].includes(getOSPlatform()); export const handleKeydown = (bookKey: string, event: KeyboardEvent) => { if (['Backspace', 'ArrowDown', 'ArrowUp'].includes(event.key)) { @@ -23,9 +28,6 @@ export const handleKeydown = (bookKey: string, event: KeyboardEvent) => { }; export const handleMousedown = (bookKey: string, event: MouseEvent) => { - longHoldTimeout = setTimeout(() => { - longHoldTimeout = null; - }, longHoldThreshold); window.postMessage( { type: 'iframe-mousedown', @@ -86,7 +88,7 @@ export const handleWheel = (bookKey: string, event: WheelEvent) => { export const handleClick = (bookKey: string, event: MouseEvent) => { const now = Date.now(); - if (now - lastClickTime < doubleClickThreshold) { + if (doubleClickEnabled && now - lastClickTime < DOUBLE_CLICK_INTERVAL_THRESHOLD_MS) { lastClickTime = now; window.postMessage( { @@ -106,35 +108,38 @@ export const handleClick = (bookKey: string, event: MouseEvent) => { lastClickTime = now; - setTimeout(() => { - if (Date.now() - lastClickTime >= doubleClickThreshold) { - let element: HTMLElement | null = event.target as HTMLElement; - while (element) { - if (['sup', 'a', 'audio', 'video'].includes(element.tagName.toLowerCase())) { - return; - } - element = element.parentElement; - } - - if (!longHoldTimeout) { + const postSingleClick = () => { + let element: HTMLElement | null = event.target as HTMLElement; + while (element) { + if (['sup', 'a', 'audio', 'video'].includes(element.tagName.toLowerCase())) { return; } - - window.postMessage( - { - type: 'iframe-single-click', - bookKey, - screenX: event.screenX, - screenY: event.screenY, - clientX: event.clientX, - clientY: event.clientY, - offsetX: event.offsetX, - offsetY: event.offsetY, - }, - '*', - ); + element = element.parentElement; } - }, doubleClickThreshold); + + window.postMessage( + { + type: 'iframe-single-click', + bookKey, + screenX: event.screenX, + screenY: event.screenY, + clientX: event.clientX, + clientY: event.clientY, + offsetX: event.offsetX, + offsetY: event.offsetY, + }, + '*', + ); + }; + if (doubleClickEnabled) { + setTimeout(() => { + if (Date.now() - lastClickTime >= DOUBLE_CLICK_INTERVAL_THRESHOLD_MS) { + postSingleClick(); + } + }, DOUBLE_CLICK_INTERVAL_THRESHOLD_MS); + } else { + postSingleClick(); + } }; const handleTouchEv = (bookKey: string, event: TouchEvent, type: string) => { diff --git a/apps/readest-app/src/services/constants.ts b/apps/readest-app/src/services/constants.ts index 689f09a6..30e6e2b7 100644 --- a/apps/readest-app/src/services/constants.ts +++ b/apps/readest-app/src/services/constants.ts @@ -397,3 +397,6 @@ export const DEFAULT_STORAGE_QUOTA: UserStorageQuota = { plus: 2 * 1024 * 1024 * 1024, pro: 10 * 1024 * 1024 * 1024, }; + +export const DOUBLE_CLICK_INTERVAL_THRESHOLD_MS = 250; +export const DISABLE_DOUBLE_CLICK_ON_MOBILE = true;