mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Fix undo / redo
This commit is contained in:
parent
0c21f1ae07
commit
eb9b6ac837
5 changed files with 118 additions and 70 deletions
|
@ -1233,3 +1233,27 @@ export const sizeOf = (
|
||||||
? value.size
|
? value.size
|
||||||
: Object.keys(value).length;
|
: Object.keys(value).length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep freeze an object to prevent any modifications to the object or its nested properties.
|
||||||
|
*/
|
||||||
|
export const deepFreeze = <T>(obj: T): T => {
|
||||||
|
// Return if obj is null or not an object
|
||||||
|
if (obj === null || typeof obj !== "object") {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Freeze the object itself
|
||||||
|
Object.freeze(obj);
|
||||||
|
|
||||||
|
// Freeze all properties
|
||||||
|
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
||||||
|
const value = (obj as any)[prop];
|
||||||
|
|
||||||
|
if (value && typeof value === "object") {
|
||||||
|
deepFreeze(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
isTestEnv,
|
isTestEnv,
|
||||||
randomId,
|
randomId,
|
||||||
Emitter,
|
Emitter,
|
||||||
|
deepFreeze,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import type { DTO, ValueOf } from "@excalidraw/common/utility-types";
|
import type { DTO, ValueOf } from "@excalidraw/common/utility-types";
|
||||||
|
@ -21,6 +22,7 @@ import { ElementsDelta, AppStateDelta, Delta } from "./delta";
|
||||||
import { hashElementsVersion, hashString } from "./index";
|
import { hashElementsVersion, hashString } from "./index";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
ElementsMap,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
OrderedExcalidrawElement,
|
OrderedExcalidrawElement,
|
||||||
SceneElementsMap,
|
SceneElementsMap,
|
||||||
|
@ -102,7 +104,7 @@ export class Store {
|
||||||
*/
|
*/
|
||||||
public scheduleMicroAction(
|
public scheduleMicroAction(
|
||||||
action: CaptureUpdateActionType,
|
action: CaptureUpdateActionType,
|
||||||
elements: Map<string, OrderedExcalidrawElement> | undefined,
|
elements: SceneElementsMap | undefined,
|
||||||
appState: AppState | ObservedAppState | undefined = undefined,
|
appState: AppState | ObservedAppState | undefined = undefined,
|
||||||
/** delta is only relevant for `CaptureUpdateAction.IMMEDIATELY`, as it's the only action producing `DurableStoreIncrement` containing a delta */
|
/** delta is only relevant for `CaptureUpdateAction.IMMEDIATELY`, as it's the only action producing `DurableStoreIncrement` containing a delta */
|
||||||
delta: StoreDelta | undefined = undefined,
|
delta: StoreDelta | undefined = undefined,
|
||||||
|
@ -126,7 +128,7 @@ export class Store {
|
||||||
* @emits StoreIncrement
|
* @emits StoreIncrement
|
||||||
*/
|
*/
|
||||||
public commit(
|
public commit(
|
||||||
elements: Map<string, OrderedExcalidrawElement> | undefined,
|
elements: SceneElementsMap | undefined,
|
||||||
appState: AppState | ObservedAppState | undefined,
|
appState: AppState | ObservedAppState | undefined,
|
||||||
): void {
|
): void {
|
||||||
// execute all scheduled micro actions first
|
// execute all scheduled micro actions first
|
||||||
|
@ -156,37 +158,15 @@ export class Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the increment to the passed elements and appState, does not modify the snapshot.
|
|
||||||
*/
|
|
||||||
public applyDeltaTo(
|
|
||||||
delta: StoreDelta,
|
|
||||||
elements: SceneElementsMap,
|
|
||||||
appState: AppState,
|
|
||||||
): [SceneElementsMap, AppState, boolean] {
|
|
||||||
const [nextElements, elementsContainVisibleChange] = delta.elements.applyTo(
|
|
||||||
elements,
|
|
||||||
this.snapshot.elements,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [nextAppState, appStateContainsVisibleChange] =
|
|
||||||
delta.appState.applyTo(appState, nextElements);
|
|
||||||
|
|
||||||
const appliedVisibleChanges =
|
|
||||||
elementsContainVisibleChange || appStateContainsVisibleChange;
|
|
||||||
|
|
||||||
return [nextElements, nextAppState, appliedVisibleChanges];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters out yet uncomitted elements from `nextElements`, which are part of in-progress local async actions (ephemerals) and thus were not yet commited to the snapshot.
|
* Filters out yet uncomitted elements from `nextElements`, which are part of in-progress local async actions (ephemerals) and thus were not yet commited to the snapshot.
|
||||||
*
|
*
|
||||||
* This is necessary in updates in which we receive reconciled elements, already containing elements which were not yet captured by the local store (i.e. collab).
|
* This is necessary in updates in which we receive reconciled elements, already containing elements which were not yet captured by the local store (i.e. collab).
|
||||||
*/
|
*/
|
||||||
public filterUncomittedElements(
|
public filterUncomittedElements(
|
||||||
prevElements: Map<string, ExcalidrawElement>,
|
prevElements: ElementsMap,
|
||||||
nextElements: Map<string, ExcalidrawElement>,
|
nextElements: ElementsMap,
|
||||||
): Map<string, OrderedExcalidrawElement> {
|
): SceneElementsMap {
|
||||||
const movedElements = new Map<string, ExcalidrawElement>();
|
const movedElements = new Map<string, ExcalidrawElement>();
|
||||||
|
|
||||||
for (const [id, prevElement] of prevElements.entries()) {
|
for (const [id, prevElement] of prevElements.entries()) {
|
||||||
|
@ -217,7 +197,7 @@ export class Store {
|
||||||
movedElements,
|
movedElements,
|
||||||
);
|
);
|
||||||
|
|
||||||
return arrayToMap(syncedElements);
|
return arrayToMap(syncedElements) as SceneElementsMap;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Clears the store instance.
|
* Clears the store instance.
|
||||||
|
@ -274,24 +254,28 @@ export class Store {
|
||||||
) {
|
) {
|
||||||
const prevSnapshot = this.snapshot;
|
const prevSnapshot = this.snapshot;
|
||||||
|
|
||||||
// Calculate the deltas based on the previous and next snapshot
|
let storeDelta: StoreDelta;
|
||||||
// We might have the delta already (i.e. when applying history entry), thus we don't need to calculate it again
|
|
||||||
const elementsDelta = delta
|
|
||||||
? delta.elements
|
|
||||||
: snapshot.metadata.didElementsChange
|
|
||||||
? ElementsDelta.calculate(prevSnapshot.elements, snapshot.elements)
|
|
||||||
: ElementsDelta.empty();
|
|
||||||
|
|
||||||
const appStateDelta = delta
|
if (delta) {
|
||||||
? delta.appState
|
// we might have the delta already (i.e. when applying history entry), thus we don't need to calculate it again
|
||||||
: snapshot.metadata.didAppStateChange
|
// using the same instance, since in history we have a check against `HistoryEntry`, so that we don't re-record the same delta again
|
||||||
? AppStateDelta.calculate(prevSnapshot.appState, snapshot.appState)
|
storeDelta = delta;
|
||||||
: AppStateDelta.empty();
|
} else {
|
||||||
|
// calculate the deltas based on the previous and next snapshot
|
||||||
|
const elementsDelta = snapshot.metadata.didElementsChange
|
||||||
|
? ElementsDelta.calculate(prevSnapshot.elements, snapshot.elements)
|
||||||
|
: ElementsDelta.empty();
|
||||||
|
|
||||||
if (!elementsDelta.isEmpty() || !appStateDelta.isEmpty()) {
|
const appStateDelta = snapshot.metadata.didAppStateChange
|
||||||
const delta = StoreDelta.create(elementsDelta, appStateDelta);
|
? AppStateDelta.calculate(prevSnapshot.appState, snapshot.appState)
|
||||||
|
: AppStateDelta.empty();
|
||||||
|
|
||||||
|
storeDelta = StoreDelta.create(elementsDelta, appStateDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!storeDelta.isEmpty()) {
|
||||||
const change = StoreChange.create(prevSnapshot, snapshot);
|
const change = StoreChange.create(prevSnapshot, snapshot);
|
||||||
const increment = new DurableIncrement(change, delta);
|
const increment = new DurableIncrement(change, storeDelta);
|
||||||
|
|
||||||
// Notify listeners with the increment
|
// Notify listeners with the increment
|
||||||
this.onStoreIncrementEmitter.trigger(increment);
|
this.onStoreIncrementEmitter.trigger(increment);
|
||||||
|
@ -317,7 +301,7 @@ export class Store {
|
||||||
*/
|
*/
|
||||||
private maybeCloneSnapshot(
|
private maybeCloneSnapshot(
|
||||||
action: CaptureUpdateActionType,
|
action: CaptureUpdateActionType,
|
||||||
elements: Map<string, OrderedExcalidrawElement> | undefined,
|
elements: SceneElementsMap | undefined,
|
||||||
appState: AppState | ObservedAppState | undefined,
|
appState: AppState | ObservedAppState | undefined,
|
||||||
) {
|
) {
|
||||||
if (!elements && !appState) {
|
if (!elements && !appState) {
|
||||||
|
@ -395,6 +379,7 @@ export class Store {
|
||||||
*/
|
*/
|
||||||
export class StoreChange {
|
export class StoreChange {
|
||||||
// so figuring out what has changed should ideally be just quick reference checks
|
// so figuring out what has changed should ideally be just quick reference checks
|
||||||
|
// TODO: we might need to have binary files here as well, in order to be drop-in replacement for `onChange`
|
||||||
private constructor(
|
private constructor(
|
||||||
public readonly elements: Record<string, OrderedExcalidrawElement>,
|
public readonly elements: Record<string, OrderedExcalidrawElement>,
|
||||||
public readonly appState: Partial<ObservedAppState>,
|
public readonly appState: Partial<ObservedAppState>,
|
||||||
|
@ -442,6 +427,7 @@ export class DurableIncrement extends StoreIncrement {
|
||||||
public readonly delta: StoreDelta,
|
public readonly delta: StoreDelta,
|
||||||
) {
|
) {
|
||||||
super("durable", change);
|
super("durable", change);
|
||||||
|
deepFreeze(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,6 +437,7 @@ export class DurableIncrement extends StoreIncrement {
|
||||||
export class EphemeralIncrement extends StoreIncrement {
|
export class EphemeralIncrement extends StoreIncrement {
|
||||||
constructor(public readonly change: StoreChange) {
|
constructor(public readonly change: StoreChange) {
|
||||||
super("ephemeral", change);
|
super("ephemeral", change);
|
||||||
|
deepFreeze(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,6 +516,29 @@ export class StoreDelta {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the delta to the passed elements and appState, does not modify the snapshot.
|
||||||
|
*/
|
||||||
|
public static applyTo(
|
||||||
|
delta: StoreDelta,
|
||||||
|
elements: SceneElementsMap,
|
||||||
|
appState: AppState,
|
||||||
|
snapshot: StoreSnapshot = StoreSnapshot.empty(),
|
||||||
|
): [SceneElementsMap, AppState, boolean] {
|
||||||
|
const [nextElements, elementsContainVisibleChange] = delta.elements.applyTo(
|
||||||
|
elements,
|
||||||
|
snapshot.elements,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [nextAppState, appStateContainsVisibleChange] =
|
||||||
|
delta.appState.applyTo(appState, nextElements);
|
||||||
|
|
||||||
|
const appliedVisibleChanges =
|
||||||
|
elementsContainVisibleChange || appStateContainsVisibleChange;
|
||||||
|
|
||||||
|
return [nextElements, nextAppState, appliedVisibleChanges];
|
||||||
|
}
|
||||||
|
|
||||||
public isEmpty() {
|
public isEmpty() {
|
||||||
return this.elements.isEmpty() && this.appState.isEmpty();
|
return this.elements.isEmpty() && this.appState.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -543,7 +553,7 @@ export class StoreSnapshot {
|
||||||
private _lastChangedAppStateHash: number = 0;
|
private _lastChangedAppStateHash: number = 0;
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
public readonly elements: Map<string, OrderedExcalidrawElement>,
|
public readonly elements: SceneElementsMap,
|
||||||
public readonly appState: ObservedAppState,
|
public readonly appState: ObservedAppState,
|
||||||
public readonly metadata: {
|
public readonly metadata: {
|
||||||
didElementsChange: boolean;
|
didElementsChange: boolean;
|
||||||
|
@ -557,11 +567,15 @@ export class StoreSnapshot {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static empty() {
|
public static empty() {
|
||||||
return new StoreSnapshot(new Map(), getDefaultObservedAppState(), {
|
return new StoreSnapshot(
|
||||||
didElementsChange: false,
|
new Map() as SceneElementsMap,
|
||||||
didAppStateChange: false,
|
getDefaultObservedAppState(),
|
||||||
isEmpty: true,
|
{
|
||||||
});
|
didElementsChange: false,
|
||||||
|
didAppStateChange: false,
|
||||||
|
isEmpty: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getChangedElements(prevSnapshot: StoreSnapshot) {
|
public getChangedElements(prevSnapshot: StoreSnapshot) {
|
||||||
|
@ -603,7 +617,7 @@ export class StoreSnapshot {
|
||||||
*/
|
*/
|
||||||
public maybeClone(
|
public maybeClone(
|
||||||
action: CaptureUpdateActionType,
|
action: CaptureUpdateActionType,
|
||||||
elements: Map<string, OrderedExcalidrawElement> | undefined,
|
elements: SceneElementsMap | undefined,
|
||||||
appState: AppState | ObservedAppState | undefined,
|
appState: AppState | ObservedAppState | undefined,
|
||||||
) {
|
) {
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -660,7 +674,7 @@ export class StoreSnapshot {
|
||||||
} = {
|
} = {
|
||||||
shouldCompareHashes: false,
|
shouldCompareHashes: false,
|
||||||
},
|
},
|
||||||
) {
|
): ObservedAppState {
|
||||||
if (!appState) {
|
if (!appState) {
|
||||||
return this.appState;
|
return this.appState;
|
||||||
}
|
}
|
||||||
|
@ -670,12 +684,12 @@ export class StoreSnapshot {
|
||||||
? getObservedAppState(appState)
|
? getObservedAppState(appState)
|
||||||
: appState;
|
: appState;
|
||||||
|
|
||||||
const changedAppState = this.detectChangedAppState(
|
const didAppStateChange = this.detectChangedAppState(
|
||||||
nextAppStateSnapshot,
|
nextAppStateSnapshot,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!changedAppState) {
|
if (!didAppStateChange) {
|
||||||
return this.appState;
|
return this.appState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,13 +697,13 @@ export class StoreSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
private maybeCreateElementsSnapshot(
|
private maybeCreateElementsSnapshot(
|
||||||
elements: Map<string, OrderedExcalidrawElement> | undefined,
|
elements: SceneElementsMap | undefined,
|
||||||
options: {
|
options: {
|
||||||
shouldCompareHashes: boolean;
|
shouldCompareHashes: boolean;
|
||||||
} = {
|
} = {
|
||||||
shouldCompareHashes: false,
|
shouldCompareHashes: false,
|
||||||
},
|
},
|
||||||
) {
|
): SceneElementsMap {
|
||||||
if (!elements) {
|
if (!elements) {
|
||||||
return this.elements;
|
return this.elements;
|
||||||
}
|
}
|
||||||
|
@ -711,17 +725,17 @@ export class StoreSnapshot {
|
||||||
} = {
|
} = {
|
||||||
shouldCompareHashes: false,
|
shouldCompareHashes: false,
|
||||||
},
|
},
|
||||||
) {
|
): boolean | undefined {
|
||||||
if (this.appState === nextObservedAppState) {
|
if (this.appState === nextObservedAppState) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changedAppState = Delta.getRightDifferences(
|
const didAppStateChange = Delta.isRightDifferent(
|
||||||
this.appState,
|
this.appState,
|
||||||
nextObservedAppState,
|
nextObservedAppState,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!changedAppState.length) {
|
if (!didAppStateChange) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,7 +752,7 @@ export class StoreSnapshot {
|
||||||
|
|
||||||
this._lastChangedElementsHash = changedAppStateHash;
|
this._lastChangedElementsHash = changedAppStateHash;
|
||||||
|
|
||||||
return changedAppState;
|
return didAppStateChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -747,7 +761,7 @@ export class StoreSnapshot {
|
||||||
* NOTE: we shouldn't just use `sceneVersionNonce` instead, as we need to call this before the scene updates.
|
* NOTE: we shouldn't just use `sceneVersionNonce` instead, as we need to call this before the scene updates.
|
||||||
*/
|
*/
|
||||||
private detectChangedElements(
|
private detectChangedElements(
|
||||||
nextElements: Map<string, OrderedExcalidrawElement>,
|
nextElements: SceneElementsMap,
|
||||||
options: {
|
options: {
|
||||||
shouldCompareHashes: boolean;
|
shouldCompareHashes: boolean;
|
||||||
} = {
|
} = {
|
||||||
|
@ -758,7 +772,7 @@ export class StoreSnapshot {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changedElements: Map<string, OrderedExcalidrawElement> = new Map();
|
const changedElements: SceneElementsMap = new Map() as SceneElementsMap;
|
||||||
|
|
||||||
for (const [id, prevElement] of this.elements) {
|
for (const [id, prevElement] of this.elements) {
|
||||||
const nextElement = nextElements.get(id);
|
const nextElement = nextElements.get(id);
|
||||||
|
@ -806,10 +820,8 @@ export class StoreSnapshot {
|
||||||
/**
|
/**
|
||||||
* Perform structural clone, deep cloning only elements that changed.
|
* Perform structural clone, deep cloning only elements that changed.
|
||||||
*/
|
*/
|
||||||
private createElementsSnapshot(
|
private createElementsSnapshot(changedElements: SceneElementsMap) {
|
||||||
changedElements: Map<string, OrderedExcalidrawElement>,
|
const clonedElements = new Map() as SceneElementsMap;
|
||||||
) {
|
|
||||||
const clonedElements = new Map();
|
|
||||||
|
|
||||||
for (const [id, prevElement] of this.elements) {
|
for (const [id, prevElement] of this.elements) {
|
||||||
// Clone previous elements, never delete, in case nextElements would be just a subset of previous elements
|
// Clone previous elements, never delete, in case nextElements would be just a subset of previous elements
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { isWindows, KEYS, matchKey, arrayToMap } from "@excalidraw/common";
|
||||||
|
|
||||||
import { CaptureUpdateAction } from "@excalidraw/element/store";
|
import { CaptureUpdateAction } from "@excalidraw/element/store";
|
||||||
|
|
||||||
|
import { orderByFractionalIndex } from "@excalidraw/element/fractionalIndex";
|
||||||
|
|
||||||
import type { SceneElementsMap } from "@excalidraw/element/types";
|
import type { SceneElementsMap } from "@excalidraw/element/types";
|
||||||
|
|
||||||
import { ToolButton } from "../components/ToolButton";
|
import { ToolButton } from "../components/ToolButton";
|
||||||
|
@ -35,7 +37,11 @@ const executeHistoryAction = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const [nextElementsMap, nextAppState] = result;
|
const [nextElementsMap, nextAppState] = result;
|
||||||
const nextElements = Array.from(nextElementsMap.values());
|
|
||||||
|
// order by fractional indices in case the map was accidently modified in the meantime
|
||||||
|
const nextElements = orderByFractionalIndex(
|
||||||
|
Array.from(nextElementsMap.values()),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appState: nextAppState,
|
appState: nextAppState,
|
||||||
|
|
|
@ -338,6 +338,7 @@ import type {
|
||||||
ExcalidrawNonSelectionElement,
|
ExcalidrawNonSelectionElement,
|
||||||
ExcalidrawArrowElement,
|
ExcalidrawArrowElement,
|
||||||
ExcalidrawElbowArrowElement,
|
ExcalidrawElbowArrowElement,
|
||||||
|
SceneElementsMap,
|
||||||
} from "@excalidraw/element/types";
|
} from "@excalidraw/element/types";
|
||||||
|
|
||||||
import type { Mutable, ValueOf } from "@excalidraw/common/utility-types";
|
import type { Mutable, ValueOf } from "@excalidraw/common/utility-types";
|
||||||
|
@ -10516,7 +10517,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
// otherwise we would end up with duplicated fractional indices on undo
|
// otherwise we would end up with duplicated fractional indices on undo
|
||||||
this.store.scheduleMicroAction(
|
this.store.scheduleMicroAction(
|
||||||
CaptureUpdateAction.NEVER,
|
CaptureUpdateAction.NEVER,
|
||||||
arrayToMap(elements),
|
arrayToMap(elements) as SceneElementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { Emitter } from "@excalidraw/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CaptureUpdateAction,
|
CaptureUpdateAction,
|
||||||
type Store,
|
|
||||||
StoreDelta,
|
StoreDelta,
|
||||||
|
type Store,
|
||||||
} from "@excalidraw/element/store";
|
} from "@excalidraw/element/store";
|
||||||
|
|
||||||
import type { SceneElementsMap } from "@excalidraw/element/types";
|
import type { SceneElementsMap } from "@excalidraw/element/types";
|
||||||
|
@ -108,7 +108,12 @@ export class History {
|
||||||
try {
|
try {
|
||||||
// creating iteration-scoped variables, so that we can use them in the unstable_scheduleCallback
|
// creating iteration-scoped variables, so that we can use them in the unstable_scheduleCallback
|
||||||
[nextElements, nextAppState, containsVisibleChange] =
|
[nextElements, nextAppState, containsVisibleChange] =
|
||||||
this.store.applyDeltaTo(historyEntry, nextElements, nextAppState);
|
StoreDelta.applyTo(
|
||||||
|
historyEntry,
|
||||||
|
nextElements,
|
||||||
|
nextAppState,
|
||||||
|
this.store.snapshot,
|
||||||
|
);
|
||||||
|
|
||||||
// schedule immediate capture, so that it's emitted for the sync purposes
|
// schedule immediate capture, so that it's emitted for the sync purposes
|
||||||
this.store.scheduleMicroAction(
|
this.store.scheduleMicroAction(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue