I tried to update the editor content through editorView.dispatch(newTr) programmatically. However it will influence undo manager’s history and when user call redo command, the content will be erased. Which is not what I expect.
So, what’s the correct way to create an editor with default content?
I expect the user behavior as:
When a user join an empty room, the editor is created with default content.
When second user join the room, the editor content is just synced from other users.
Only one peer should populate the document with content. Populating content is an insertion. Therefore, duplicate insertions of “default content” will always lead to duplication of content.
Your first approach doesn’t work because y-prosemirror prefers the state of the Yjs type and overrides the existing content. Otherwise, we’d also get duplication of content.
My recommendation is to only initialize a document once. This can happen on the first client that creates a document. You will avoid a lot of complicated issues if you simply keep the Yjs document around instead of re-initializing it every time you open a document.
As I said, it makes more sense to design your application in a way so that only the client that creates a document populates it with content.
I often see developers working around this, trying to populate the document from a JSON representation of their data instead of simply storing the Yjs document (possibly alongside the JSON representation). This leads to all kinds of problems and complexities that you really want to avoid.
Waiting for a “sync” event is not good enough. You could have a client with an older version of the document (re)-joining the session after a short disconnection. In the best case, your re-populated content gets duplicated, in the worst-case newer changes get overwritten by the old version.
If you go this route, you need to implement some kind of session management. y-websocket does not support this. You also want to “populate” the content on the backend instead of the client. You need to have some process to elect a peer that initializes content. This can happen, for example, through a proper lock implementation (e.g. redlock - super complex, hard to really understand). Electing a client-peer, with a potentially bad network connection, will lead to problems that are impossible to debug, so choose a peer with a good network connection (server).
You need another way to prevent duplication of content.
Waiting for the sync event is also not good enough, because sometimes clients are just disconnected for a short period of time. Once they rejoin… BAM… duplication of content.
I hear you that you want to store everything as markdown. I’m just saying that it is highly recommended to store the Yjs document instead somewhere. Once the editing history is lost, Yjs will duplicate content.
It doesn’t matter which framework for conflict resolution you use (CRDTs, OT, Git, …), you must always retain the history, otherwise, you can’t resolve conflicts.
Content should be initialized only once by the client that initially creates a document. After that, you should keep the Yjs document laying around somewhere (indexeddb, file system, database, wherever). Otherwise, you need to find your own way around the duplication issue (you asked for the recommended approach).