parent
d315e3dc4d
commit
35049e3de7
@ -6,7 +6,7 @@ import { randomInteger } from "../random";
|
|||||||
|
|
||||||
type ElementUpdate<TElement extends ExcalidrawElement> = Omit<
|
type ElementUpdate<TElement extends ExcalidrawElement> = Omit<
|
||||||
Partial<TElement>,
|
Partial<TElement>,
|
||||||
"id" | "seed"
|
"id" | "seed" | "version" | "versionNonce"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// This function tracks updates of text elements for the purposes for collaboration.
|
// This function tracks updates of text elements for the purposes for collaboration.
|
||||||
@ -52,7 +52,7 @@ export const newElementWith = <TElement extends ExcalidrawElement>(
|
|||||||
updates: ElementUpdate<TElement>,
|
updates: ElementUpdate<TElement>,
|
||||||
): TElement => ({
|
): TElement => ({
|
||||||
...element,
|
...element,
|
||||||
|
...updates,
|
||||||
version: element.version + 1,
|
version: element.version + 1,
|
||||||
versionNonce: randomInteger(),
|
versionNonce: randomInteger(),
|
||||||
...updates,
|
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { AppState } from "./types";
|
import { AppState } from "./types";
|
||||||
import { ExcalidrawElement } from "./element/types";
|
import { ExcalidrawElement } from "./element/types";
|
||||||
import { newElementWith } from "./element/mutateElement";
|
|
||||||
import { isLinearElement } from "./element/typeChecks";
|
import { isLinearElement } from "./element/typeChecks";
|
||||||
import { deepCopyElement } from "./element/newElement";
|
import { deepCopyElement } from "./element/newElement";
|
||||||
|
|
||||||
@ -11,12 +10,11 @@ export interface HistoryEntry {
|
|||||||
|
|
||||||
interface DehydratedExcalidrawElement {
|
interface DehydratedExcalidrawElement {
|
||||||
id: string;
|
id: string;
|
||||||
version: number;
|
|
||||||
versionNonce: number;
|
versionNonce: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DehydratedHistoryEntry {
|
interface DehydratedHistoryEntry {
|
||||||
appState: ReturnType<typeof clearAppStatePropertiesForHistory>;
|
appState: string;
|
||||||
elements: DehydratedExcalidrawElement[];
|
elements: DehydratedExcalidrawElement[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,10 +27,7 @@ const clearAppStatePropertiesForHistory = (appState: AppState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class SceneHistory {
|
export class SceneHistory {
|
||||||
private elementCache = new Map<
|
private elementCache = new Map<string, Map<number, ExcalidrawElement>>();
|
||||||
string,
|
|
||||||
Map<number, Map<number, ExcalidrawElement>>
|
|
||||||
>();
|
|
||||||
private recording: boolean = true;
|
private recording: boolean = true;
|
||||||
private stateHistory: DehydratedHistoryEntry[] = [];
|
private stateHistory: DehydratedHistoryEntry[] = [];
|
||||||
private redoStack: DehydratedHistoryEntry[] = [];
|
private redoStack: DehydratedHistoryEntry[] = [];
|
||||||
@ -43,18 +38,16 @@ export class SceneHistory {
|
|||||||
elements,
|
elements,
|
||||||
}: DehydratedHistoryEntry): HistoryEntry {
|
}: DehydratedHistoryEntry): HistoryEntry {
|
||||||
return {
|
return {
|
||||||
appState,
|
appState: JSON.parse(appState),
|
||||||
elements: elements.map((dehydratedExcalidrawElement) => {
|
elements: elements.map((dehydratedExcalidrawElement) => {
|
||||||
const element = this.elementCache
|
const element = this.elementCache
|
||||||
.get(dehydratedExcalidrawElement.id)
|
.get(dehydratedExcalidrawElement.id)
|
||||||
?.get(dehydratedExcalidrawElement.version)
|
|
||||||
?.get(dehydratedExcalidrawElement.versionNonce);
|
?.get(dehydratedExcalidrawElement.versionNonce);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Element not found: ${dehydratedExcalidrawElement.id}:${dehydratedExcalidrawElement.version}:${dehydratedExcalidrawElement.versionNonce}`,
|
`Element not found: ${dehydratedExcalidrawElement.id}:${dehydratedExcalidrawElement.versionNonce}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -65,22 +58,17 @@ export class SceneHistory {
|
|||||||
elements,
|
elements,
|
||||||
}: HistoryEntry): DehydratedHistoryEntry {
|
}: HistoryEntry): DehydratedHistoryEntry {
|
||||||
return {
|
return {
|
||||||
appState,
|
appState: JSON.stringify(appState),
|
||||||
elements: elements.map((element) => {
|
elements: elements.map((element: ExcalidrawElement) => {
|
||||||
if (!this.elementCache.has(element.id)) {
|
if (!this.elementCache.has(element.id)) {
|
||||||
this.elementCache.set(element.id, new Map());
|
this.elementCache.set(element.id, new Map());
|
||||||
}
|
}
|
||||||
const versions = this.elementCache.get(element.id)!;
|
const versions = this.elementCache.get(element.id)!;
|
||||||
if (!versions.has(element.version)) {
|
if (!versions.has(element.versionNonce)) {
|
||||||
versions.set(element.version, new Map());
|
versions.set(element.versionNonce, deepCopyElement(element));
|
||||||
}
|
|
||||||
const nonces = versions.get(element.version)!;
|
|
||||||
if (!nonces.has(element.versionNonce)) {
|
|
||||||
nonces.set(element.versionNonce, deepCopyElement(element));
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: element.id,
|
id: element.id,
|
||||||
version: element.version,
|
|
||||||
versionNonce: element.versionNonce,
|
versionNonce: element.versionNonce,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
@ -162,7 +150,6 @@ export class SceneHistory {
|
|||||||
!prev ||
|
!prev ||
|
||||||
!next ||
|
!next ||
|
||||||
prev.id !== next.id ||
|
prev.id !== next.id ||
|
||||||
prev.version !== next.version ||
|
|
||||||
prev.versionNonce !== next.versionNonce
|
prev.versionNonce !== next.versionNonce
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
@ -199,17 +186,6 @@ export class SceneHistory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private restoreEntry(entrySerialized: DehydratedHistoryEntry): HistoryEntry {
|
|
||||||
const entry = this.hydrateHistoryEntry(entrySerialized);
|
|
||||||
if (entry) {
|
|
||||||
entry.elements = entry.elements.map((element) => {
|
|
||||||
// renew versions
|
|
||||||
return newElementWith(element, {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearRedoStack() {
|
clearRedoStack() {
|
||||||
this.redoStack.splice(0, this.redoStack.length);
|
this.redoStack.splice(0, this.redoStack.length);
|
||||||
}
|
}
|
||||||
@ -223,7 +199,7 @@ export class SceneHistory {
|
|||||||
|
|
||||||
if (entryToRestore !== undefined) {
|
if (entryToRestore !== undefined) {
|
||||||
this.stateHistory.push(entryToRestore);
|
this.stateHistory.push(entryToRestore);
|
||||||
return this.restoreEntry(entryToRestore);
|
return this.hydrateHistoryEntry(entryToRestore);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -240,7 +216,7 @@ export class SceneHistory {
|
|||||||
|
|
||||||
if (currentEntry !== undefined) {
|
if (currentEntry !== undefined) {
|
||||||
this.redoStack.push(currentEntry);
|
this.redoStack.push(currentEntry);
|
||||||
return this.restoreEntry(entryToRestore);
|
return this.hydrateHistoryEntry(entryToRestore);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -3697,8 +3697,8 @@ Object {
|
|||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 1,
|
"strokeWidth": 1,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"version": 3,
|
"version": 4,
|
||||||
"versionNonce": 1014066025,
|
"versionNonce": 1116226695,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
@ -3720,8 +3720,8 @@ Object {
|
|||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 1,
|
"strokeWidth": 1,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"version": 3,
|
"version": 4,
|
||||||
"versionNonce": 238820263,
|
"versionNonce": 1014066025,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 30,
|
"x": 30,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
@ -12061,8 +12061,8 @@ Object {
|
|||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 1,
|
"strokeWidth": 1,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"version": 3,
|
"version": 6,
|
||||||
"versionNonce": 941653321,
|
"versionNonce": 1006504105,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
@ -12084,8 +12084,8 @@ Object {
|
|||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 1,
|
"strokeWidth": 1,
|
||||||
"type": "rectangle",
|
"type": "rectangle",
|
||||||
"version": 3,
|
"version": 6,
|
||||||
"versionNonce": 908564423,
|
"versionNonce": 289600103,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 30,
|
"x": 30,
|
||||||
"y": 10,
|
"y": 10,
|
||||||
@ -12121,8 +12121,8 @@ Object {
|
|||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 1,
|
"strokeWidth": 1,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"version": 9,
|
"version": 11,
|
||||||
"versionNonce": 1349943049,
|
"versionNonce": 1315507081,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 30,
|
"y": 30,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user