-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
What’s the bug you are facing?
We are integrating the tiptap collaboration feature using hocuspocus.
We thought it would be the most simple solution if we would just load the document on the client instead of fetching it from the hocuspocus backend.
This would have these advantages:
- No need to communicate with the CMS backend on hocuspocus,
- No need to authenticate on the CMS backend via hocuspocus,
- No need to load any of the client extensions when creating the document.
When opening the document and loading the tiptap editor, the content should be loaded into tiptap on the client side only in one of these two cases:
- The person which is opening the document/loading the editor is the first one opening it,
or - The document was not yet created on the hocuspocus server.
In these cases I would initialize the tiptap editor with the content from a textarea.
In other cases (second person opening the document, document already created on the hocuspocus server) the document would be loaded via the collaboration sync mechanism.
I would now need to know if any other person has authenticated on the hocuspocus server for the same document,
or if the document already exists on the hocuspocus server.
I tried several methods, nothing did work.
How can we reproduce the bug on our side?
First, this is our most simple hocuspocus server config:
import { Server } from "@hocuspocus/server";
import jwt from "jwt-simple";
const url = "http://info.cern.ch/hypertext/WWW/TheProject.html";
const ARGS = {};
if (process.argv.length < 4) {
console.warn("Usage: node ./src/tiptap-collaboration-server.js PORT JWT_SECRET");
process.exit();
}
ARGS.port = process.argv[2];
ARGS.secret = process.argv[3];
const server = Server.configure({
port: ARGS.port,
async onAuthenticate({ token, documentName }) {
try {
const decoded = jwt.decode(token, ARGS.secret);
console.log(
`Authentification from ${decoded.user} on document ${documentName}.`
);
return {
user: {
name: decoded.user,
},
};
} catch {
throw new Error("Authorization failed.");
}
},
});
server.listen();And this is the relevant client code for the tiptap editor:
[...]
const config = {};
if (this.options.collaboration.server && this.options.collaboration.document) {
// Random color, see: https://css-tricks.com/snippets/javascript/random-hex-color/
const random_color = "#" + ((Math.random() * 0xffffff) << 0).toString(16);
// Information about the current user
const user_name = this.options.collaboration.user || random_color;
const user_color = this.options.collaboration.color || random_color;
// Set up the Hocuspocus WebSocket provider
const HocuspocusProvider = (await import("@hocuspocus/provider")).HocuspocusProvider; // prettier-ignore
const YDoc = (await import("yjs")).Doc;
const y_doc = new YDoc();
const provider = new HocuspocusProvider({
url: this.options.collaboration.server,
name: this.options.collaboration.document,
document: y_doc,
token: this.options.collaboration["authentication-token"],
onAwarenessUpdate: ({ states }) => {
collaboration_states = states;
},
});
provider.setAwarenessField("user", {
name: user_name,
color: user_color,
document_name: this.options.collaboration.document,
});
// Wait for user being authenticated
const authenticated = () =>
new Promise((resolve) =>
provider.on("authenticated", resolve, { once: true })
);
await authenticated();
// Wait for document being synced
const synced = () =>
new Promise((resolve) => provider.on("synced", resolve, { once: true }));
await synced();
// Attempt 1: Only if the y_doc is the same as the document on the provider object,
// which I believe should be returned from the hocuspocus server.
// The problem here is, that also for the second connection with another browser
// returns true for the following if clause.
if (y_doc === provider.document) {
// Initialize the tiptap editor later with some initial content.
config["content"] = getText();
}
// Attempt 2: Only for the first connecting user.
// The problem here is that I apparently do not reliably get the number of connected users.
// With two browsers connecting, one time I get 0 users, one time 1 user, another time 2.
const connected_users = [...provider.awareness.states.values()].map(
(it) => it.user
);
if (connected_users.length === 1) {
// Initialize the tiptap editor later with some initial content.
config["content"] = getText();
}
// Load tiptap collaboration extensions
// Setup collaboration cursor, etc..
[...]
}
// Setup tiptap, etc.Can you provide a CodeSandbox?
No response
What did you expect to happen?
Maybe I should just hand the document loading to the hocuspocus server.
But our attempt here has the benefit of not needing to load any client extensions,
which the hocuspocus server potentially doesn't know anything about.
Is there a better API of some hocuspocus provider callbacks or events to get notified if a call to new HocuspocusProvider initially created the document or to get a relyable number of conected users?
Anything to add? (optional)
Sorry for the bug description not really fitting the bug report questions.
Say "nay" and I will update and try to provide a minimal running sandbox.
This is the PR where I develop the collaboration feature: Patternslib/pat-tiptap#6
We are sponsoring tiptap/hocuspocus (pilz, Syslab.com) - I'm part of the Syslab.com team and implementing the tiptap integration.
Did you update your dependencies?
- Yes, I’ve updated my dependencies to use the latest version of all packages.
Are you sponsoring us?
- Yes, I’m a sponsor. 💖 (pilz, Syslab.com)