Custom room, various fixes

This commit is contained in:
Marcel Mraz 2024-12-31 12:49:23 +01:00
parent e72d83541a
commit c57249481e
No known key found for this signature in database
GPG key ID: 4EBD6E62DC830CD2
3 changed files with 44 additions and 38 deletions

View file

@ -856,7 +856,7 @@ const ExcalidrawWrapper = () => {
excalidrawAPI?.updateScene({ excalidrawAPI?.updateScene({
appState: { appState: {
...excalidrawAPI?.getAppState(), ...excalidrawAPI?.getAppState(),
viewModeEnabled: value !== acknowledgedIncrements.length, viewModeEnabled: value !== -1,
}, },
elements: Array.from(elements.values()), elements: Array.from(elements.values()),
storeAction: StoreAction.UPDATE, storeAction: StoreAction.UPDATE,
@ -865,7 +865,7 @@ const ExcalidrawWrapper = () => {
currentVersion.current = value; currentVersion.current = value;
}, 0); }, 0);
const latestVersion = acknowledgedIncrements.length - 1; const latestVersion = acknowledgedIncrements.length;
return ( return (
<div <div
@ -885,24 +885,24 @@ const ExcalidrawWrapper = () => {
step={1} step={1}
min={0} min={0}
max={latestVersion} max={latestVersion}
value={ value={nextVersion === -1 ? latestVersion : nextVersion}
nextVersion === -1 || nextVersion === latestVersion
? latestVersion
: nextVersion
}
onChange={(value) => { onChange={(value) => {
let nextValue: number;
// CFDO: should be disabled when offline! (later we could have speculative changes in the versioning log as well) // CFDO: should be disabled when offline! (later we could have speculative changes in the versioning log as well)
// CFDO: in safari the whole canvas gets selected when dragging // CFDO: in safari the whole canvas gets selected when dragging
if (value !== acknowledgedIncrements.length) { if (value !== acknowledgedIncrements.length) {
// don't listen to updates in the detached mode // don't listen to updates in the detached mode
syncAPI?.disconnect(); syncAPI?.disconnect();
nextValue = value as number;
} else { } else {
// reconnect once we're back to the latest version // reconnect once we're back to the latest version
syncAPI?.connect(); syncAPI?.connect();
nextValue = -1;
} }
setNextVersion(value as number); setNextVersion(nextValue);
debouncedTimeTravel(value as number); debouncedTimeTravel(nextValue);
}} }}
/> />
<Excalidraw <Excalidraw

View file

@ -1,6 +1,9 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import throttle from "lodash.throttle"; import throttle from "lodash.throttle";
import ReconnectingWebSocket, { type Event } from "reconnecting-websocket"; import ReconnectingWebSocket, {
type Event,
type CloseEvent,
} from "reconnecting-websocket";
import { Utils } from "./utils"; import { Utils } from "./utils";
import { import {
SyncQueue, SyncQueue,
@ -76,7 +79,7 @@ class SocketClient {
window.addEventListener("online", this.onOnline); window.addEventListener("online", this.onOnline);
window.addEventListener("offline", this.onOffline); window.addEventListener("offline", this.onOffline);
console.debug("Connecting to the sync server..."); console.debug(`Connecting to the room "${this.roomId}"...`);
this.socket = new ReconnectingWebSocket( this.socket = new ReconnectingWebSocket(
`${this.host}/connect?roomId=${this.roomId}`, `${this.host}/connect?roomId=${this.roomId}`,
[], [],
@ -95,16 +98,15 @@ class SocketClient {
); );
this.socket.addEventListener("message", this.onMessage); this.socket.addEventListener("message", this.onMessage);
this.socket.addEventListener("open", this.onOpen); this.socket.addEventListener("open", this.onOpen);
this.socket.addEventListener("close", this.onClose);
this.socket.addEventListener("error", this.onError);
}, },
1000, 1000,
{ leading: true, trailing: false }, { leading: true, trailing: false },
); );
// CFDO: the connections seem to keep hanging for some reason // CFDO: the connections seem to keep hanging for some reason
public disconnect( public disconnect() {
code: number = SocketClient.NORMAL_CLOSURE_CODE,
reason?: string,
) {
if (this.isDisconnected) { if (this.isDisconnected) {
return; return;
} }
@ -113,25 +115,13 @@ class SocketClient {
window.removeEventListener("online", this.onOnline); window.removeEventListener("online", this.onOnline);
window.removeEventListener("offline", this.onOffline); window.removeEventListener("offline", this.onOffline);
console.debug(
`Disconnecting from the sync server with code "${code}"${
reason ? ` and reason "${reason}".` : "."
}`,
);
this.socket?.removeEventListener("message", this.onMessage); this.socket?.removeEventListener("message", this.onMessage);
this.socket?.removeEventListener("open", this.onOpen); this.socket?.removeEventListener("open", this.onOpen);
this.socket?.removeEventListener("close", this.onClose);
this.socket?.removeEventListener("error", this.onError);
this.socket?.close();
let remappedCode = code; console.debug(`Disconnected from the room "${this.roomId}".`);
switch (code) {
case 1009: {
// remapping the code, otherwise getting "The close code must be either 1000, or between 3000 and 4999. 1009 is neither."
remappedCode = SocketClient.MESSAGE_IS_TOO_LARGE_ERROR_CODE;
break;
}
}
this.socket?.close(remappedCode, reason);
} finally { } finally {
this.socket = null; this.socket = null;
} }
@ -186,9 +176,21 @@ class SocketClient {
}; };
private onOpen = (event: Event) => { private onOpen = (event: Event) => {
console.debug(`Connection to the room "${this.roomId}" opened.`);
this.isOffline = false; this.isOffline = false;
this.handlers.onOpen(event); this.handlers.onOpen(event);
}; };
private onClose = (event: CloseEvent) => {
console.debug(`Connection to the room "${this.roomId}" closed.`, event);
};
private onError = (event: Event) => {
console.debug(
`Connection to the room "${this.roomId}" returned an error.`,
event,
);
};
} }
interface AcknowledgedIncrement { interface AcknowledgedIncrement {
@ -258,9 +260,12 @@ export class SyncClient {
repository: IncrementsRepository & MetadataRepository, repository: IncrementsRepository & MetadataRepository,
) { ) {
const queue = await SyncQueue.create(repository); const queue = await SyncQueue.create(repository);
// CFDO: temporary for custom roomId (though E+ will be similar)
const roomId = window.location.pathname.split("/").at(-1);
return new SyncClient(api, repository, queue, { return new SyncClient(api, repository, queue, {
host: SyncClient.HOST_URL, host: SyncClient.HOST_URL,
roomId: SyncClient.ROOM_ID, roomId: roomId ?? SyncClient.ROOM_ID,
// CFDO: temporary, so that all increments are loaded and applied on init // CFDO: temporary, so that all increments are loaded and applied on init
lastAcknowledgedVersion: 0, lastAcknowledgedVersion: 0,
}); });

View file

@ -64,6 +64,12 @@ const checkpoint = (name: string) => {
...strippedAppState ...strippedAppState
} = h.state; } = h.state;
expect(strippedAppState).toMatchSnapshot(`[${name}] appState`); expect(strippedAppState).toMatchSnapshot(`[${name}] appState`);
expect(h.elements.length).toMatchSnapshot(`[${name}] number of elements`);
h.elements
.map(({ seed, versionNonce, ...strippedElement }) => strippedElement)
.forEach((element, i) =>
expect(element).toMatchSnapshot(`[${name}] element ${i}`),
);
const stripSeed = (deltas: Record<string, { deleted: any; inserted: any }>) => const stripSeed = (deltas: Record<string, { deleted: any; inserted: any }>) =>
Object.entries(deltas).reduce((acc, curr) => { Object.entries(deltas).reduce((acc, curr) => {
@ -92,6 +98,7 @@ const checkpoint = (name: string) => {
}, },
})), })),
).toMatchSnapshot(`[${name}] undo stack`); ).toMatchSnapshot(`[${name}] undo stack`);
expect( expect(
h.history.redoStack.map((x) => ({ h.history.redoStack.map((x) => ({
...x, ...x,
@ -103,12 +110,6 @@ const checkpoint = (name: string) => {
}, },
})), })),
).toMatchSnapshot(`[${name}] redo stack`); ).toMatchSnapshot(`[${name}] redo stack`);
expect(h.elements.length).toMatchSnapshot(`[${name}] number of elements`);
h.elements
.map(({ seed, versionNonce, ...strippedElement }) => strippedElement)
.forEach((element, i) =>
expect(element).toMatchSnapshot(`[${name}] element ${i}`),
);
}; };
const renderStaticScene = vi.spyOn(StaticScene, "renderStaticScene"); const renderStaticScene = vi.spyOn(StaticScene, "renderStaticScene");