|
@@ -3,7 +3,6 @@ import React from "react";
|
3
|
3
|
import rough from "roughjs/bin/rough";
|
4
|
4
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
5
|
5
|
import { simplify, Point } from "points-on-curve";
|
6
|
|
-import { SocketUpdateData } from "../types";
|
7
|
6
|
|
8
|
7
|
import {
|
9
|
8
|
newElement,
|
|
@@ -279,9 +278,8 @@ export type ExcalidrawImperativeAPI =
|
279
|
278
|
class App extends React.Component<ExcalidrawProps, AppState> {
|
280
|
279
|
canvas: HTMLCanvasElement | null = null;
|
281
|
280
|
rc: RoughCanvas | null = null;
|
282
|
|
- portal: Portal = new Portal(this);
|
283
|
|
- lastBroadcastedOrReceivedSceneVersion: number = -1;
|
284
|
|
- broadcastedElementVersions: Map<string, number> = new Map();
|
|
281
|
+ portal: Portal;
|
|
282
|
+ private lastBroadcastedOrReceivedSceneVersion: number = -1;
|
285
|
283
|
unmounted: boolean = false;
|
286
|
284
|
actionManager: ActionManager;
|
287
|
285
|
private excalidrawRef: any;
|
|
@@ -312,6 +310,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
312
|
310
|
};
|
313
|
311
|
}
|
314
|
312
|
this.scene = new Scene();
|
|
313
|
+ this.portal = new Portal(this);
|
|
314
|
+
|
315
|
315
|
this.excalidrawRef = React.createRef();
|
316
|
316
|
this.actionManager = new ActionManager(
|
317
|
317
|
this.syncActionResult,
|
|
@@ -396,6 +396,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
396
|
396
|
);
|
397
|
397
|
}
|
398
|
398
|
|
|
399
|
+ public setLastBroadcastedOrReceivedSceneVersion = (version: number) => {
|
|
400
|
+ this.lastBroadcastedOrReceivedSceneVersion = version;
|
|
401
|
+ };
|
|
402
|
+
|
|
403
|
+ public getLastBroadcastedOrReceivedSceneVersion = () => {
|
|
404
|
+ return this.lastBroadcastedOrReceivedSceneVersion;
|
|
405
|
+ };
|
|
406
|
+
|
|
407
|
+ public getSceneElementsIncludingDeleted = () => {
|
|
408
|
+ return this.scene.getElementsIncludingDeleted();
|
|
409
|
+ };
|
|
410
|
+
|
399
|
411
|
private syncActionResult = withBatchedUpdates(
|
400
|
412
|
(actionResult: ActionResult) => {
|
401
|
413
|
if (this.unmounted || actionResult === false) {
|
|
@@ -823,7 +835,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
823
|
835
|
});
|
824
|
836
|
|
825
|
837
|
queueBroadcastAllElements = throttle(() => {
|
826
|
|
- this.broadcastScene(SCENE.UPDATE, /* syncAll */ true);
|
|
838
|
+ this.portal.broadcastScene(SCENE.UPDATE, /* syncAll */ true);
|
827
|
839
|
}, SYNC_FULL_SCENE_INTERVAL_MS);
|
828
|
840
|
|
829
|
841
|
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
|
@@ -949,7 +961,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
949
|
961
|
getSceneVersion(this.scene.getElementsIncludingDeleted()) >
|
950
|
962
|
this.lastBroadcastedOrReceivedSceneVersion
|
951
|
963
|
) {
|
952
|
|
- this.broadcastScene(SCENE.UPDATE, /* syncAll */ false);
|
|
964
|
+ this.portal.broadcastScene(SCENE.UPDATE, /* syncAll */ false);
|
953
|
965
|
this.queueBroadcastAllElements();
|
954
|
966
|
}
|
955
|
967
|
|
|
@@ -1363,7 +1375,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
1363
|
1375
|
// we just received!
|
1364
|
1376
|
// Note: this needs to be set before replaceAllElements as it
|
1365
|
1377
|
// syncronously calls render.
|
1366
|
|
- this.lastBroadcastedOrReceivedSceneVersion = getSceneVersion(newElements);
|
|
1378
|
+
|
|
1379
|
+ this.setLastBroadcastedOrReceivedSceneVersion(
|
|
1380
|
+ getSceneVersion(newElements),
|
|
1381
|
+ );
|
1367
|
1382
|
|
1368
|
1383
|
this.scene.replaceAllElements(newElements);
|
1369
|
1384
|
}
|
|
@@ -1511,28 +1526,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
1511
|
1526
|
});
|
1512
|
1527
|
}
|
1513
|
1528
|
|
1514
|
|
- private broadcastMouseLocation = (payload: {
|
1515
|
|
- pointer: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointer"];
|
1516
|
|
- button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
1517
|
|
- }) => {
|
1518
|
|
- if (this.portal.socket?.id) {
|
1519
|
|
- const data: SocketUpdateDataSource["MOUSE_LOCATION"] = {
|
1520
|
|
- type: "MOUSE_LOCATION",
|
1521
|
|
- payload: {
|
1522
|
|
- socketId: this.portal.socket.id,
|
1523
|
|
- pointer: payload.pointer,
|
1524
|
|
- button: payload.button || "up",
|
1525
|
|
- selectedElementIds: this.state.selectedElementIds,
|
1526
|
|
- username: this.state.username,
|
1527
|
|
- },
|
1528
|
|
- };
|
1529
|
|
- return this.portal._broadcastSocketData(
|
1530
|
|
- data as SocketUpdateData,
|
1531
|
|
- true, // volatile
|
1532
|
|
- );
|
1533
|
|
- }
|
1534
|
|
- };
|
1535
|
|
-
|
1536
|
1529
|
saveCollabRoomToFirebase = async (
|
1537
|
1530
|
syncableElements: ExcalidrawElement[] = getSyncableElements(
|
1538
|
1531
|
this.scene.getElementsIncludingDeleted(),
|
|
@@ -1545,62 +1538,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
1545
|
1538
|
}
|
1546
|
1539
|
};
|
1547
|
1540
|
|
1548
|
|
- // maybe should move to Portal
|
1549
|
|
- broadcastScene = async (
|
1550
|
|
- sceneType: SCENE.INIT | SCENE.UPDATE,
|
1551
|
|
- syncAll: boolean,
|
1552
|
|
- ) => {
|
1553
|
|
- if (sceneType === SCENE.INIT && !syncAll) {
|
1554
|
|
- throw new Error("syncAll must be true when sending SCENE.INIT");
|
1555
|
|
- }
|
1556
|
|
-
|
1557
|
|
- let syncableElements = getSyncableElements(
|
1558
|
|
- this.scene.getElementsIncludingDeleted(),
|
1559
|
|
- );
|
1560
|
|
-
|
1561
|
|
- if (!syncAll) {
|
1562
|
|
- // sync out only the elements we think we need to to save bandwidth.
|
1563
|
|
- // periodically we'll resync the whole thing to make sure no one diverges
|
1564
|
|
- // due to a dropped message (server goes down etc).
|
1565
|
|
- syncableElements = syncableElements.filter(
|
1566
|
|
- (syncableElement) =>
|
1567
|
|
- !this.broadcastedElementVersions.has(syncableElement.id) ||
|
1568
|
|
- syncableElement.version >
|
1569
|
|
- this.broadcastedElementVersions.get(syncableElement.id)!,
|
1570
|
|
- );
|
1571
|
|
- }
|
1572
|
|
-
|
1573
|
|
- const data: SocketUpdateDataSource[typeof sceneType] = {
|
1574
|
|
- type: sceneType,
|
1575
|
|
- payload: {
|
1576
|
|
- elements: syncableElements,
|
1577
|
|
- },
|
1578
|
|
- };
|
1579
|
|
- this.lastBroadcastedOrReceivedSceneVersion = Math.max(
|
1580
|
|
- this.lastBroadcastedOrReceivedSceneVersion,
|
1581
|
|
- getSceneVersion(this.scene.getElementsIncludingDeleted()),
|
1582
|
|
- );
|
1583
|
|
- for (const syncableElement of syncableElements) {
|
1584
|
|
- this.broadcastedElementVersions.set(
|
1585
|
|
- syncableElement.id,
|
1586
|
|
- syncableElement.version,
|
1587
|
|
- );
|
1588
|
|
- }
|
1589
|
|
-
|
1590
|
|
- const broadcastPromise = this.portal._broadcastSocketData(
|
1591
|
|
- data as SocketUpdateData,
|
1592
|
|
- );
|
1593
|
|
-
|
1594
|
|
- if (syncAll && this.state.isCollaborating) {
|
1595
|
|
- await Promise.all([
|
1596
|
|
- broadcastPromise,
|
1597
|
|
- this.saveCollabRoomToFirebase(syncableElements),
|
1598
|
|
- ]);
|
1599
|
|
- } else {
|
1600
|
|
- await broadcastPromise;
|
1601
|
|
- }
|
1602
|
|
- };
|
1603
|
|
-
|
1604
|
1541
|
private onSceneUpdated = () => {
|
1605
|
1542
|
this.setState({});
|
1606
|
1543
|
};
|
|
@@ -4078,7 +4015,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
4078
|
4015
|
this.portal.socket &&
|
4079
|
4016
|
// do not broadcast when more than 1 pointer since that shows flickering on the other side
|
4080
|
4017
|
gesture.pointers.size < 2 &&
|
4081
|
|
- this.broadcastMouseLocation({
|
|
4018
|
+ this.portal.broadcastMouseLocation({
|
4082
|
4019
|
pointer,
|
4083
|
4020
|
button,
|
4084
|
4021
|
});
|