fix(desktop): avoid redundant editor hydration
This commit is contained in:
1 parent
07a3da44d7
commit
cf62dc1b92
3 files changed
+51
-5
No files matched your search
@@ -222,7 +222,10 @@ import {
|
||||
ResourceActionMenu,
|
||||
type NoteLinkHintPosition,
|
||||
} from "./editor/EditorPaneChrome";
|
||||
import { resolveEditorDraftState } from "./editor/editor-draft-state";
|
||||
import {
|
||||
resolveEditorDraftState,
|
||||
shouldReplaceEditorDocument,
|
||||
} from "./editor/editor-draft-state";
|
||||
import type { EdgeEverPluginHost, PluginEditorAdapter } from "@/lib/plugins/plugin-host";
|
||||
import {
|
||||
useEditorResourceActions,
|
||||
@@ -2212,10 +2215,14 @@ const RichEditorPane = ({
|
||||
} = resolvedDraft;
|
||||
|
||||
const alreadyHydratedSameMemo = sameMemo && hydratedMemoIdRef.current === memo.id;
|
||||
const currentEditorDocument = isEditorReady(currentEditor)
|
||||
? currentEditor.getJSON() as TiptapDoc
|
||||
: null;
|
||||
const shouldReplaceDocument = shouldReplaceEditorDocument(currentEditorDocument, nextContent);
|
||||
const editorMarkdownMatches = Boolean(
|
||||
alreadyHydratedSameMemo &&
|
||||
isEditorReady(currentEditor) &&
|
||||
docToMarkdown(currentEditor.getJSON() as TiptapDoc) === nextMarkdown &&
|
||||
currentEditorDocument &&
|
||||
docToMarkdown(currentEditorDocument) === nextMarkdown &&
|
||||
title === nextTitle &&
|
||||
tagsText === nextTagsText
|
||||
);
|
||||
@@ -2275,7 +2282,7 @@ const RichEditorPane = ({
|
||||
setMarkdownSource(nextMarkdown);
|
||||
setMobilePlainTextElementValue(mobileTextAreaRef.current, nextMarkdown);
|
||||
|
||||
if (isEditorReady(currentEditor)) {
|
||||
if (isEditorReady(currentEditor) && shouldReplaceDocument) {
|
||||
try {
|
||||
currentEditor.commands.setContent(nextContent);
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { resolveEditorDraftState } from "./editor-draft-state.ts";
|
||||
import {
|
||||
resolveEditorDraftState,
|
||||
shouldReplaceEditorDocument,
|
||||
} from "./editor-draft-state.ts";
|
||||
|
||||
const doc = (text) => ({ type: "doc", content: [{ type: "paragraph", content: [{ type: "text", text }] }] });
|
||||
|
||||
@@ -125,3 +128,28 @@ describe("editor draft source resolution", () => {
|
||||
expect(state.hasUnsavedChanges).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("editor document hydration", () => {
|
||||
test("does not replace a newly mounted empty note with the same empty document", () => {
|
||||
const emptyDocument = { type: "doc", content: [{ type: "paragraph" }] };
|
||||
expect(shouldReplaceEditorDocument(emptyDocument, emptyDocument)).toBe(false);
|
||||
});
|
||||
|
||||
test("replaces the mounted memo when a recovered local draft has different content", () => {
|
||||
expect(shouldReplaceEditorDocument(doc("remote body"), doc("recovered draft"))).toBe(true);
|
||||
});
|
||||
|
||||
test("does not treat rich documents with the same markdown text as identical", () => {
|
||||
const plainDocument = doc("same body");
|
||||
const themedDocument = {
|
||||
type: "doc",
|
||||
content: [{ type: "edgeeverThemeBlock", attrs: { theme: "paper" }, content: plainDocument.content }],
|
||||
};
|
||||
|
||||
expect(shouldReplaceEditorDocument(plainDocument, themedDocument)).toBe(true);
|
||||
});
|
||||
|
||||
test("hydrates when the current editor document cannot be read", () => {
|
||||
expect(shouldReplaceEditorDocument(null, doc("remote body"))).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -24,6 +24,17 @@ export type EditorDraftState = {
|
||||
hasUnsavedChanges: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* TipTap is created with the memo snapshot before the asynchronous local-draft
|
||||
* lookup finishes. Replacing that already-identical document during hydration
|
||||
* resets the ProseMirror view and selection, which is visible in slower desktop
|
||||
* runtimes as a second editor paint.
|
||||
*/
|
||||
export const shouldReplaceEditorDocument = (
|
||||
currentDocument: TiptapDoc | null,
|
||||
nextDocument: TiptapDoc,
|
||||
) => currentDocument === null || JSON.stringify(currentDocument) !== JSON.stringify(nextDocument);
|
||||
|
||||
type ResolveEditorDraftStateInput = {
|
||||
memo: MemoDetail;
|
||||
draft?: LocalDraft | null;
|
||||
|
||||
Reference in new issue
Block a user