|
|
|
@ -133,6 +133,8 @@ import {
|
|
|
|
|
saveUsernameToLocalStorage,
|
|
|
|
|
} from "../data/localStorage";
|
|
|
|
|
|
|
|
|
|
import throttle from "lodash.throttle";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param func handler taking at most single parameter (event).
|
|
|
|
|
*/
|
|
|
|
@ -163,11 +165,14 @@ const gesture: Gesture = {
|
|
|
|
|
initialScale: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const SYNC_FULL_SCENE_INTERVAL_MS = 20000;
|
|
|
|
|
|
|
|
|
|
class App extends React.Component<any, AppState> {
|
|
|
|
|
canvas: HTMLCanvasElement | null = null;
|
|
|
|
|
rc: RoughCanvas | null = null;
|
|
|
|
|
portal: Portal = new Portal(this);
|
|
|
|
|
lastBroadcastedOrReceivedSceneVersion: number = -1;
|
|
|
|
|
broadcastedElementVersions: Map<string, number> = new Map();
|
|
|
|
|
removeSceneCallback: SceneStateCallbackRemover | null = null;
|
|
|
|
|
|
|
|
|
|
actionManager: ActionManager;
|
|
|
|
@ -474,6 +479,10 @@ class App extends React.Component<any, AppState> {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
queueBroadcastAllElements = throttle(() => {
|
|
|
|
|
this.broadcastScene(SCENE.UPDATE, /* syncAll */ true);
|
|
|
|
|
}, SYNC_FULL_SCENE_INTERVAL_MS);
|
|
|
|
|
|
|
|
|
|
componentDidUpdate() {
|
|
|
|
|
if (this.state.isCollaborating && !this.portal.socket) {
|
|
|
|
|
this.initializeSocketClient({ showLoadingState: true });
|
|
|
|
@ -555,7 +564,8 @@ class App extends React.Component<any, AppState> {
|
|
|
|
|
getDrawingVersion(globalSceneState.getElementsIncludingDeleted()) >
|
|
|
|
|
this.lastBroadcastedOrReceivedSceneVersion
|
|
|
|
|
) {
|
|
|
|
|
this.broadcastScene(SCENE.UPDATE);
|
|
|
|
|
this.broadcastScene(SCENE.UPDATE, /* syncAll */ false);
|
|
|
|
|
this.queueBroadcastAllElements();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
history.record(this.state, globalSceneState.getElementsIncludingDeleted());
|
|
|
|
@ -1020,19 +1030,43 @@ class App extends React.Component<any, AppState> {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// maybe should move to Portal
|
|
|
|
|
broadcastScene = (sceneType: SCENE.INIT | SCENE.UPDATE) => {
|
|
|
|
|
broadcastScene = (sceneType: SCENE.INIT | SCENE.UPDATE, syncAll: boolean) => {
|
|
|
|
|
if (sceneType === SCENE.INIT && !syncAll) {
|
|
|
|
|
throw new Error("syncAll must be true when sending SCENE.INIT");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let syncableElements = getSyncableElements(
|
|
|
|
|
globalSceneState.getElementsIncludingDeleted(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!syncAll) {
|
|
|
|
|
// sync out only the elements we think we need to to save bandwidth.
|
|
|
|
|
// periodically we'll resync the whole thing to make sure no one diverges
|
|
|
|
|
// due to a dropped message (server goes down etc).
|
|
|
|
|
syncableElements = syncableElements.filter(
|
|
|
|
|
(syncableElement) =>
|
|
|
|
|
!this.broadcastedElementVersions.has(syncableElement.id) ||
|
|
|
|
|
syncableElement.version >
|
|
|
|
|
this.broadcastedElementVersions.get(syncableElement.id)!,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data: SocketUpdateDataSource[typeof sceneType] = {
|
|
|
|
|
type: sceneType,
|
|
|
|
|
payload: {
|
|
|
|
|
elements: getSyncableElements(
|
|
|
|
|
globalSceneState.getElementsIncludingDeleted(),
|
|
|
|
|
),
|
|
|
|
|
elements: syncableElements,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
this.lastBroadcastedOrReceivedSceneVersion = Math.max(
|
|
|
|
|
this.lastBroadcastedOrReceivedSceneVersion,
|
|
|
|
|
getDrawingVersion(globalSceneState.getElementsIncludingDeleted()),
|
|
|
|
|
);
|
|
|
|
|
for (const syncableElement of syncableElements) {
|
|
|
|
|
this.broadcastedElementVersions.set(
|
|
|
|
|
syncableElement.id,
|
|
|
|
|
syncableElement.version,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return this.portal._broadcastSocketData(data as SocketUpdateData);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|