Cache received changes, ignore snapshot cache for durable changes, revert StoreAction, history fix, indices fix

This commit is contained in:
Marcel Mraz 2025-01-21 11:34:42 +01:00
parent 310a9ae4e0
commit 7e0f5b6369
No known key found for this signature in database
GPG key ID: 4EBD6E62DC830CD2
50 changed files with 437 additions and 338 deletions

View file

@ -24,7 +24,7 @@ import {
Excalidraw,
LiveCollaborationTrigger,
TTDDialogTrigger,
SnapshotAction,
StoreAction,
reconcileElements,
newElementWith,
} from "../packages/excalidraw";
@ -527,7 +527,7 @@ const ExcalidrawWrapper = () => {
excalidrawAPI.updateScene({
...data.scene,
...restore(data.scene, null, null, { repairBindings: true }),
snapshotAction: SnapshotAction.CAPTURE,
storeAction: StoreAction.CAPTURE,
});
}
});
@ -554,7 +554,7 @@ const ExcalidrawWrapper = () => {
setLangCode(getPreferredLanguage());
excalidrawAPI.updateScene({
...localDataState,
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
LibraryIndexedDBAdapter.load().then((data) => {
if (data) {
@ -686,7 +686,7 @@ const ExcalidrawWrapper = () => {
if (didChange) {
excalidrawAPI.updateScene({
elements,
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
}
}
@ -856,9 +856,15 @@ const ExcalidrawWrapper = () => {
const debouncedTimeTravel = debounce(
(value: number, direction: "forward" | "backward") => {
let elements = new Map(
excalidrawAPI?.getSceneElements().map((x) => [x.id, x]),
);
if (!excalidrawAPI) {
return;
}
let nextAppState = excalidrawAPI.getAppState();
// CFDO: retrieve the scene map already
let nextElements = new Map(
excalidrawAPI.getSceneElements().map((x) => [x.id, x]),
) as SceneElementsMap;
let deltas: StoreDelta[] = [];
@ -879,19 +885,20 @@ const ExcalidrawWrapper = () => {
}
for (const delta of deltas) {
[elements] = delta.elements.applyTo(
elements as SceneElementsMap,
excalidrawAPI?.store.snapshot.elements!,
[nextElements, nextAppState] = excalidrawAPI.store.applyDeltaTo(
delta,
nextElements,
nextAppState,
);
}
excalidrawAPI?.updateScene({
appState: {
...excalidrawAPI?.getAppState(),
...nextAppState,
viewModeEnabled: value !== acknowledgedDeltas.length,
},
elements: Array.from(elements.values()),
snapshotAction: SnapshotAction.NONE,
elements: Array.from(nextElements.values()),
storeAction: StoreAction.UPDATE,
});
},
0,
@ -918,7 +925,6 @@ const ExcalidrawWrapper = () => {
value={sliderVersion}
onChange={(value) => {
const nextSliderVersion = value as number;
// CFDO II: should be disabled when offline! (later we could have speculative changes in the versioning log as well)
// CFDO: in safari the whole canvas gets selected when dragging
if (nextSliderVersion !== acknowledgedDeltas.length) {
// don't listen to updates in the detached mode

View file

@ -15,7 +15,7 @@ import type {
OrderedExcalidrawElement,
} from "../../packages/excalidraw/element/types";
import {
SnapshotAction,
StoreAction,
getSceneVersion,
restoreElements,
zoomToFitBounds,
@ -393,7 +393,7 @@ class Collab extends PureComponent<CollabProps, CollabState> {
this.excalidrawAPI.updateScene({
elements,
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
}
};
@ -544,7 +544,7 @@ class Collab extends PureComponent<CollabProps, CollabState> {
// to database even if deleted before creating the room.
this.excalidrawAPI.updateScene({
elements,
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
this.saveCollabRoomToFirebase(getSyncableElements(elements));
@ -782,7 +782,7 @@ class Collab extends PureComponent<CollabProps, CollabState> {
) => {
this.excalidrawAPI.updateScene({
elements,
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
this.loadImageFiles();

View file

@ -19,7 +19,7 @@ import throttle from "lodash.throttle";
import { newElementWith } from "../../packages/excalidraw/element/mutateElement";
import { encryptData } from "../../packages/excalidraw/data/encryption";
import type { Socket } from "socket.io-client";
import { SnapshotAction } from "../../packages/excalidraw";
import { StoreAction } from "../../packages/excalidraw";
class Portal {
collab: TCollabClass;
@ -133,7 +133,7 @@ class Portal {
if (isChanged) {
this.collab.excalidrawAPI.updateScene({
elements: newElements,
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
}
}, FILE_UPLOAD_TIMEOUT);

View file

@ -1,4 +1,4 @@
import { SnapshotAction } from "../../packages/excalidraw";
import { StoreAction } from "../../packages/excalidraw";
import { compressData } from "../../packages/excalidraw/data/encode";
import { newElementWith } from "../../packages/excalidraw/element/mutateElement";
import { isInitializedImageElement } from "../../packages/excalidraw/element/typeChecks";
@ -268,6 +268,6 @@ export const updateStaleImageStatuses = (params: {
}
return element;
}),
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
};

View file

@ -11,7 +11,7 @@ import {
createRedoAction,
createUndoAction,
} from "../../packages/excalidraw/actions/actionHistory";
import { SnapshotAction, newElementWith } from "../../packages/excalidraw";
import { StoreAction, newElementWith } from "../../packages/excalidraw";
const { h } = window;
@ -89,7 +89,7 @@ describe("collaboration", () => {
API.updateScene({
elements: syncInvalidIndices([rect1, rect2]),
snapshotAction: SnapshotAction.CAPTURE,
storeAction: StoreAction.CAPTURE,
});
API.updateScene({
@ -97,7 +97,7 @@ describe("collaboration", () => {
rect1,
newElementWith(h.elements[1], { isDeleted: true }),
]),
snapshotAction: SnapshotAction.CAPTURE,
storeAction: StoreAction.CAPTURE,
});
await waitFor(() => {
@ -144,7 +144,7 @@ describe("collaboration", () => {
// simulate force deleting the element remotely
API.updateScene({
elements: syncInvalidIndices([rect1]),
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
await waitFor(() => {
@ -182,7 +182,7 @@ describe("collaboration", () => {
h.elements[0],
newElementWith(h.elements[1], { x: 100 }),
]),
snapshotAction: SnapshotAction.CAPTURE,
storeAction: StoreAction.CAPTURE,
});
await waitFor(() => {
@ -217,7 +217,7 @@ describe("collaboration", () => {
// simulate force deleting the element remotely
API.updateScene({
elements: syncInvalidIndices([rect1]),
snapshotAction: SnapshotAction.UPDATE,
storeAction: StoreAction.UPDATE,
});
// snapshot was correctly updated and marked the element as deleted