فهرست منبع

Merge pull request #31981 from overleaf/mg-context-mobile

[web] Add stricter mobile detection for context menu

GitOrigin-RevId: 9c9e75a14c1f9841b5125cb4fb4ef8c2f3b8a33f
Malik Glossop 5 ماه پیش
والد
کامیت
9d58797a04

+ 8 - 20
services/web/frontend/js/features/source-editor/extensions/context-menu.ts

@@ -17,6 +17,9 @@ import {
 } from '@codemirror/state'
 import { closeAllContextMenusEffect } from '../utils/close-all-context-menus-effect'
 import { isContextMenuMouseEvent } from '../utils/context-menu-mouse-event'
+import { isMobileDevice } from '../utils/isMobileDevice'
+
+const isMobile = isMobileDevice()
 
 export const openContextMenuEffect = StateEffect.define<{
   pos: number
@@ -28,11 +31,6 @@ export const closeContextMenuEffect = StateEffect.define()
 
 export const openContextMenuAnnotation = Annotation.define<boolean>()
 
-const isTouchOnlyInput =
-  typeof window.matchMedia === 'function' &&
-  window.matchMedia('(pointer: coarse)').matches &&
-  window.matchMedia('(hover: none)').matches
-
 type ContextMenuState = {
   tooltip: Tooltip | null
   mousePosition: { x: number; y: number } | null
@@ -236,7 +234,7 @@ function isClickOnGutter(target: HTMLElement): boolean {
 // Gutter context menu plugin
 const gutterContextMenuPlugin = (): Extension =>
   EditorView.updateListener.of(update => {
-    if (isTouchOnlyInput || !update.view.dom.parentElement) {
+    if (!update.view.dom.parentElement) {
       return
     }
 
@@ -275,12 +273,8 @@ const gutterContextMenuPlugin = (): Extension =>
 
 // Handle right-click on ol-cm-filler (empty line widget)
 // domEventHandlers doesn't fire for contenteditable="false" elements, so we use a direct DOM listener
-const emptyLineFillerContextMenuPlugin = (): Extension => {
-  if (isTouchOnlyInput) {
-    return []
-  }
-
-  return ViewPlugin.define(view => {
+const emptyLineFillerContextMenuPlugin = (): Extension =>
+  ViewPlugin.define(view => {
     const contentDOM = view.contentDOM
 
     const handleContextMenu = (event: Event) => {
@@ -313,16 +307,11 @@ const emptyLineFillerContextMenuPlugin = (): Extension => {
       },
     }
   })
-}
 
 // Editor view context menu handlers
 const editorContextMenuHandlers = (): Extension =>
   EditorView.domEventHandlers({
     contextmenu(event: MouseEvent, view: EditorView) {
-      if (isTouchOnlyInput) {
-        return false
-      }
-
       const pos = view.posAtCoords({ x: event.clientX, y: event.clientY })
       if (pos === null) {
         return false
@@ -361,8 +350,7 @@ const editorContextMenuHandlers = (): Extension =>
       }
 
       // Prevent default on right-click to preserve selection
-      // But not on touch devices - they need native selection behavior
-      if (isRightClick && !isTouchOnlyInput) {
+      if (isRightClick) {
         event.preventDefault()
         return true
       }
@@ -394,7 +382,7 @@ const contextMenuKeymap = (): Extension =>
   )
 
 export const contextMenu = (enabled: boolean): Extension =>
-  enabled
+  enabled && !isMobile
     ? [
         contextMenuContainerTheme,
         contextMenuStateField,

+ 15 - 0
services/web/frontend/js/features/source-editor/utils/isMobileDevice.ts

@@ -0,0 +1,15 @@
+import browser from '../extensions/browser'
+
+export function isMobileDevice(): boolean {
+  const ua = typeof navigator !== 'undefined' ? navigator.userAgent : ''
+  const isMobileUserAgent = /Android|iPhone|iPad|Mobile/i.test(ua)
+
+  // Input-capability fallback.
+  const isTouchOnlyInput =
+    typeof window !== 'undefined' &&
+    typeof window.matchMedia === 'function' &&
+    window.matchMedia('(pointer: coarse)').matches &&
+    window.matchMedia('(hover: none)').matches
+
+  return browser.ios || browser.android || isMobileUserAgent || isTouchOnlyInput
+}