From 5c5a4f36588d770fce3f1e2511b95872fcb17a90 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Mon, 28 Oct 2024 12:16:17 +0100 Subject: [PATCH] Fix: duplicated newlines when using dictation on iOS 18+. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a bug that started to happen with iOS 18, where you would get duplicated newlines after completing the dictation. The bug happens because, upon dictation completion, iOS sends the sequence of `beforeinput` events very quickly. With the `insertParagraph` ones, it can happen that the internal document range fails to update, resulting in duplicated newlines and missed content. This workaround is necessary due to the inability to distinguish text entered in dictation mode, as iOS WebKit doesn’t trigger composition events during dictation (https://bugs.webkit.org/show_bug.cgi?id=261764). --- .../controllers/level_2_input_controller.js | 9 +++++++-- src/trix/core/helpers/events.js | 19 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/trix/controllers/level_2_input_controller.js b/src/trix/controllers/level_2_input_controller.js index 65b302157..5cf79c840 100644 --- a/src/trix/controllers/level_2_input_controller.js +++ b/src/trix/controllers/level_2_input_controller.js @@ -1,4 +1,4 @@ -import { getAllAttributeNames, squishBreakableWhitespace } from "trix/core/helpers" +import { getAllAttributeNames, shouldRenderInmmediately, squishBreakableWhitespace } from "trix/core/helpers" import InputController from "trix/controllers/input_controller" import * as config from "trix/config" @@ -80,7 +80,12 @@ export default class Level2InputController extends InputController { if (handler) { this.withEvent(event, handler) - this.scheduleRender() + + if (shouldRenderInmmediately(event)) { + this.render() + } else { + this.scheduleRender() + } } }, diff --git a/src/trix/core/helpers/events.js b/src/trix/core/helpers/events.js index 4248830d3..35b7253dc 100644 --- a/src/trix/core/helpers/events.js +++ b/src/trix/core/helpers/events.js @@ -1,6 +1,6 @@ const testTransferData = { "application/x-trix-feature-detection": "test" } -export const dataTransferIsPlainText = function(dataTransfer) { +export const dataTransferIsPlainText = function (dataTransfer) { const text = dataTransfer.getData("text/plain") const html = dataTransfer.getData("text/html") @@ -20,7 +20,7 @@ export const dataTransferIsMsOfficePaste = ({ dataTransfer }) => { dataTransfer.getData("text/html").includes("urn:schemas-microsoft-com:office:office") } -export const dataTransferIsWritable = function(dataTransfer) { +export const dataTransferIsWritable = function (dataTransfer) { if (!dataTransfer?.setData) return false for (const key in testTransferData) { @@ -36,10 +36,23 @@ export const dataTransferIsWritable = function(dataTransfer) { return true } -export const keyEventIsKeyboardCommand = (function() { +export const keyEventIsKeyboardCommand = (function () { if (/Mac|^iP/.test(navigator.platform)) { return (event) => event.metaKey } else { return (event) => event.ctrlKey } })() + +export function shouldRenderInmmediately(inputEvent) { + if (/iPhone|iPad/i.test(navigator.userAgent)) { + // Handle duplicated newlines when using dictation on iOS 18+. Upon dictation completion, iOS sends + // the list of insertText / insertParagraph events in a quick sequence. For newlines, if we don't render + // the editor synchronously, the internal range fails to update and results in duplicated newlines. + // This workaround is necessary because iOS doesn't send composing events as expected while dictating: + // https://bugs.webkit.org/show_bug.cgi?id=261764 + return inputEvent.inputType === "insertParagraph" + } else { + return false + } +}