fix: Alternative fix to #3583, addresses some bugs that were introduced
This commit is contained in:
@@ -2,9 +2,11 @@ import { formatDistanceToNow } from "date-fns";
|
||||
import { deburr, sortBy } from "lodash";
|
||||
import { TextSelection } from "prosemirror-state";
|
||||
import * as React from "react";
|
||||
import mergeRefs from "react-merge-refs";
|
||||
import { Optional } from "utility-types";
|
||||
import insertFiles from "@shared/editor/commands/insertFiles";
|
||||
import embeds from "@shared/editor/embeds";
|
||||
import { Heading } from "@shared/editor/lib/getHeadings";
|
||||
import { supportedImageMimeTypes } from "@shared/utils/files";
|
||||
import getDataTransferFiles from "@shared/utils/getDataTransferFiles";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
@@ -45,12 +47,13 @@ export type Props = Optional<
|
||||
shareId?: string | undefined;
|
||||
embedsDisabled?: boolean;
|
||||
grow?: boolean;
|
||||
onHeadingsChange?: (headings: Heading[]) => void;
|
||||
onSynced?: () => Promise<void>;
|
||||
onPublish?: (event: React.MouseEvent) => any;
|
||||
};
|
||||
|
||||
function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
const { id, shareId } = props;
|
||||
const { id, shareId, onChange, onHeadingsChange } = props;
|
||||
const { documents } = useStores();
|
||||
const { showToast } = useToasts();
|
||||
const dictionary = useDictionary();
|
||||
@@ -58,6 +61,7 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
activeLinkEvent,
|
||||
setActiveLinkEvent,
|
||||
] = React.useState<MouseEvent | null>(null);
|
||||
const previousHeadings = React.useRef<Heading[] | null>(null);
|
||||
|
||||
const handleLinkActive = React.useCallback((event: MouseEvent) => {
|
||||
setActiveLinkEvent(event);
|
||||
@@ -216,11 +220,43 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
[]
|
||||
);
|
||||
|
||||
// Calculate if headings have changed and trigger callback if so
|
||||
const updateHeadings = React.useCallback(() => {
|
||||
if (onHeadingsChange) {
|
||||
const headings = ref?.current?.getHeadings();
|
||||
if (
|
||||
headings &&
|
||||
headings.map((h) => h.level + h.title).join("") !==
|
||||
previousHeadings.current?.map((h) => h.level + h.title).join("")
|
||||
) {
|
||||
previousHeadings.current = headings;
|
||||
onHeadingsChange(headings);
|
||||
}
|
||||
}
|
||||
}, [ref, onHeadingsChange]);
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
(event) => {
|
||||
onChange?.(event);
|
||||
updateHeadings();
|
||||
},
|
||||
[onChange, updateHeadings]
|
||||
);
|
||||
|
||||
const handleRefChanged = React.useCallback(
|
||||
(node: SharedEditor | null) => {
|
||||
if (node && !previousHeadings.current) {
|
||||
updateHeadings();
|
||||
}
|
||||
},
|
||||
[updateHeadings]
|
||||
);
|
||||
|
||||
return (
|
||||
<ErrorBoundary reloadOnChunkMissing>
|
||||
<>
|
||||
<LazyLoadedEditor
|
||||
ref={ref}
|
||||
ref={mergeRefs([ref, handleRefChanged])}
|
||||
uploadFile={onUploadFile}
|
||||
onShowToast={showToast}
|
||||
embeds={embeds}
|
||||
@@ -229,6 +265,7 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
onHoverLink={handleLinkActive}
|
||||
onClickLink={onClickLink}
|
||||
onSearchLink={handleSearchLink}
|
||||
onChange={handleChange}
|
||||
placeholder={props.placeholder || ""}
|
||||
defaultValue={props.defaultValue || ""}
|
||||
/>
|
||||
|
||||
@@ -75,7 +75,7 @@ type Props = WithTranslation &
|
||||
@observer
|
||||
class DocumentScene extends React.Component<Props> {
|
||||
@observable
|
||||
editor: TEditor | null;
|
||||
editor = React.createRef<TEditor>();
|
||||
|
||||
@observable
|
||||
isUploading = false;
|
||||
@@ -157,7 +157,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
}
|
||||
|
||||
replaceDocument = (template: Document | Revision) => {
|
||||
const editorRef = this.editor;
|
||||
const editorRef = this.editor.current;
|
||||
|
||||
if (!editorRef) {
|
||||
return;
|
||||
@@ -194,7 +194,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
const { toasts, history, location, t } = this.props;
|
||||
const restore = location.state?.restore;
|
||||
const revisionId = location.state?.revisionId;
|
||||
const editorRef = this.editor;
|
||||
const editorRef = this.editor.current;
|
||||
|
||||
if (!editorRef || !restore) {
|
||||
return;
|
||||
@@ -379,17 +379,8 @@ class DocumentScene extends React.Component<Props> {
|
||||
const { document, auth } = this.props;
|
||||
this.getEditorText = getEditorText;
|
||||
|
||||
// Keep headings in sync for table of contents
|
||||
const headings = this.editor?.getHeadings() ?? [];
|
||||
if (
|
||||
headings.map((h) => h.level + h.title).join("") !==
|
||||
this.headings.map((h) => h.level + h.title).join("")
|
||||
) {
|
||||
this.headings = headings;
|
||||
}
|
||||
|
||||
// Keep derived task list in sync
|
||||
const tasks = this.editor?.getTasks();
|
||||
const tasks = this.editor.current?.getTasks();
|
||||
const total = tasks?.length ?? 0;
|
||||
const completed = tasks?.filter((t) => t.completed).length ?? 0;
|
||||
document.updateTasks(total, completed);
|
||||
@@ -414,6 +405,10 @@ class DocumentScene extends React.Component<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
onHeadingsChange = (headings: Heading[]) => {
|
||||
this.headings = headings;
|
||||
};
|
||||
|
||||
onChangeTitle = action((value: string) => {
|
||||
this.title = value;
|
||||
this.props.document.title = value;
|
||||
@@ -427,11 +422,6 @@ class DocumentScene extends React.Component<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
handleRef = (ref: TEditor | null) => {
|
||||
this.editor = ref;
|
||||
this.headings = this.editor?.getHeadings() ?? [];
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
document,
|
||||
@@ -586,7 +576,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
<Editor
|
||||
id={document.id}
|
||||
key={embedsDisabled ? "disabled" : "enabled"}
|
||||
ref={this.handleRef}
|
||||
ref={this.editor}
|
||||
multiplayer={collaborativeEditing}
|
||||
shareId={shareId}
|
||||
isDraft={document.isDraft}
|
||||
@@ -603,6 +593,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
onCreateLink={this.props.onCreateLink}
|
||||
onChangeTitle={this.onChangeTitle}
|
||||
onChange={this.onChange}
|
||||
onHeadingsChange={this.onHeadingsChange}
|
||||
onSave={this.onSave}
|
||||
onPublish={this.onPublish}
|
||||
onCancel={this.goBack}
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^11.16.6",
|
||||
"react-medium-image-zoom": "^3.1.3",
|
||||
"react-merge-refs": "^1.1.0",
|
||||
"react-portal": "^4.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-table": "^7.7.0",
|
||||
|
||||
@@ -12459,6 +12459,11 @@ react-medium-image-zoom@^3.1.3:
|
||||
resolved "https://registry.yarnpkg.com/react-medium-image-zoom/-/react-medium-image-zoom-3.1.3.tgz#b1470abc5a342d65c23021c01bafa8c731821478"
|
||||
integrity sha512-5CoU8whSCz5Xz2xNeGD34dDfZ6jaf/pybdfZh8HNUmA9mbXbLfj0n6bQWfEUwkq9lsNg1sEkyeIJq2tcvZY8bw==
|
||||
|
||||
react-merge-refs@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06"
|
||||
integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==
|
||||
|
||||
react-portal@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.1.tgz#12c1599238c06fb08a9800f3070bea2a3f78b1a6"
|
||||
|
||||
Reference in New Issue
Block a user