89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
|
import { nanoid } from "https://esm.sh/nanoid@4.0.0";
|
||
|
import type { Editor } from "./editor.tsx";
|
||
|
|
||
|
const collabPingInterval = 2500;
|
||
|
|
||
|
export class CollabManager {
|
||
|
clientId = nanoid();
|
||
|
localCollabServer: string;
|
||
|
|
||
|
constructor(private editor: Editor) {
|
||
|
this.localCollabServer = location.protocol === "http:"
|
||
|
? `ws://${location.host}/.ws-collab`
|
||
|
: `wss://${location.host}/.ws-collab`;
|
||
|
editor.eventHook.addLocalListener(
|
||
|
"editor:pageLoaded",
|
||
|
(pageName, previousPage) => {
|
||
|
console.log("Page loaded", pageName, previousPage);
|
||
|
this.updatePresence(pageName, previousPage).catch(console.error);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
start() {
|
||
|
setInterval(() => {
|
||
|
this.updatePresence(this.editor.currentPage!).catch(console.error);
|
||
|
}, collabPingInterval);
|
||
|
}
|
||
|
|
||
|
async updatePresence(currentPage?: string, previousPage?: string) {
|
||
|
try {
|
||
|
const resp = await this.editor.remoteSpacePrimitives.authenticatedFetch(
|
||
|
this.editor.remoteSpacePrimitives.url,
|
||
|
{
|
||
|
method: "POST",
|
||
|
headers: {
|
||
|
"Content-Type": "application/json",
|
||
|
},
|
||
|
body: JSON.stringify({
|
||
|
operation: "presence",
|
||
|
clientId: this.clientId,
|
||
|
previousPage,
|
||
|
currentPage,
|
||
|
}),
|
||
|
keepalive: true, // important for beforeunload event
|
||
|
},
|
||
|
);
|
||
|
const { collabId } = await resp.json();
|
||
|
|
||
|
if (this.editor.collabState && !this.editor.collabState.isLocalCollab) {
|
||
|
// We're in a remote collab mode, don't do anything
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// console.log("Collab ID", collabId);
|
||
|
const previousCollabId = this.editor.collabState?.token.split("/")[0];
|
||
|
if (!collabId && this.editor.collabState) {
|
||
|
// Stop collab
|
||
|
console.log("Stopping collab");
|
||
|
if (this.editor.collabState.path === `${currentPage}.md`) {
|
||
|
this.editor.flashNotification(
|
||
|
"Other users have left this page, switched back to single-user mode.",
|
||
|
);
|
||
|
}
|
||
|
this.editor.stopCollab();
|
||
|
} else if (collabId && collabId !== previousCollabId) {
|
||
|
// Start collab
|
||
|
console.log("Starting collab");
|
||
|
this.editor.flashNotification(
|
||
|
"Opening page in multi-user mode.",
|
||
|
);
|
||
|
this.editor.startCollab(
|
||
|
this.localCollabServer,
|
||
|
`${collabId}/${currentPage}.md`,
|
||
|
this.editor.getUsername(),
|
||
|
true,
|
||
|
);
|
||
|
}
|
||
|
} catch (e: any) {
|
||
|
// console.error("Ping error", e);
|
||
|
if (
|
||
|
e.message.toLowerCase().includes("failed") && this.editor.collabState
|
||
|
) {
|
||
|
console.log("Offline, stopping collab");
|
||
|
this.editor.stopCollab();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|