mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-10 02:49:57 +02:00
fix: even deltas with version & version nonce are valid (#9897)
This commit is contained in:
@@ -1111,16 +1111,16 @@ export class ElementsDelta implements DeltaContainer<SceneElementsMap> {
|
||||
inserted,
|
||||
}: Delta<ElementPartial>) =>
|
||||
!!(
|
||||
deleted.version &&
|
||||
inserted.version &&
|
||||
// versions are required integers
|
||||
Number.isInteger(deleted.version) &&
|
||||
Number.isInteger(inserted.version) &&
|
||||
// versions should be positive, zero included
|
||||
deleted.version >= 0 &&
|
||||
inserted.version >= 0 &&
|
||||
// versions should never be the same
|
||||
deleted.version !== inserted.version
|
||||
(
|
||||
Number.isInteger(deleted.version) &&
|
||||
Number.isInteger(inserted.version) &&
|
||||
// versions should be positive, zero included
|
||||
deleted.version! >= 0 &&
|
||||
inserted.version! >= 0 &&
|
||||
// versions should never be the same
|
||||
deleted.version !== inserted.version
|
||||
)
|
||||
);
|
||||
|
||||
private static satisfiesUniqueInvariants = (
|
||||
@@ -1191,9 +1191,10 @@ export class ElementsDelta implements DeltaContainer<SceneElementsMap> {
|
||||
ElementsDelta.stripIrrelevantProps,
|
||||
);
|
||||
|
||||
// ignore updates which would "delete" already deleted element
|
||||
if (!prevElement.isDeleted) {
|
||||
removed[prevElement.id] = delta;
|
||||
} else {
|
||||
updated[prevElement.id] = delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1221,6 +1222,8 @@ export class ElementsDelta implements DeltaContainer<SceneElementsMap> {
|
||||
// ignore updates which would "delete" already deleted element
|
||||
if (!nextElement.isDeleted) {
|
||||
added[nextElement.id] = delta;
|
||||
} else {
|
||||
updated[nextElement.id] = delta;
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -1250,15 +1253,7 @@ export class ElementsDelta implements DeltaContainer<SceneElementsMap> {
|
||||
continue;
|
||||
}
|
||||
|
||||
const strippedDeleted = ElementsDelta.stripVersionProps(delta.deleted);
|
||||
const strippedInserted = ElementsDelta.stripVersionProps(
|
||||
delta.inserted,
|
||||
);
|
||||
|
||||
// making sure there are at least some changes and only changed version & versionNonce does not count!
|
||||
if (Delta.isInnerDifferent(strippedDeleted, strippedInserted, true)) {
|
||||
updated[nextElement.id] = delta;
|
||||
}
|
||||
updated[nextElement.id] = delta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1372,15 +1367,8 @@ export class ElementsDelta implements DeltaContainer<SceneElementsMap> {
|
||||
latestDelta = delta;
|
||||
}
|
||||
|
||||
const strippedDeleted = ElementsDelta.stripVersionProps(
|
||||
latestDelta.deleted,
|
||||
);
|
||||
const strippedInserted = ElementsDelta.stripVersionProps(
|
||||
latestDelta.inserted,
|
||||
);
|
||||
|
||||
// it might happen that after applying latest changes the delta itself does not contain any changes
|
||||
if (Delta.isInnerDifferent(strippedDeleted, strippedInserted)) {
|
||||
if (Delta.isInnerDifferent(latestDelta.deleted, latestDelta.inserted)) {
|
||||
modifiedDeltas[id] = latestDelta;
|
||||
}
|
||||
}
|
||||
@@ -2075,12 +2063,4 @@ export class ElementsDelta implements DeltaContainer<SceneElementsMap> {
|
||||
|
||||
return strippedPartial;
|
||||
}
|
||||
|
||||
private static stripVersionProps(
|
||||
partial: Partial<OrderedExcalidrawElement>,
|
||||
): ElementPartial {
|
||||
const { version, versionNonce, ...strippedPartial } = partial;
|
||||
|
||||
return strippedPartial;
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import { AppStateDelta, Delta, ElementsDelta } from "../src/delta";
|
||||
|
||||
describe("ElementsDelta", () => {
|
||||
describe("elements delta calculation", () => {
|
||||
it("should not create removed delta when element gets removed but was already deleted", () => {
|
||||
it("should not throw when element gets removed but was already deleted", () => {
|
||||
const element = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 100,
|
||||
@@ -19,12 +19,12 @@ describe("ElementsDelta", () => {
|
||||
const prevElements = new Map([[element.id, element]]);
|
||||
const nextElements = new Map();
|
||||
|
||||
const delta = ElementsDelta.calculate(prevElements, nextElements);
|
||||
|
||||
expect(delta.isEmpty()).toBeTruthy();
|
||||
expect(() =>
|
||||
ElementsDelta.calculate(prevElements, nextElements),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it("should not create added delta when adding element as already deleted", () => {
|
||||
it("should not throw when adding element as already deleted", () => {
|
||||
const element = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 100,
|
||||
@@ -35,12 +35,12 @@ describe("ElementsDelta", () => {
|
||||
const prevElements = new Map();
|
||||
const nextElements = new Map([[element.id, element]]);
|
||||
|
||||
const delta = ElementsDelta.calculate(prevElements, nextElements);
|
||||
|
||||
expect(delta.isEmpty()).toBeTruthy();
|
||||
expect(() =>
|
||||
ElementsDelta.calculate(prevElements, nextElements),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it("should not create updated delta when there is only version and versionNonce change", () => {
|
||||
it("should create updated delta even when there is only version and versionNonce change", () => {
|
||||
const baseElement = API.createElement({
|
||||
type: "rectangle",
|
||||
x: 100,
|
||||
@@ -65,7 +65,24 @@ describe("ElementsDelta", () => {
|
||||
nextElements as SceneElementsMap,
|
||||
);
|
||||
|
||||
expect(delta.isEmpty()).toBeTruthy();
|
||||
expect(delta).toEqual(
|
||||
ElementsDelta.create(
|
||||
{},
|
||||
{},
|
||||
{
|
||||
[baseElement.id]: Delta.create(
|
||||
{
|
||||
version: baseElement.version,
|
||||
versionNonce: baseElement.versionNonce,
|
||||
},
|
||||
{
|
||||
version: baseElement.version + 1,
|
||||
versionNonce: baseElement.versionNonce + 1,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user