Fix selection coordinates in mobile

Because the HTML editor is different, we need a different selector to get the editable element.

We also need to add a small offset to the coordinates to avoid positionning the overlay icon above native selection UI in mobile. For example, Android adds a marker at the beginning and the end of the selection and we do not want to display the Scribe overlay icon above this marker.
This commit is contained in:
Théo Poizat
2026-01-07 08:38:38 +01:00
committed by Dat H. Pham
parent 9e601caa86
commit 03c70922a0
3 changed files with 27 additions and 12 deletions
+22 -8
View File
@@ -73,12 +73,15 @@ class HtmlUtils {
name: 'unregisterDropListener'); name: 'unregisterDropListener');
static ({String name, String script}) registerSelectionChangeListener( static ({String name, String script}) registerSelectionChangeListener(
String viewId, String viewId, {
) => bool isWebPlatform = false,
}) =>
( (
script: ''' script: '''
let lastSelectedText = ''; let lastSelectedText = '';
const isWebPlatform = $isWebPlatform;
const sendSelectionChangeMessage = (data) => { const sendSelectionChangeMessage = (data) => {
// When iframe // When iframe
if (window.parent) { if (window.parent) {
@@ -101,10 +104,17 @@ class HtmlUtils {
function getEditableFromSelection(selection) { function getEditableFromSelection(selection) {
const node = selection?.focusNode || selection?.anchorNode; const node = selection?.focusNode || selection?.anchorNode;
const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement; const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
return (
el?.closest('.note-editor .note-editable') || if (isWebPlatform) {
document.querySelector('.note-editor .note-editable') return (
); el?.closest('.note-editor .note-editable') ||
document.querySelector('.note-editor .note-editable')
);
} else {
return (
el?.closest('#editor')
);
}
} }
function clamp(v, min, max) { function clamp(v, min, max) {
@@ -143,8 +153,12 @@ class HtmlUtils {
const lastRect = rects[rects.length - 1]; const lastRect = rects[rects.length - 1];
let x = lastRect.right - editableRect.left; // Avoid native selection marks in mobile
let y = lastRect.bottom - editableRect.top; // Offset has been arbitrary determined to avoid selection marks on Android and iOS
const buttonOffset = isWebPlatform ? { x: 0, y: 0 } : { x: 22, y: -20 };
let x = lastRect.right - editableRect.left + buttonOffset.x;
let y = lastRect.bottom - editableRect.top + buttonOffset.y;
const isInside = const isInside =
lastRect.bottom >= editableRect.top && lastRect.bottom >= editableRect.top &&
@@ -66,7 +66,7 @@ class _MobileEditorState extends State<MobileEditorWidget> with TextSelectionMix
_editorController = editorApi.webViewController; _editorController = editorApi.webViewController;
registerSelectionChange = registerSelectionChange =
HtmlUtils.registerSelectionChangeListener(_createdViewId); HtmlUtils.registerSelectionChangeListener(_createdViewId, isWebPlatform: PlatformInfo.isWeb);
_editorController?.addJavaScriptHandler( _editorController?.addJavaScriptHandler(
handlerName: registerSelectionChange!.name, handlerName: registerSelectionChange!.name,
@@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_template.dart'; import 'package:core/utils/html/html_template.dart';
import 'package:core/utils/html/html_utils.dart'; import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart'; import 'package:html_editor_enhanced/html_editor.dart';
import 'package:tmail_ui_user/features/composer/presentation/mixin/text_selection_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/text_selection_mixin.dart';
@@ -104,7 +105,7 @@ class _WebEditorState extends State<WebEditorWidget> with TextSelectionMixin {
_editorController = widget.editorController; _editorController = widget.editorController;
final registerSelectionChange = final registerSelectionChange =
HtmlUtils.registerSelectionChangeListener(_createdViewId); HtmlUtils.registerSelectionChangeListener(_createdViewId, isWebPlatform: PlatformInfo.isWeb);
_selectionChangeScript = WebScript( _selectionChangeScript = WebScript(
name: registerSelectionChange.name, name: registerSelectionChange.name,
script: registerSelectionChange.script, script: registerSelectionChange.script,
@@ -224,7 +225,7 @@ class _WebEditorState extends State<WebEditorWidget> with TextSelectionMixin {
_editorController.evaluateJavascriptWeb( _editorController.evaluateJavascriptWeb(
HtmlUtils.registerDropListener.name); HtmlUtils.registerDropListener.name);
_editorController.evaluateJavascriptWeb( _editorController.evaluateJavascriptWeb(
HtmlUtils.registerSelectionChangeListener(_createdViewId).name); _selectionChangeScript.name);
_editorListenerRegistered = true; _editorListenerRegistered = true;
} }
}, },