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
This commit is contained in:
David Luzar 2021-10-30 23:40:35 +02:00 committed by GitHub
parent d6d629f416
commit c61f95a327
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 33 additions and 40 deletions

View File

@ -113,7 +113,11 @@ import {
updateBoundElements, updateBoundElements,
} from "../element/binding"; } from "../element/binding";
import { LinearElementEditor } from "../element/linearElementEditor"; import { LinearElementEditor } from "../element/linearElementEditor";
import { bumpVersion, mutateElement } from "../element/mutateElement"; import {
bumpVersion,
mutateElement,
newElementWith,
} from "../element/mutateElement";
import { deepCopyElement, newFreeDrawElement } from "../element/newElement"; import { deepCopyElement, newFreeDrawElement } from "../element/newElement";
import { import {
isBindingElement, isBindingElement,
@ -4268,11 +4272,7 @@ class App extends React.Component<AppProps, AppState> {
} }
if (erroredFiles.has(element.fileId)) { if (erroredFiles.has(element.fileId)) {
mutateElement( newElementWith(element, { status: "error" });
element,
{ status: "error" },
/* informMutation */ false,
);
} }
} }
} }

View File

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

View File

@ -60,7 +60,7 @@ import {
isImageElement, isImageElement,
isInitializedImageElement, isInitializedImageElement,
} from "../../element/typeChecks"; } from "../../element/typeChecks";
import { mutateElement } from "../../element/mutateElement"; import { newElementWith } from "../../element/mutateElement";
import { import {
ReconciledElements, ReconciledElements,
reconcileElements as _reconcileElements, reconcileElements as _reconcileElements,
@ -241,6 +241,9 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
}; };
closePortal = () => { closePortal = () => {
this.queueBroadcastAllElements.cancel();
this.loadImageFiles.cancel();
this.saveCollabRoomToFirebase(); this.saveCollabRoomToFirebase();
if (window.confirm(t("alerts.collabStopOverridePrompt"))) { if (window.confirm(t("alerts.collabStopOverridePrompt"))) {
window.history.pushState({}, APP_NAME, window.location.origin); window.history.pushState({}, APP_NAME, window.location.origin);
@ -253,7 +256,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
.getSceneElementsIncludingDeleted() .getSceneElementsIncludingDeleted()
.map((element) => { .map((element) => {
if (isImageElement(element) && element.status === "saved") { if (isImageElement(element) && element.status === "saved") {
return mutateElement(element, { status: "pending" }, false); return newElementWith(element, { status: "pending" });
} }
return element; return element;
}); });
@ -351,11 +354,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
} else { } else {
const elements = this.excalidrawAPI.getSceneElements().map((element) => { const elements = this.excalidrawAPI.getSceneElements().map((element) => {
if (isImageElement(element) && element.status === "saved") { if (isImageElement(element) && element.status === "saved") {
return mutateElement( return newElementWith(element, { status: "pending" });
element,
{ status: "pending" },
/* informMutation */ false,
);
} }
return element; return element;
}); });

View File

@ -11,7 +11,7 @@ import { BROADCAST, FILE_UPLOAD_TIMEOUT, SCENE } from "../app_constants";
import { UserIdleState } from "../../types"; import { UserIdleState } from "../../types";
import { trackEvent } from "../../analytics"; import { trackEvent } from "../../analytics";
import { throttle } from "lodash"; import { throttle } from "lodash";
import { mutateElement } from "../../element/mutateElement"; import { newElementWith } from "../../element/mutateElement";
import { BroadcastedExcalidrawElement } from "./reconciliation"; import { BroadcastedExcalidrawElement } from "./reconciliation";
class Portal { class Portal {
@ -54,6 +54,7 @@ class Portal {
if (!this.socket) { if (!this.socket) {
return; return;
} }
this.queueFileUpload.flush();
this.socket.close(); this.socket.close();
this.socket = null; this.socket = null;
this.roomId = null; this.roomId = null;
@ -79,7 +80,7 @@ class Portal {
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.roomKey!);
this.socket!.emit( this.socket?.emit(
volatile ? BROADCAST.SERVER_VOLATILE : BROADCAST.SERVER, volatile ? BROADCAST.SERVER_VOLATILE : BROADCAST.SERVER,
this.roomId, this.roomId,
encrypted.data, encrypted.data,
@ -95,12 +96,14 @@ class Portal {
files: this.collab.excalidrawAPI.getFiles(), files: this.collab.excalidrawAPI.getFiles(),
}); });
} catch (error) { } catch (error) {
if (error.name !== "AbortError") {
this.collab.excalidrawAPI.updateScene({ this.collab.excalidrawAPI.updateScene({
appState: { appState: {
errorMessage: error.message, errorMessage: error.message,
}, },
}); });
} }
}
this.collab.excalidrawAPI.updateScene({ this.collab.excalidrawAPI.updateScene({
elements: this.collab.excalidrawAPI elements: this.collab.excalidrawAPI
@ -110,11 +113,7 @@ class Portal {
// this will signal collaborators to pull image data from server // this will signal collaborators to pull image data from server
// (using mutation instead of newElementWith otherwise it'd break // (using mutation instead of newElementWith otherwise it'd break
// in-progress dragging) // in-progress dragging)
return mutateElement( return newElementWith(element, { status: "saved" });
element,
{ status: "saved" },
/* informMutation */ false,
);
} }
return element; return element;
}), }),

View File

@ -95,8 +95,10 @@ export const ExportToExcalidrawPlus: React.FC<{
await exportToExcalidrawPlus(elements, appState, files); await exportToExcalidrawPlus(elements, appState, files);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
if (error.name !== "AbortError") {
onError(new Error(t("exportDialog.excalidrawplus_exportError"))); onError(new Error(t("exportDialog.excalidrawplus_exportError")));
} }
}
}} }}
/> />
</Card> </Card>

