瀏覽代碼

fix: image-related fixes (#4147)

* flush queues on portal close

* fix mouse broadcast race condition

* stop mutating image elements when updating status

to fix race condition when closing/opening collab room

* check `files` when resolving `LayerUI`

* fix displaying AbortError
vanilla_orig
David Luzar 4 年之前
父節點
當前提交
c61f95a327
No account linked to committer's email address

+ 6
- 6
src/components/App.tsx 查看文件

@@ -113,7 +113,11 @@ import {
113 113
   updateBoundElements,
114 114
 } from "../element/binding";
115 115
 import { LinearElementEditor } from "../element/linearElementEditor";
116
-import { bumpVersion, mutateElement } from "../element/mutateElement";
116
+import {
117
+  bumpVersion,
118
+  mutateElement,
119
+  newElementWith,
120
+} from "../element/mutateElement";
117 121
 import { deepCopyElement, newFreeDrawElement } from "../element/newElement";
118 122
 import {
119 123
   isBindingElement,
@@ -4268,11 +4272,7 @@ class App extends React.Component<AppProps, AppState> {
4268 4272
         }
4269 4273
 
4270 4274
         if (erroredFiles.has(element.fileId)) {
4271
-          mutateElement(
4272
-            element,
4273
-            { status: "error" },
4274
-            /* informMutation */ false,
4275
-          );
4275
+          newElementWith(element, { status: "error" });
4276 4276
         }
4277 4277
       }
4278 4278
     }

+ 1
- 0
src/components/LayerUI.tsx 查看文件

@@ -845,6 +845,7 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
845 845
     prev.renderCustomFooter === next.renderCustomFooter &&
846 846
     prev.langCode === next.langCode &&
847 847
     prev.elements === next.elements &&
848
+    prev.files === next.files &&
848 849
     keys.every((key) => prevAppState[key] === nextAppState[key])
849 850
   );
850 851
 };

+ 6
- 7
src/excalidraw-app/collab/CollabWrapper.tsx 查看文件

@@ -60,7 +60,7 @@ import {
60 60
   isImageElement,
61 61
   isInitializedImageElement,
62 62
 } from "../../element/typeChecks";
63
-import { mutateElement } from "../../element/mutateElement";
63
+import { newElementWith } from "../../element/mutateElement";
64 64
 import {
65 65
   ReconciledElements,
66 66
   reconcileElements as _reconcileElements,
@@ -241,6 +241,9 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
241 241
   };
242 242
 
