perf: Remove markdown serialize from editor render path (#3567)
* perf: Remove markdown serialize from editor render path * fix: Simplify heading equality check * perf: Add cache for slugified headings * tsc
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
} from "react-router";
|
||||
import styled from "styled-components";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import { Heading } from "@shared/editor/lib/getHeadings";
|
||||
import getTasks from "@shared/utils/getTasks";
|
||||
import RootStore from "~/stores/RootStore";
|
||||
import Document from "~/models/Document";
|
||||
@@ -29,6 +30,7 @@ import PageTitle from "~/components/PageTitle";
|
||||
import PlaceholderDocument from "~/components/PlaceholderDocument";
|
||||
import RegisterKeyDown from "~/components/RegisterKeyDown";
|
||||
import withStores from "~/components/withStores";
|
||||
import type { Editor as TEditor } from "~/editor";
|
||||
import { NavigationNode } from "~/types";
|
||||
import { client } from "~/utils/ApiClient";
|
||||
import { isCustomDomain } from "~/utils/domains";
|
||||
@@ -73,7 +75,7 @@ type Props = WithTranslation &
|
||||
@observer
|
||||
class DocumentScene extends React.Component<Props> {
|
||||
@observable
|
||||
editor = React.createRef<typeof Editor>();
|
||||
editor = React.createRef<TEditor>();
|
||||
|
||||
@observable
|
||||
isUploading = false;
|
||||
@@ -96,6 +98,9 @@ class DocumentScene extends React.Component<Props> {
|
||||
@observable
|
||||
title: string = this.props.document.title;
|
||||
|
||||
@observable
|
||||
headings: Heading[] = [];
|
||||
|
||||
getEditorText: () => string = () => this.props.document.text;
|
||||
|
||||
componentDidMount() {
|
||||
@@ -158,7 +163,6 @@ class DocumentScene extends React.Component<Props> {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'view' does not exist on type 'unknown'.
|
||||
const { view, parser } = editorRef;
|
||||
view.dispatch(
|
||||
view.state.tr
|
||||
@@ -375,13 +379,24 @@ class DocumentScene extends React.Component<Props> {
|
||||
const { document, auth } = this.props;
|
||||
this.getEditorText = getEditorText;
|
||||
|
||||
// If the multiplayer editor is enabled then we still want to keep the local
|
||||
// text value in sync as it is used as a cache.
|
||||
// Keep headings in sync for table of contents
|
||||
const headings = this.editor.current?.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.current?.getTasks();
|
||||
const total = tasks?.length ?? 0;
|
||||
const completed = tasks?.filter((t) => t.completed).length ?? 0;
|
||||
document.updateTasks(total, completed);
|
||||
|
||||
// If the multiplayer editor is enabled we're done here as changes are saved
|
||||
// through the persistence protocol. The rest of this method is legacy.
|
||||
if (auth.team?.collaborativeEditing) {
|
||||
action(() => {
|
||||
document.text = this.getEditorText();
|
||||
document.tasks = getTasks(document.text);
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -429,12 +444,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
const embedsDisabled =
|
||||
(team && team.documentEmbeds === false) || document.embedsDisabled;
|
||||
|
||||
const headings = this.editor.current
|
||||
? // @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
|
||||
this.editor.current.getHeadings()
|
||||
: [];
|
||||
|
||||
const hasHeadings = headings.length > 0;
|
||||
const hasHeadings = this.headings.length > 0;
|
||||
const showContents =
|
||||
ui.tocVisible &&
|
||||
((readOnly && hasHeadings) || team?.collaborativeEditing);
|
||||
@@ -549,7 +559,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
sharedTree={this.props.sharedTree}
|
||||
onSelectTemplate={this.replaceDocument}
|
||||
onSave={this.onSave}
|
||||
headings={headings}
|
||||
headings={this.headings}
|
||||
/>
|
||||
<MaxWidth
|
||||
archived={document.isArchived}
|
||||
@@ -564,7 +574,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
<Flex auto={!readOnly}>
|
||||
{showContents && (
|
||||
<Contents
|
||||
headings={headings}
|
||||
headings={this.headings}
|
||||
isFullWidth={document.fullWidth}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -139,9 +139,6 @@ function MultiplayerEditor({ onSynced, ...props }: Props, ref: any) {
|
||||
});
|
||||
|
||||
if (debug) {
|
||||
provider.on("status", (ev: ConnectionStatusEvent) =>
|
||||
Logger.debug("collaboration", "status", ev)
|
||||
);
|
||||
provider.on("message", (ev: MessageEvent) =>
|
||||
Logger.debug("collaboration", "incoming", {
|
||||
message: ev.message,
|
||||
|
||||
Reference in New Issue
Block a user