Begin extracting collaboration code to Portal (#1306)
This commit is contained in:
parent
ed378170b7
commit
9a1af38c97
@ -142,13 +142,33 @@ const gesture: Gesture = {
|
|||||||
initialScale: null,
|
initialScale: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class App extends React.Component<any, AppState> {
|
class Portal {
|
||||||
canvas: HTMLCanvasElement | null = null;
|
|
||||||
rc: RoughCanvas | null = null;
|
|
||||||
socket: SocketIOClient.Socket | null = null;
|
socket: SocketIOClient.Socket | null = null;
|
||||||
socketInitialized: boolean = false; // we don't want the socket to emit any updates until it is fully initalized
|
socketInitialized: boolean = false; // we don't want the socket to emit any updates until it is fully initalized
|
||||||
roomID: string | null = null;
|
roomID: string | null = null;
|
||||||
roomKey: string | null = null;
|
roomKey: string | null = null;
|
||||||
|
|
||||||
|
open(socket: SocketIOClient.Socket, id: string, key: string) {
|
||||||
|
this.socket = socket;
|
||||||
|
this.roomID = id;
|
||||||
|
this.roomKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
if (!this.socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.socket.close();
|
||||||
|
this.socket = null;
|
||||||
|
this.roomID = null;
|
||||||
|
this.roomKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class App extends React.Component<any, AppState> {
|
||||||
|
canvas: HTMLCanvasElement | null = null;
|
||||||
|
rc: RoughCanvas | null = null;
|
||||||
|
room: Portal = new Portal();
|
||||||
lastBroadcastedOrReceivedSceneVersion: number = -1;
|
lastBroadcastedOrReceivedSceneVersion: number = -1;
|
||||||
removeSceneCallback: SceneStateCallbackRemover | null = null;
|
removeSceneCallback: SceneStateCallbackRemover | null = null;
|
||||||
|
|
||||||
@ -193,8 +213,8 @@ export class App extends React.Component<any, AppState> {
|
|||||||
return !element.isDeleted;
|
return !element.isDeleted;
|
||||||
})}
|
})}
|
||||||
setElements={this.setElements}
|
setElements={this.setElements}
|
||||||
onRoomCreate={this.createRoom}
|
onRoomCreate={this.openPortal}
|
||||||
onRoomDestroy={this.destroyRoom}
|
onRoomDestroy={this.closePortal}
|
||||||
onLockToggle={this.toggleLock}
|
onLockToggle={this.toggleLock}
|
||||||
/>
|
/>
|
||||||
<main>
|
<main>
|
||||||
@ -428,7 +448,7 @@ export class App extends React.Component<any, AppState> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.state.isCollaborating && !this.socket) {
|
if (this.state.isCollaborating && !this.room.socket) {
|
||||||
this.initializeSocketClient({ showLoadingState: true });
|
this.initializeSocketClient({ showLoadingState: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,7 +720,7 @@ export class App extends React.Component<any, AppState> {
|
|||||||
gesture.pointers.delete(event.pointerId);
|
gesture.pointers.delete(event.pointerId);
|
||||||
};
|
};
|
||||||
|
|
||||||
createRoom = async () => {
|
openPortal = async () => {
|
||||||
window.history.pushState(
|
window.history.pushState(
|
||||||
{},
|
{},
|
||||||
"Excalidraw",
|
"Excalidraw",
|
||||||
@ -709,7 +729,7 @@ export class App extends React.Component<any, AppState> {
|
|||||||
this.initializeSocketClient({ showLoadingState: false });
|
this.initializeSocketClient({ showLoadingState: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
destroyRoom = () => {
|
closePortal = () => {
|
||||||
window.history.pushState({}, "Excalidraw", window.location.origin);
|
window.history.pushState({}, "Excalidraw", window.location.origin);
|
||||||
this.destroySocketClient();
|
this.destroySocketClient();
|
||||||
};
|
};
|
||||||
@ -728,22 +748,17 @@ export class App extends React.Component<any, AppState> {
|
|||||||
isCollaborating: false,
|
isCollaborating: false,
|
||||||
collaborators: new Map(),
|
collaborators: new Map(),
|
||||||
});
|
});
|
||||||
if (this.socket) {
|
this.room.close();
|
||||||
this.socket.close();
|
|
||||||
this.socket = null;
|
|
||||||
this.roomID = null;
|
|
||||||
this.roomKey = null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private initializeSocketClient = (opts: { showLoadingState: boolean }) => {
|
private initializeSocketClient = (opts: { showLoadingState: boolean }) => {
|
||||||
if (this.socket) {
|
if (this.room.socket) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const roomMatch = getCollaborationLinkData(window.location.href);
|
const roomMatch = getCollaborationLinkData(window.location.href);
|
||||||
if (roomMatch) {
|
if (roomMatch) {
|
||||||
const initialize = () => {
|
const initialize = () => {
|
||||||
this.socketInitialized = true;
|
this.room.socketInitialized = true;
|
||||||
clearTimeout(initializationTimer);
|
clearTimeout(initializationTimer);
|
||||||
if (this.state.isLoading && !this.unmounted) {
|
if (this.state.isLoading && !this.unmounted) {
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
@ -849,26 +864,26 @@ export class App extends React.Component<any, AppState> {
|
|||||||
// undo, a user makes a change, and then try to redo, your element(s) will be lost. However,
|
// undo, a user makes a change, and then try to redo, your element(s) will be lost. However,
|
||||||
// right now we think this is the right tradeoff.
|
// right now we think this is the right tradeoff.
|
||||||
history.clear();
|
history.clear();
|
||||||
if (this.socketInitialized === false) {
|
if (this.room.socketInitialized === false) {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.socket = socketIOClient(SOCKET_SERVER);
|
this.room.open(socketIOClient(SOCKET_SERVER), roomMatch[1], roomMatch[2]);
|
||||||
this.roomID = roomMatch[1];
|
|
||||||
this.roomKey = roomMatch[2];
|
this.room.socket!.on("init-room", () => {
|
||||||
this.socket.on("init-room", () => {
|
this.room.socket &&
|
||||||
this.socket && this.socket.emit("join-room", this.roomID);
|
this.room.socket.emit("join-room", this.room.roomID);
|
||||||
});
|
});
|
||||||
this.socket.on(
|
this.room.socket!.on(
|
||||||
"client-broadcast",
|
"client-broadcast",
|
||||||
async (encryptedData: ArrayBuffer, iv: Uint8Array) => {
|
async (encryptedData: ArrayBuffer, iv: Uint8Array) => {
|
||||||
if (!this.roomKey) {
|
if (!this.room.roomKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const decryptedData = await decryptAESGEM(
|
const decryptedData = await decryptAESGEM(
|
||||||
encryptedData,
|
encryptedData,
|
||||||
this.roomKey,
|
this.room.roomKey,
|
||||||
iv,
|
iv,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -876,7 +891,7 @@ export class App extends React.Component<any, AppState> {
|
|||||||
case "INVALID_RESPONSE":
|
case "INVALID_RESPONSE":
|
||||||
return;
|
return;
|
||||||
case "SCENE_INIT": {
|
case "SCENE_INIT": {
|
||||||
if (!this.socketInitialized) {
|
if (!this.room.socketInitialized) {
|
||||||
updateScene(decryptedData, { scrollToContent: true });
|
updateScene(decryptedData, { scrollToContent: true });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -909,13 +924,13 @@ export class App extends React.Component<any, AppState> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.socket.on("first-in-room", () => {
|
this.room.socket!.on("first-in-room", () => {
|
||||||
if (this.socket) {
|
if (this.room.socket) {
|
||||||
this.socket.off("first-in-room");
|
this.room.socket.off("first-in-room");
|
||||||
}
|
}
|
||||||
initialize();
|
initialize();
|
||||||
});
|
});
|
||||||
this.socket.on("room-user-change", (clients: string[]) => {
|
this.room.socket!.on("room-user-change", (clients: string[]) => {
|
||||||
this.setState((state) => {
|
this.setState((state) => {
|
||||||
const collaborators: typeof state.collaborators = new Map();
|
const collaborators: typeof state.collaborators = new Map();
|
||||||
for (const socketID of clients) {
|
for (const socketID of clients) {
|
||||||
@ -931,7 +946,7 @@ export class App extends React.Component<any, AppState> {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.socket.on("new-user", async (_socketID: string) => {
|
this.room.socket!.on("new-user", async (_socketID: string) => {
|
||||||
this.broadcastScene("SCENE_INIT");
|
this.broadcastScene("SCENE_INIT");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -946,11 +961,11 @@ export class App extends React.Component<any, AppState> {
|
|||||||
pointerCoords: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointerCoords"];
|
pointerCoords: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointerCoords"];
|
||||||
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
||||||
}) => {
|
}) => {
|
||||||
if (this.socket?.id) {
|
if (this.room.socket?.id) {
|
||||||
const data: SocketUpdateDataSource["MOUSE_LOCATION"] = {
|
const data: SocketUpdateDataSource["MOUSE_LOCATION"] = {
|
||||||
type: "MOUSE_LOCATION",
|
type: "MOUSE_LOCATION",
|
||||||
payload: {
|
payload: {
|
||||||
socketID: this.socket.id,
|
socketID: this.room.socket.id,
|
||||||
pointerCoords: payload.pointerCoords,
|
pointerCoords: payload.pointerCoords,
|
||||||
button: payload.button || "up",
|
button: payload.button || "up",
|
||||||
selectedElementIds: this.state.selectedElementIds,
|
selectedElementIds: this.state.selectedElementIds,
|
||||||
@ -985,13 +1000,18 @@ export class App extends React.Component<any, AppState> {
|
|||||||
_brand: "socketUpdateData";
|
_brand: "socketUpdateData";
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (this.socketInitialized && this.socket && this.roomID && this.roomKey) {
|
if (
|
||||||
|
this.room.socketInitialized &&
|
||||||
|
this.room.socket &&
|
||||||
|
this.room.roomID &&
|
||||||
|
this.room.roomKey
|
||||||
|
) {
|
||||||
const json = JSON.stringify(data);
|
const json = JSON.stringify(data);
|
||||||
const encoded = new TextEncoder().encode(json);
|
const encoded = new TextEncoder().encode(json);
|
||||||
const encrypted = await encryptAESGEM(encoded, this.roomKey);
|
const encrypted = await encryptAESGEM(encoded, this.room.roomKey);
|
||||||
this.socket.emit(
|
this.room.socket.emit(
|
||||||
"server-broadcast",
|
"server-broadcast",
|
||||||
this.roomID,
|
this.room.roomID,
|
||||||
encrypted.data,
|
encrypted.data,
|
||||||
encrypted.iv,
|
encrypted.iv,
|
||||||
);
|
);
|
||||||
@ -2490,7 +2510,7 @@ export class App extends React.Component<any, AppState> {
|
|||||||
// sometimes the pointer goes off screen
|
// sometimes the pointer goes off screen
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.socket &&
|
this.room.socket &&
|
||||||
this.broadcastMouseLocation({
|
this.broadcastMouseLocation({
|
||||||
pointerCoords,
|
pointerCoords,
|
||||||
button,
|
button,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user