Avoid broadcasting what was just received (#1116)

Fixes #1115

The issue is that replaceAllElements calls a render synchronously, preventing lastBroadcastedOrReceivedSceneVersion from being set correctly.

I tried using batchUpdate but it only takes a single argument ( c5d2fc7127/packages/react-reconciler/src/ReactFiberWorkLoop.js (L1088) ) whereas the callback takes two.

Test Plan:
- Add a console.log before `this.broadcastScene("SCENE_UPDATE");` in App.tsx
- Connect a bunch of clients
- Have one move a shape
- Make sure that this client has the console logged
- Make sure the other clients don't have it
This commit is contained in:
Christopher Chedeau 2020-03-28 21:25:40 -07:00 committed by GitHub
parent 763735ac84
commit a7bd21ccf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -731,58 +731,63 @@ export class App extends React.Component<any, AppState> {
); );
// Reconcile // Reconcile
globalSceneState.replaceAllElements( const newElements = restoredState.elements
restoredState.elements .reduce((elements, element) => {
.reduce((elements, element) => { // if the remote element references one that's currently
// if the remote element references one that's currently // edited on local, skip it (it'll be added in the next
// edited on local, skip it (it'll be added in the next // step)
// step) if (
if ( element.id === this.state.editingElement?.id ||
element.id === this.state.editingElement?.id || element.id === this.state.resizingElement?.id ||
element.id === this.state.resizingElement?.id || element.id === this.state.draggingElement?.id
element.id === this.state.draggingElement?.id ) {
) { return elements;
return elements; }
}
if (
localElementMap.hasOwnProperty(element.id) &&
localElementMap[element.id].version > element.version
) {
elements.push(localElementMap[element.id]);
delete localElementMap[element.id];
} else if (
localElementMap.hasOwnProperty(element.id) &&
localElementMap[element.id].version === element.version &&
localElementMap[element.id].versionNonce !==
element.versionNonce
) {
// resolve conflicting edits deterministically by taking the one with the lowest versionNonce
if ( if (
localElementMap.hasOwnProperty(element.id) && localElementMap[element.id].versionNonce <
localElementMap[element.id].version > element.version element.versionNonce
) { ) {
elements.push(localElementMap[element.id]); elements.push(localElementMap[element.id]);
delete localElementMap[element.id];
} else if (
localElementMap.hasOwnProperty(element.id) &&
localElementMap[element.id].version === element.version &&
localElementMap[element.id].versionNonce !==
element.versionNonce
) {
// resolve conflicting edits deterministically by taking the one with the lowest versionNonce
if (
localElementMap[element.id].versionNonce <
element.versionNonce
) {
elements.push(localElementMap[element.id]);
} else {
// it should be highly unlikely that the two versionNonces are the same. if we are
// really worried about this, we can replace the versionNonce with the socket id.
elements.push(element);
}
delete localElementMap[element.id];
} else { } else {
// it should be highly unlikely that the two versionNonces are the same. if we are
// really worried about this, we can replace the versionNonce with the socket id.
elements.push(element); elements.push(element);
delete localElementMap[element.id];
} }
delete localElementMap[element.id];
} else {
elements.push(element);
delete localElementMap[element.id];
}
return elements; return elements;
}, [] as Mutable<typeof restoredState.elements>) }, [] as Mutable<typeof restoredState.elements>)
// add local elements that weren't deleted or on remote // add local elements that weren't deleted or on remote
.concat(...Object.values(localElementMap)), .concat(...Object.values(localElementMap));
// Avoid broadcasting to the rest of the collaborators the scene
// we just received!
// Note: this needs to be set before replaceAllElements as it
// syncronously calls render.
this.lastBroadcastedOrReceivedSceneVersion = getDrawingVersion(
newElements,
); );
globalSceneState.replaceAllElements(newElements);
} }
this.lastBroadcastedOrReceivedSceneVersion = getDrawingVersion(
globalSceneState.getAllElements(),
);
// We haven't yet implemented multiplayer undo functionality, so we clear the undo stack // We haven't yet implemented multiplayer undo functionality, so we clear the undo stack
// when we receive any messages from another peer. This UX can be pretty rough -- if you // when we receive any messages from another peer. This UX can be pretty rough -- if you