There are input events unique for the moment of when the issue is reproduced. beforeinput, textInput events containing the whole text with \n are observed. (in the videos above) In Chrome - where there is no repro - I do not observe those events.
e.g. 'Like this one!\n\nTry it out by typing in here or see more examples.\n\n'
My environment
WebView: Android WebView 110
OS: Android 13, One UI 5.0
Device: Samsung Galaxy A52, but it happens on various Samsung devices
userAgent example: Mozilla/5.0 (Linux; Android 13; SM-A525F Build/TP1A.220624.014; ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/95.0.4638.69 Mobile Safari/537.36 BingSapphire/24.1.410205314
Thanks for digging into this to such an extent! I hadn’t seen this one yet, but there have been somewhat similar text-duplication issues with GBoard. The fact that it happens in a plan contentEditable element as well suggests that it’s probably not triggered by ProseMirror doing anything weird, so I don’t expect it can be fixed in ProseMirror, except with really bad kludges that try to detect when something like this happens and then revert it. But those always cause secondary issues (false positives, etc) so I really don’t like adding them.
Reporting the bug to Samsung might, eventually, get this fixed, but it seems the bigger the company the longer the response time on stuff like this, so that probably also won’t help a lot.
That’s right - the core reason of this issue is outside of ProseMirror
I can only imagine that it must be a headache.
This issue is terrible TBH, lots of users are reporting it, because it happens for every default Samsung setup in a few browsers, and within apps with a WebView (Samsung Internet too). It’s already 6 weeks old, so it might we worth giving it a shot.
In this case it would be maybe an option to check:
When a single beforeinput (or textInput) event contains "\n\n" at the end
AND the data is exactly the same as full current of .innerText + two additional new lines at the end "\n\n"
AND it’s Android
AND maybe some nodes check or selection check to distinguish from other events
Then preventDefault so it doesn’t get to input event if possible. If not, undo the changes.
I guess I could somehow hook up to ProseMirror, or just the events on my contenteditable and do it locally in my app?
I have managed to create a workaround for the Samsung Keyboard issue on my side. I tested this a bit, and it works for contenteditableprosemirror.net demo and my ProseMirror usage.
Feel free to use it if you encounter the issue yourself. Note that the fix is fragile and might stop working with new Samsung Keyboard update. Hopefully it won’t be needed soon.
@marijn let me know if you have any comments to the code below, and feel free to use it in ProseMirror if you wish. I’ll update this post if it doesn’t pass QA.
if (navigator.userAgent.includes("Android")) {
const isSamsungKeyboardNewlineBugEvent = (event) => {
// First, light checks to avoid heavy DOM reads
const target = event.target;
const newText = event.data;
const isPotentiallySamsungKeyboardBugEvent =
target.isContentEditable &&
event.inputType === "insertText" &&
!event.isComposing &&
newText &&
newText.endsWith("\n\n"); // very specific for this Samsung Keyboard bug
if (!isPotentiallySamsungKeyboardBugEvent) {
return false;
}
// Then, potentially heavier checks reading the DOM
// While the Samsung Keyboard bug happens (in our ProseMirror case), the whole text is selected.
// In case of other `contenteditable`s, this might not be the case, but we want to narrow it down as much as possible,
// to avoid preventing events that user actually meant.
const selection = document.getSelection();
if (selection.rangeCount === 1) {
const range = document.getSelection().getRangeAt(0);
const isNoTextSelected = range.collapsed === true;
if (isNoTextSelected || range.startOffset !== 0) {
return false;
}
}
// Samsung Keyboard bug event changes the number of line breaks,
// but rest is the same as existing .innerText (not the same as .textContent!)
const existingText = target.innerText;
return (
existingText.length > 0 &&
existingText.replace(/\n/g, "") === newText.replace(/\n/g, "")
);
};
// You can also attach it to a contenteditable instead.
document.addEventListener("beforeinput", (event) => {
if (isSamsungKeyboardNewlineBugEvent(event)) {
event.preventDefault();
}
});
}
We use Froala and not ProseMirror (though it looks great!) and we’re getting killed by this issue. We originally thought it was Froala-specific, but I tried a different editor and we’re still seeing the same problem, which led me here.
I really hope Samsung fixes this – we saw it months ago and it seemed to resolve itself now it’s reared its head again.
@Johny after a deep investigation, it looks like the issue is with Grammarly actually!, version 13 suggest text corrections are based on Grammarly.
can be fixed by Samsung Keyboard → Suggest text corrections → Manage apps → turn off your app or turn them all
So I think the best solution will be to ignore somehow Grammarly on the contenteditable div
@marijn maybe Grammarly will be better on fix this
I’ve reported this to Grammarly support right now.
I’m not sure if there is a Grammarly process doing anything directly, or is it the Samsung Keyboard using the Grammarly service under the hood, and then causing input in conjunction with WebView contenteditable.
I can’t test it right now, but I wonder if it happens with this Grammarly keyboard too.
EDIT: It does not reproduce with Grammarly keyboard, it doesn’t touch the HTML contents even if middle of the sentence gets changed by Grammarly.
Either way, I’m going to use the hack-fix above for now until they fix it.
Grammarly has a long history of breaking editors by doing crude things to the DOM, so I’m not super surprised there. It doesn’t seem possible to install the Samsung virtual keyboard on a non-Samsung phone, making this tricky to test for me (there are apps called Samsung Keyboard in the store, but they look like obvious fakes). Thanks for posting a kludge. Ideally the software causing this, whether it’s the virtual keyboard or grammarly, gets its act together, but if not I guess it might be necessary to add the kludge to the core.
@Johny thanks for that fix! I gave it a try on our side but unfortunately, we’re still seeing it occur. Still investigating why; we’re seeing this in our mobile app, which uses a WebView, so that could have something to do with it.
@GuySerfaty nice find! I am definitely going to give this a try.
On another subject, I have a support ticket open with Samsung, so maybe they will pressure Grammarly (or maybe not ) if that is the problem.
I’m getting reports in CodeMirror issue #1087 that an update to Samsung Keyboard might fix this. If one of you can reproduce this, could you see if it still happens with the very latest version? (All Samsung devices in my house refuse to upgrade the keyboard to a recent version, unfortunately.)
@jdixon Add some logs in the fix and see whether the selection matches when the problem reproduces. If not, remove the selection checking piece of code. Use the code from gist, not the code I’ve posted here - it missed some scenarios.
@marijn great to see that! For some reason I’m stuck at 5.6.00 (can still reproduce) and can’t install 5.6.10 which is mentioned there.
Hey everyone – I spoke with Samsung and they said it’s a known issue with One UI. Fortunately, updating to One UI 5.1 appears to fix it. Let me know if updating solves it for you as well!
Hi, I’m one of Trix developers and just wanted to mention that we found the same issue in Trix. Definitely not related to any particular editor, but caused by Samsung’s awful contenteditable support.
We ended up adding some code to detect when the Samsung keyboard starts and ends that flurry of broken events.