Skip to content

Commit 251a668

Browse files
author
Attila Cseh
committed
undo/redo handles multiple canvases
1 parent b37583f commit 251a668

File tree

7 files changed

+485
-538
lines changed

7 files changed

+485
-538
lines changed

invokeai/frontend/web/src/app/store/store.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ import { merge } from 'es-toolkit';
2222
import { omit, pick } from 'es-toolkit/compat';
2323
import { changeBoardModalSliceConfig } from 'features/changeBoardModal/store/slice';
2424
import { canvasSettingsSliceConfig } from 'features/controlLayers/store/canvasSettingsSlice';
25-
import { canvasSliceConfig } from 'features/controlLayers/store/canvasSlice';
25+
import { canvasSliceConfig, undoableCanvasSliceReducer } from 'features/controlLayers/store/canvasSlice';
2626
import { canvasSessionSliceConfig } from 'features/controlLayers/store/canvasStagingAreaSlice';
2727
import { lorasSliceConfig } from 'features/controlLayers/store/lorasSlice';
2828
import { paramsSliceConfig } from 'features/controlLayers/store/paramsSlice';
2929
import { refImagesSliceConfig } from 'features/controlLayers/store/refImagesSlice';
3030
import { dynamicPromptsSliceConfig } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
3131
import { gallerySliceConfig } from 'features/gallery/store/gallerySlice';
3232
import { modelManagerSliceConfig } from 'features/modelManagerV2/store/modelManagerV2Slice';
33-
import { nodesSliceConfig } from 'features/nodes/store/nodesSlice';
33+
import { nodesSliceConfig, undoableNodesSliceReducer } from 'features/nodes/store/nodesSlice';
3434
import { workflowLibrarySliceConfig } from 'features/nodes/store/workflowLibrarySlice';
3535
import { workflowSettingsSliceConfig } from 'features/nodes/store/workflowSettingsSlice';
3636
import { upscaleSliceConfig } from 'features/parameters/store/upscaleSlice';
@@ -44,7 +44,6 @@ import { diff } from 'jsondiffpatch';
4444
import dynamicMiddlewares from 'redux-dynamic-middlewares';
4545
import type { SerializeFunction, UnserializeFunction } from 'redux-remember';
4646
import { REMEMBER_REHYDRATED, rememberEnhancer, rememberReducer } from 'redux-remember';
47-
import undoable, { newHistory } from 'redux-undo';
4847
import { serializeError } from 'serialize-error';
4948
import { api } from 'services/api';
5049
import { authToastMiddleware } from 'services/api/authToastMiddleware';
@@ -91,22 +90,14 @@ const ALL_REDUCERS = {
9190
[api.reducerPath]: api.reducer,
9291
[canvasSessionSliceConfig.slice.reducerPath]: canvasSessionSliceConfig.slice.reducer,
9392
[canvasSettingsSliceConfig.slice.reducerPath]: canvasSettingsSliceConfig.slice.reducer,
94-
// Undoable!
95-
[canvasSliceConfig.slice.reducerPath]: undoable(
96-
canvasSliceConfig.slice.reducer,
97-
canvasSliceConfig.undoableConfig?.reduxUndoOptions
98-
),
93+
[canvasSliceConfig.slice.reducerPath]: undoableCanvasSliceReducer,
9994
[changeBoardModalSliceConfig.slice.reducerPath]: changeBoardModalSliceConfig.slice.reducer,
10095
[configSliceConfig.slice.reducerPath]: configSliceConfig.slice.reducer,
10196
[dynamicPromptsSliceConfig.slice.reducerPath]: dynamicPromptsSliceConfig.slice.reducer,
10297
[gallerySliceConfig.slice.reducerPath]: gallerySliceConfig.slice.reducer,
10398
[lorasSliceConfig.slice.reducerPath]: lorasSliceConfig.slice.reducer,
10499
[modelManagerSliceConfig.slice.reducerPath]: modelManagerSliceConfig.slice.reducer,
105-
// Undoable!
106-
[nodesSliceConfig.slice.reducerPath]: undoable(
107-
nodesSliceConfig.slice.reducer,
108-
nodesSliceConfig.undoableConfig?.reduxUndoOptions
109-
),
100+
[nodesSliceConfig.slice.reducerPath]: undoableNodesSliceReducer,
110101
[paramsSliceConfig.slice.reducerPath]: paramsSliceConfig.slice.reducer,
111102
[queueSliceConfig.slice.reducerPath]: queueSliceConfig.slice.reducer,
112103
[refImagesSliceConfig.slice.reducerPath]: refImagesSliceConfig.slice.reducer,
@@ -162,7 +153,7 @@ const unserialize: UnserializeFunction = (data, key) => {
162153

163154
// Undoable slices must be wrapped in a history!
164155
if (undoableConfig) {
165-
return newHistory([], state, []);
156+
return undoableConfig.wrapState(state);
166157
} else {
167158
return state;
168159
}
@@ -175,7 +166,7 @@ const serialize: SerializeFunction = (data, key) => {
175166
}
176167

177168
const result = omit(
178-
sliceConfig.undoableConfig ? data.present : data,
169+
sliceConfig.undoableConfig ? sliceConfig.undoableConfig.unwrapState(data) : data,
179170
sliceConfig.persistConfig.persistDenylist ?? []
180171
);
181172

invokeai/frontend/web/src/app/store/types.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import type { Slice } from '@reduxjs/toolkit';
2-
import type { UndoableOptions } from 'redux-undo';
32
import type { ZodType } from 'zod';
43

54
type StateFromSlice<T extends Slice> = T extends Slice<infer U> ? U : never;
65

7-
export type SliceConfig<T extends Slice> = {
6+
export type SliceConfig<T extends Slice, TInternalState = StateFromSlice<T>, TSerializedState = StateFromSlice<T>> = {
87
/**
98
* The redux slice (return of createSlice).
109
*/
@@ -16,7 +15,7 @@ export type SliceConfig<T extends Slice> = {
1615
/**
1716
* A function that returns the initial state of the slice.
1817
*/
19-
getInitialState: () => StateFromSlice<T>;
18+
getInitialState: () => TSerializedState;
2019
/**
2120
* The optional persist configuration for this slice. If omitted, the slice will not be persisted.
2221
*/
@@ -28,7 +27,7 @@ export type SliceConfig<T extends Slice> = {
2827
* @param state The rehydrated state.
2928
* @returns A correctly-shaped state.
3029
*/
31-
migrate: (state: unknown) => StateFromSlice<T>;
30+
migrate: (state: unknown) => TSerializedState;
3231
/**
3332
* Keys to omit from the persisted state.
3433
*/
@@ -39,8 +38,18 @@ export type SliceConfig<T extends Slice> = {
3938
*/
4039
undoableConfig?: {
4140
/**
42-
* The options to be passed into redux-undo.
41+
* Wraps state into state with history
42+
*
43+
* @param state The state without history
44+
* @returns The state with history
45+
*/
46+
wrapState: (state: unknown) => TInternalState;
47+
/**
48+
* Unwraps state with history
49+
*
50+
* @param state The state with history
51+
* @returns The state without history
4352
*/
44-
reduxUndoOptions: UndoableOptions<StateFromSlice<T>>;
53+
unwrapState: (state: TInternalState) => TSerializedState;
4554
};
4655
};

0 commit comments

Comments
 (0)