Fix EditorJS read-mode toggle race condition (#1711)
* Fix EditorJS read-mode toggle race condition Fixes rare edge case when toggling read mode is peerformed at the same time. * Make PromiseQueue function
This commit is contained in:
parent
a01a2b5a30
commit
4c53dd0792
4 changed files with 35 additions and 14 deletions
20
package-lock.json
generated
20
package-lock.json
generated
|
@ -1826,12 +1826,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@editorjs/editorjs": {
|
"@editorjs/editorjs": {
|
||||||
"version": "2.20.0",
|
"version": "2.22.2",
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.22.2.tgz",
|
||||||
"integrity": "sha512-e6DWi8bMypFhovq9R6cefaDWVfrlVU++Q7ABp79+MxZIuC/SKAW5EtxBbKPL22H/Mc3bJIhZCxOqEl70HBh2yw==",
|
"integrity": "sha512-rPCv7Z5LZebreQaaL4DZuWzoVGEqwB+P7BF1dsefGQNBmLyeLF412topeW2b6e+g4l1oQ7t75kCOACNTEyYYIA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"codex-notifier": "^1.1.2",
|
"codex-notifier": "^1.1.2",
|
||||||
"codex-tooltip": "^1.0.1"
|
"codex-tooltip": "^1.0.2",
|
||||||
|
"nanoid": "^3.1.22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@editorjs/embed": {
|
"@editorjs/embed": {
|
||||||
|
@ -11530,9 +11531,9 @@
|
||||||
"integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg=="
|
"integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg=="
|
||||||
},
|
},
|
||||||
"codex-tooltip": {
|
"codex-tooltip": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.4.tgz",
|
||||||
"integrity": "sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w=="
|
"integrity": "sha512-Ud+N+y8PMIa9xGyKuo2j3q8QlfTzkMWQ5KeRrbCDerwVn7xq45nqPKQCFBXEMV0YI42/OqSMnsxP8MyVAyVhnA=="
|
||||||
},
|
},
|
||||||
"collapse-white-space": {
|
"collapse-white-space": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
|
@ -21820,6 +21821,11 @@
|
||||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"nanoid": {
|
||||||
|
"version": "3.1.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
|
||||||
|
"integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ=="
|
||||||
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
"version": "1.2.13",
|
"version": "1.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.4.15",
|
"@apollo/client": "^3.4.15",
|
||||||
"@editorjs/editorjs": "^2.19.3",
|
"@editorjs/editorjs": "^2.22.2",
|
||||||
"@editorjs/header": "^2.6.1",
|
"@editorjs/header": "^2.6.1",
|
||||||
"@editorjs/image": "^2.6.0",
|
"@editorjs/image": "^2.6.0",
|
||||||
"@editorjs/list": "^1.6.1",
|
"@editorjs/list": "^1.6.1",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import EditorJS, { LogLevels, OutputData } from "@editorjs/editorjs";
|
import EditorJS, { LogLevels, OutputData } from "@editorjs/editorjs";
|
||||||
import { FormControl, FormHelperText, InputLabel } from "@material-ui/core";
|
import { FormControl, FormHelperText, InputLabel } from "@material-ui/core";
|
||||||
|
import { PromiseQueue } from "@saleor/misc";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({
|
||||||
const [isFocused, setFocus] = React.useState(false);
|
const [isFocused, setFocus] = React.useState(false);
|
||||||
const editor = React.useRef<EditorJS>();
|
const editor = React.useRef<EditorJS>();
|
||||||
const editorContainer = React.useRef<HTMLDivElement>();
|
const editorContainer = React.useRef<HTMLDivElement>();
|
||||||
const prevTogglePromise = React.useRef<Promise<boolean>>(); // used to await subsequent toggle invocations
|
const togglePromiseQueue = React.useRef(PromiseQueue()); // used to await subsequent toggle invocations
|
||||||
const initialMount = React.useRef(true);
|
const initialMount = React.useRef(true);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
|
@ -75,15 +76,14 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({
|
||||||
if (editor.current?.readOnly) {
|
if (editor.current?.readOnly) {
|
||||||
// readOnly.toggle() by itself does not enqueue the events and will result in a broken output if invocations overlap
|
// readOnly.toggle() by itself does not enqueue the events and will result in a broken output if invocations overlap
|
||||||
// Remove this logic when this is fixed in EditorJS
|
// Remove this logic when this is fixed in EditorJS
|
||||||
if (prevTogglePromise.current instanceof Promise) {
|
togglePromiseQueue.current.add(() =>
|
||||||
await prevTogglePromise.current;
|
editor.current.readOnly.toggle(disabled)
|
||||||
}
|
);
|
||||||
prevTogglePromise.current = editor.current.readOnly.toggle(disabled);
|
|
||||||
|
|
||||||
// Switching to readOnly with empty blocks present causes the editor to freeze
|
// Switching to readOnly with empty blocks present causes the editor to freeze
|
||||||
// Remove this logic when this is fixed in EditorJS
|
// Remove this logic when this is fixed in EditorJS
|
||||||
if (!disabled && !data?.blocks?.length) {
|
if (!disabled && !data?.blocks?.length) {
|
||||||
await prevTogglePromise.current;
|
await togglePromiseQueue.current.queue;
|
||||||
editor.current.clear();
|
editor.current.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/misc.ts
15
src/misc.ts
|
@ -460,3 +460,18 @@ export const flatten = (obj: unknown) => {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function PromiseQueue() {
|
||||||
|
let queue = Promise.resolve();
|
||||||
|
|
||||||
|
function add<T>(operation: (value: T | void) => PromiseLike<T>) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
queue = queue
|
||||||
|
.then(operation)
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { queue, add };
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue