Skip to content

Commit

Permalink
Better Samsung Andorid keyboard detection
Browse files Browse the repository at this point in the history
  • Loading branch information
afcapel committed Nov 28, 2022
1 parent 3ea729d commit f492c77
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 13 deletions.
5 changes: 5 additions & 0 deletions src/trix/controllers/input_controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import BasicObject from "trix/core/basic_object"
import MutationObserver from "trix/observers/mutation_observer"
import FileVerificationOperation from "trix/operations/file_verification_operation"
import SamsungAndroidKeyboardDetector from "../models/samsung_android_keybpard_detector"

import { handleEvent, innerElementIsActive } from "trix/core/helpers"

Expand All @@ -13,6 +14,7 @@ export default class InputController extends BasicObject {
this.element = element
this.mutationObserver = new MutationObserver(this.element)
this.mutationObserver.delegate = this
this.samsungKeyboardDetector = new SamsungAndroidKeyboardDetector(this.element)
for (const eventName in this.constructor.events) {
handleEvent(eventName, { onElement: this.element, withCallback: this.handlerFor(eventName) })
}
Expand Down Expand Up @@ -55,6 +57,9 @@ export default class InputController extends BasicObject {
if (!event.defaultPrevented) {
this.handleInput(() => {
if (!innerElementIsActive(this.element)) {
this.samsungKeyboardDetector.process(event)
if (this.samsungKeyboardDetector.buggyMode) return

this.eventName = eventName
this.constructor.events[eventName].call(this, event)
}
Expand Down
14 changes: 1 addition & 13 deletions src/trix/controllers/level_2_input_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ export default class Level2InputController extends InputController {
},

beforeinput(event) {
if (guardAgainstSpuriousAndroidEvents(event)) return

const handler = this.constructor.inputTypes[event.inputType]

if (handler) {
Expand All @@ -82,7 +80,7 @@ export default class Level2InputController extends InputController {
},

input(event) {
if (!emittedBySamsungKeyboard(event)) selectionChangeObserver.reset()
selectionChangeObserver.reset()
},

dragstart(event) {
Expand Down Expand Up @@ -609,13 +607,3 @@ const pointFromEvent = (event) => ({
x: event.clientX,
y: event.clientY,
})

// Samsung keyboard running in a webview emits insertText events
// with composed true, in addition to composition events, let's ignore those
const guardAgainstSpuriousAndroidEvents = (event) => {
return emittedBySamsungKeyboard(event) && event.data !== ". "
}

const emittedBySamsungKeyboard = (event) => {
return config.browser.samsungAndroid && event.inputType === "insertText" && !event.sourceCapabilities
}
41 changes: 41 additions & 0 deletions src/trix/models/samsung_android_keybpard_detector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as config from "trix/config"

// The Samsung keyboard on Android can enter a buggy state in which it emmits a flurry of confused events that,
// if processed, corrupts the editor. This class detects when that flurry of events starts and ends so we can
// ignore those events in the input controller.
export default class SamsungAndroidKeyboardDetector {
constructor(element) {
this.element = element
}

process(event) {
this.previousEvent = this.event
this.event = event

this.checkBuggyModeStart()
this.checkBuggyModeEnd()
}

// The buggy mode always starts with an insertText event with the same text as the editor element, except for an extra new line
// after the cursor
checkBuggyModeStart() {
if (config.browser.samsungAndroid && this.insertTextAfterUnidentifiedChar() && differsInOneCR(this.element.innerText, this.event.data)) {
this.buggyMode = true
this.event.preventDefault()
}
}

// The flurry of buggy events are always insertText. If we see any other type, it means it is over.
checkBuggyModeEnd() {
if (this.buggyMode && this.event.inputType !== "insertText") {
this.buggyMode = false
}
}

insertTextAfterUnidentifiedChar() {
return this.event.type === "beforeinput" && this.event.inputType === "insertText" && this.event.data && this.previousEvent.key === "Unidentified"
}
}

const differsInOneCR = (text1, text2) => Math.abs(text1.length - text2.length) === 1 && normalize(text1) === normalize(text2)
const normalize = (text) => text.replace(/\s+/g, " ")

0 comments on commit f492c77

Please sign in to comment.