243 243
   closePortal = () => {
244
+    this.queueBroadcastAllElements.cancel();
245
+    this.loadImageFiles.cancel();
246
+
244 247
     this.saveCollabRoomToFirebase();
245 248
     if (window.confirm(t("alerts.collabStopOverridePrompt"))) {
246 249
       window.history.pushState({}, APP_NAME, window.location.origin);
@@ -253,7 +256,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
253 256
         .getSceneElementsIncludingDeleted()
254 257
         .map((element) => {
255 258
           if (isImageElement(element) && element.status === "saved") {
256
-            return mutateElement(element, { status: "pending" }, false);
259
+            return newElementWith(element, { status: "pending" });
257 260
           }
258 261
           return element;
259 262
         });
@@ -351,11 +354,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
351 354
     } else {
352 355
       const elements = this.excalidrawAPI.getSceneElements().map((element) => {
353 356
         if (isImageElement(element) && element.status === "saved") {
354
-          return mutateElement(
355
-            element,
356
-            { status: "pending" },
357
-            /* informMutation */ false,
358
-          );
357
+          return newElementWith(element, { status: "pending" });
359 358
         }
360 359
         return element;
361 360
       });

+ 11
- 12
src/excalidraw-app/collab/Portal.tsx 查看文件

@@ -11,7 +11,7 @@ import { BROADCAST, FILE_UPLOAD_TIMEOUT, SCENE } from "../app_constants";
11 11
 import { UserIdleState } from "../../types";
12 12
 import { trackEvent } from "../../analytics";
13 13
 import { throttle } from "lodash";
14
-import { mutateElement } from "../../element/mutateElement";
14
+import { newElementWith } from "../../element/mutateElement";
15 15
 import { BroadcastedExcalidrawElement } from "./reconciliation";
16 16
 
17 17
 class Portal {
@@ -54,6 +54,7 @@ class Portal {
54 54
     if (!this.socket) {
55 55
       return;
56 56
     }
57
+    this.queueFileUpload.flush();
57 58
     this.socket.close();
58 59
     this.socket = null;
59 60
     this.roomId = null;
@@ -79,7 +80,7 @@ class Portal {
79 80
       const json = JSON.stringify(data);
80 81
       const encoded = new TextEncoder().encode(json);
81 82
       const encrypted = await encryptAESGEM(encoded, this.roomKey!);
82
-      this.socket!.emit(
83
+      this.socket?.emit(
83 84
         volatile ? BROADCAST.SERVER_VOLATILE : BROADCAST.SERVER,
84 85
         this.roomId,
85 86
         encrypted.data,
@@ -95,11 +96,13 @@ class Portal {
95 96
         files: this.collab.excalidrawAPI.getFiles(),
96 97
       });
97 98
     } catch (error) {
98
-      this.collab.excalidrawAPI.updateScene({
99
-        appState: {
100
-          errorMessage: error.message,
101
-        },
102
-      });
99
+      if (error.name !== "AbortError") {
100
+        this.collab.excalidrawAPI.updateScene({
101
+          appState: {
102
+            errorMessage: error.message,
103
+          },
104
+        });
105
+      }
103 106
     }
104 107
 
105 108
     this.collab.excalidrawAPI.updateScene({
@@ -110,11 +113,7 @@ class Portal {
110 113
             // this will signal collaborators to pull image data from server
111 114
             // (using mutation instead of newElementWith otherwise it'd break
112 115
             // in-progress dragging)
113
-            return mutateElement(
114
-              element,
115
-              { status: "saved" },
116
-              /* informMutation */ false,
117
-            );
116
+            return newElementWith(element, { status: "saved" });
118 117
           }
119 118
           return element;
120 119
         }),

+ 3
- 1
src/excalidraw-app/components/ExportToExcalidrawPlus.tsx 查看文件

@@ -95,7 +95,9 @@ export const ExportToExcalidrawPlus: React.FC<{
95 95
             await exportToExcalidrawPlus(elements, appState, files);
96 96
           } catch (error) {
97 97
             console.error(error);
98
-            onError(new Error(t("exportDialog.excalidrawplus_exportError")));
98
+            if (error.name !== "AbortError") {
99
+              onError(new Error(t("exportDialog.excalidrawplus_exportError")));
100
+            }
99 101
           }
100 102
         }}
101 103
       />

+ 4
- 8
src/excalidraw-app/data/FileManager.ts 查看文件

@@ -1,5 +1,5 @@
1 1
 import { compressData } from "../../data/encode";
2
-import { mutateElement } from "../../element/mutateElement";
2
+import { newElementWith } from "../../element/mutateElement";
3 3
 import { isInitializedImageElement } from "../../element/typeChecks";
4 4
 import {
5 5
   ExcalidrawElement,
@@ -235,13 +235,9 @@ export const updateStaleImageStatuses = (params: {
235 235
           isInitializedImageElement(element) &&
236 236
           params.erroredFiles.has(element.fileId)
237 237
         ) {
238
-          return mutateElement(
239
-            element,
240
-            {
241
-              status: "error",
242
-            },
243
-            false,
244
-          );
238
+          return newElementWith(element, {
239
+            status: "error",
240
+          });
245 241
         }
246 242
         return element;
247 243
       }),

+ 2
- 6
src/excalidraw-app/index.tsx 查看文件

@@ -64,7 +64,7 @@ import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
64 64
 
65 65
 import { getMany, set, del, keys, createStore } from "idb-keyval";
66 66
 import { FileManager, updateStaleImageStatuses } from "./data/FileManager";
67
-import { mutateElement } from "../element/mutateElement";
67
+import { newElementWith } from "../element/mutateElement";
68 68
 import { isInitializedImageElement } from "../element/typeChecks";
69 69
 import { loadFilesFromFirebase } from "./data/firebase";
70 70
 
@@ -465,11 +465,7 @@ const ExcalidrawWrapper = () => {
465 465
             .map((element) => {
466 466
               if (localFileStorage.shouldUpdateImageElementStatus(element)) {
467 467
                 didChange = true;
468
-                return mutateElement(
469
-                  element,
470
-                  { status: "saved" },
471
-                  /* informMutation */ false,
472
-                );
468
+                return newElementWith(element, { status: "saved" });
473 469
               }
474 470
               return element;
475 471
             });

Loading…
取消
儲存