Skip to content

Commit 073891e

Browse files
authored
fix: block id null when using collaboration (#419)
* temp fix for error * proper fix * clone node before mutating
1 parent 61ed6e0 commit 073891e

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

examples/editor/src/main.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ window.React = React;
66

77
const root = createRoot(document.getElementById("root")!);
88
root.render(
9-
<React.StrictMode>
10-
<App />
11-
</React.StrictMode>
9+
// TODO: StrictMode is causing duplicate mounts and conflicts with collaboration
10+
// <React.StrictMode>
11+
<App />
12+
// </React.StrictMode>
1213
);

packages/core/src/BlockNoteEditor.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
BlockSchema,
3030
PartialBlock,
3131
} from "./extensions/Blocks/api/blockTypes";
32-
import { TextCursorPosition } from "./extensions/Blocks/api/cursorPositionTypes";
3332
import {
3433
DefaultBlockSchema,
3534
defaultBlockSchema,
@@ -42,6 +41,7 @@ import {
4241
import { Selection } from "./extensions/Blocks/api/selectionTypes";
4342
import { getBlockInfoFromPos } from "./extensions/Blocks/helpers/getBlockInfoFromPos";
4443

44+
import { TextCursorPosition } from "./extensions/Blocks/api/cursorPositionTypes";
4545
import { FormattingToolbarProsemirrorPlugin } from "./extensions/FormattingToolbar/FormattingToolbarPlugin";
4646
import { HyperlinkToolbarProsemirrorPlugin } from "./extensions/HyperlinkToolbar/HyperlinkToolbarPlugin";
4747
import { ImageToolbarProsemirrorPlugin } from "./extensions/ImageToolbar/ImageToolbarPlugin";
@@ -217,6 +217,12 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
217217

218218
this.uploadFile = newOptions.uploadFile;
219219

220+
if (newOptions.collaboration && newOptions.initialContent) {
221+
console.warn(
222+
"When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider"
223+
);
224+
}
225+
220226
const initialContent =
221227
newOptions.initialContent ||
222228
(options.collaboration
@@ -233,20 +239,35 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
233239
...newOptions._tiptapOptions,
234240
onBeforeCreate(editor) {
235241
newOptions._tiptapOptions?.onBeforeCreate?.(editor);
236-
if (!initialContent) {
237-
// when using collaboration
238-
return;
239-
}
240-
241242
// We always set the initial content to a single paragraph block. This
242243
// allows us to easily replace it with the actual initial content once
243244
// the TipTap editor is initialized.
244245
const schema = editor.editor.schema;
246+
247+
// This is a hack to make "initial content detection" by y-prosemirror (and also tiptap isEmpty)
248+
// properly detect whether or not the document has changed.
249+
// We change the doc.createAndFill function to make sure the initial block id is set, instead of null
250+
let cache: any;
251+
const oldCreateAndFill = schema.nodes.doc.createAndFill;
252+
(schema.nodes.doc as any).createAndFill = (...args: any) => {
253+
if (cache) {
254+
return cache;
255+
}
256+
const ret = oldCreateAndFill.apply(schema.nodes.doc, args);
257+
258+
// create a copy that we can mutate (otherwise, assigning attrs is not safe and corrupts the pm state)
259+
const jsonNode = JSON.parse(JSON.stringify(ret!.toJSON()));
260+
jsonNode.content[0].content[0].attrs.id = "initialBlockId";
261+
262+
cache = Node.fromJSON(schema, jsonNode);
263+
return ret;
264+
};
265+
245266
const root = schema.node(
246267
"doc",
247268
undefined,
248269
schema.node("blockGroup", undefined, [
249-
blockToNode({ id: "initialBlock", type: "paragraph" }, schema),
270+
blockToNode({ id: "initialBlockId", type: "paragraph" }, schema),
250271
])
251272
);
252273
editor.editor.options.content = root.toJSON();

0 commit comments

Comments
 (0)