-
Notifications
You must be signed in to change notification settings - Fork 4
[TAS-5490] 🚸 Hide context menu when annotation menu opens #879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,15 +1,26 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| const APP_USER_AGENT_PREFIX = '3ook-com-app' | ||||||||||||||||||||||||||||||||||||||||||||
| // e.g. "3ook-com-app/1.1.0 (iOS 18.0) Build/42" | ||||||||||||||||||||||||||||||||||||||||||||
| const APP_USER_AGENT_REGEX = /^3ook-com-app\/[\d.]+ \((iOS|Android) [\d.]+\)/ | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export function useAppDetection() { | ||||||||||||||||||||||||||||||||||||||||||||
| const getRouteQuery = useRouteQuery() | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const isAppUserAgent = import.meta.server | ||||||||||||||||||||||||||||||||||||||||||||
| ? useRequestHeaders(['user-agent'])['user-agent']?.startsWith(APP_USER_AGENT_PREFIX) || false | ||||||||||||||||||||||||||||||||||||||||||||
| : navigator.userAgent?.startsWith(APP_USER_AGENT_PREFIX) || false | ||||||||||||||||||||||||||||||||||||||||||||
| const userAgent = import.meta.server | ||||||||||||||||||||||||||||||||||||||||||||
| ? useRequestHeaders(['user-agent'])['user-agent'] || '' | ||||||||||||||||||||||||||||||||||||||||||||
| : navigator.userAgent || '' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const appUAMatches = userAgent.match(APP_USER_AGENT_REGEX) | ||||||||||||||||||||||||||||||||||||||||||||
| const appOSName = appUAMatches?.[1] | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const isApp = computed(() => { | ||||||||||||||||||||||||||||||||||||||||||||
| return isAppUserAgent || getRouteQuery('app') === '1' | ||||||||||||||||||||||||||||||||||||||||||||
| return !!appUAMatches || getRouteQuery('app') === '1' | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return { isApp } | ||||||||||||||||||||||||||||||||||||||||||||
| const isIOS = computed(() => appOSName === 'iOS' || /iPhone|iPad/.test(userAgent)) | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
| const isIOS = computed(() => appOSName === 'iOS' || /iPhone|iPad/.test(userAgent)) | |
| const isIOS = computed(() => { | |
| if (appOSName === 'iOS') { | |
| return true | |
| } | |
| // Match common iOS devices by user agent | |
| if (/iPhone|iPad|iPod/.test(userAgent)) { | |
| return true | |
| } | |
| // Detect iPadOS in "desktop mode" where UA may look like macOS | |
| if (import.meta.client) { | |
| const nav = window.navigator | |
| if (nav && nav.platform === 'MacIntel' && (nav as any).maxTouchPoints > 1) { | |
| return true | |
| } | |
| } | |
| return false | |
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore for now
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -419,6 +419,8 @@ const isMobileTocOpen = computed({ | |||||||||||||||
| }, | ||||||||||||||||
| }) | ||||||||||||||||
|
|
||||||||||||||||
| const { isIOS, isAndroid } = useAppDetection() | ||||||||||||||||
|
|
||||||||||||||||
| const isPageLoading = ref(false) | ||||||||||||||||
|
|
||||||||||||||||
| const isAnnotationMenuVisible = ref(false) | ||||||||||||||||
|
|
@@ -583,6 +585,7 @@ const FONT_SIZE_OPTIONS = [ | |||||||||||||||
| ] | ||||||||||||||||
| const DEFAULT_FONT_SIZE_INDEX = 8 // Default to 24px | ||||||||||||||||
| const COPY_CHAR_LIMIT = 100 | ||||||||||||||||
| const SELECTION_CHANGE_DEBOUNCE_MS = 300 | ||||||||||||||||
| const fontSize = useSyncedBookSettings({ | ||||||||||||||||
| nftClassId: nftClassId.value, | ||||||||||||||||
| key: 'fontSize', | ||||||||||||||||
|
|
@@ -618,6 +621,9 @@ function applyTheme() { | |||||||||||||||
| textCSS.color = bodyCSS.color as string | ||||||||||||||||
| textCSS['background-color'] = 'transparent !important' | ||||||||||||||||
| } | ||||||||||||||||
| if (isIOS.value || isAndroid.value) { | ||||||||||||||||
|
||||||||||||||||
| if (isIOS.value || isAndroid.value) { | |
| const isRNWebView = | |
| typeof window !== 'undefined' && 'ReactNativeWebView' in window | |
| if ((isIOS.value || isAndroid.value) && isRNWebView) { |
Copilot
AI
Mar 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
applyTheme() sets -webkit-touch-callout: none for all iOS/Android user agents. Combined with the global contextmenu suppression, this effectively disables the native long-press callout across the whole reader (including mobile web), not only while the annotation menu is shown. If the goal is just to avoid overlapping menus, consider scoping this behavior to app/webview contexts and/or toggling it only when the annotation menu is visible.
| if (isIOS.value || isAndroid.value) { | |
| textCSS['-webkit-touch-callout'] = 'none' | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is intended
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The contextmenu handler currently calls preventDefault() unconditionally on iOS/Android, which can block long-press actions (copy, lookup, open-in-new-tab) even when the annotation menu isn’t involved. Consider preventing the context menu only while text selection/annotation menu is active, or scope this to app-only (isApp) to avoid a web UX regression.
| event.preventDefault() | |
| const selection = view.window.getSelection?.() | |
| const selectedText = selection?.toString() || '' | |
| if (selectedText.length > 0) { | |
| event.preventDefault() | |
| } |
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On iOS, selection.removeAllRanges() clears the selection immediately after it’s made. Since the AnnotationMenu currently doesn’t offer a Copy action, this effectively removes the user’s ability to copy selected text on iOS browsers. If suppressing the native callout is required, consider adding an explicit Copy action to the annotation menu (and/or only clearing selection in app mode) so copy remains possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intended
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need this to match old version like
3ook-com-app/1.1.0or3ook-com-app/1.1.0 (ios 18.0)also?