View File

@ -1,5 +1,5 @@
import { compressData } from "../../data/encode"; import { compressData } from "../../data/encode";
import { mutateElement } from "../../element/mutateElement"; import { newElementWith } from "../../element/mutateElement";
import { isInitializedImageElement } from "../../element/typeChecks"; import { isInitializedImageElement } from "../../element/typeChecks";
import { import {
ExcalidrawElement, ExcalidrawElement,
@ -235,13 +235,9 @@ export const updateStaleImageStatuses = (params: {
isInitializedImageElement(element) && isInitializedImageElement(element) &&
params.erroredFiles.has(element.fileId) params.erroredFiles.has(element.fileId)
) { ) {
return mutateElement( return newElementWith(element, {
element,
{
status: "error", status: "error",
}, });
false,
);
} }
return element; return element;
}), }),

View File

@ -64,7 +64,7 @@ import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
import { getMany, set, del, keys, createStore } from "idb-keyval"; import { getMany, set, del, keys, createStore } from "idb-keyval";
import { FileManager, updateStaleImageStatuses } from "./data/FileManager"; import { FileManager, updateStaleImageStatuses } from "./data/FileManager";
import { mutateElement } from "../element/mutateElement"; import { newElementWith } from "../element/mutateElement";
import { isInitializedImageElement } from "../element/typeChecks"; import { isInitializedImageElement } from "../element/typeChecks";
import { loadFilesFromFirebase } from "./data/firebase"; import { loadFilesFromFirebase } from "./data/firebase";
@ -465,11 +465,7 @@ const ExcalidrawWrapper = () => {
.map((element) => { .map((element) => {
if (localFileStorage.shouldUpdateImageElementStatus(element)) { if (localFileStorage.shouldUpdateImageElementStatus(element)) {
didChange = true; didChange = true;
return mutateElement( return newElementWith(element, { status: "saved" });
element,
{ status: "saved" },
/* informMutation */ false,
);
} }
return element; return element;
}); });