Skip to content

Commit 19ba409

Browse files
author
Attila Cseh
committed
undo/redo refinements
1 parent edd34a9 commit 19ba409

File tree

4 files changed

+42
-48
lines changed

4 files changed

+42
-48
lines changed

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ 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, undoableCanvasSliceReducer } from 'features/controlLayers/store/canvasSlice';
25+
import { canvasSliceConfig, undoableCanvasesReducer } 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';
@@ -90,7 +90,7 @@ const ALL_REDUCERS = {
9090
[api.reducerPath]: api.reducer,
9191
[canvasSessionSliceConfig.slice.reducerPath]: canvasSessionSliceConfig.slice.reducer,
9292
[canvasSettingsSliceConfig.slice.reducerPath]: canvasSettingsSliceConfig.slice.reducer,
93-
[canvasSliceConfig.slice.reducerPath]: undoableCanvasSliceReducer,
93+
[canvasSliceConfig.slice.reducerPath]: undoableCanvasesReducer,
9494
[changeBoardModalSliceConfig.slice.reducerPath]: changeBoardModalSliceConfig.slice.reducer,
9595
[configSliceConfig.slice.reducerPath]: configSliceConfig.slice.reducer,
9696
[dynamicPromptsSliceConfig.slice.reducerPath]: dynamicPromptsSliceConfig.slice.reducer,
@@ -119,7 +119,7 @@ const unserialize: UnserializeFunction = (data, key) => {
119119
if (!sliceConfig?.persistConfig) {
120120
throw new Error(`No persist config for slice "${key}"`);
121121
}
122-
const { getInitialState, persistConfig, undoableConfig } = sliceConfig;
122+
const { getInitialState, persistConfig } = sliceConfig;
123123
let state;
124124
try {
125125
const initialState = getInitialState();
@@ -151,12 +151,7 @@ const unserialize: UnserializeFunction = (data, key) => {
151151
state = getInitialState();
152152
}
153153

154-
// Undoable slices must be wrapped in a history!
155-
if (undoableConfig) {
156-
return undoableConfig.wrapState(state);
157-
} else {
158-
return state;
159-
}
154+
return persistConfig.wrapState ? persistConfig.wrapState(state) : state;
160155
};
161156

162157
const serialize: SerializeFunction = (data, key) => {
@@ -166,7 +161,7 @@ const serialize: SerializeFunction = (data, key) => {
166161
}
167162

168163
const result = omit(
169-
sliceConfig.undoableConfig ? sliceConfig.undoableConfig.unwrapState(data) : data,
164+
sliceConfig.persistConfig.unwrapState ? sliceConfig.persistConfig.unwrapState(data) : data,
170165
sliceConfig.persistConfig.persistDenylist ?? []
171166
);
172167

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,19 @@ export type SliceConfig<T extends Slice, TInternalState = StateFromSlice<T>, TSe
3232
* Keys to omit from the persisted state.
3333
*/
3434
persistDenylist?: (keyof StateFromSlice<T>)[];
35-
};
36-
/**
37-
* The optional undoable configuration for this slice. If omitted, the slice will not be undoable.
38-
*/
39-
undoableConfig?: {
4035
/**
4136
* Wraps state into state with history
4237
*
4338
* @param state The state without history
4439
* @returns The state with history
4540
*/
46-
wrapState: (state: unknown) => TInternalState;
41+
wrapState?: (state: unknown) => TInternalState;
4742
/**
4843
* Unwraps state with history
4944
*
5045
* @param state The state with history
5146
* @returns The state without history
5247
*/
53-
unwrapState: (state: TInternalState) => TSerializedState;
48+
unwrapState?: (state: TInternalState) => TSerializedState;
5449
};
5550
};

invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,6 @@ import {
109109
makeDefaultRasterLayerAdjustments,
110110
} from './util';
111111

112-
type CanvasDeletedPayloadAction = PayloadAction<{ id: string }>;
113-
114112
const getInitialCanvasState = (id: string, name: string): CanvasState => ({
115113
id,
116114
name,
@@ -156,7 +154,7 @@ const getInitialCanvasesHistoryState = (): CanvasesStateWithHistory => {
156154
};
157155
};
158156

159-
const slice = createSlice({
157+
const canvasesSlice = createSlice({
160158
name: 'canvas',
161159
initialState: getInitialCanvasesHistoryState(),
162160
reducers: {
@@ -198,7 +196,7 @@ const slice = createSlice({
198196

199197
canvas.name = name;
200198
},
201-
canvasDeleted: (state, action: CanvasDeletedPayloadAction) => {
199+
canvasDeleted: (state, action: PayloadAction<{ id: string }>) => {
202200
const { id } = action.payload;
203201

204202
if (state.canvases.length === 1) {
@@ -1869,7 +1867,7 @@ export const {
18691867
canvasSelected,
18701868
canvasNameChanged,
18711869
canvasDeleted,
1872-
} = slice.actions;
1870+
} = canvasesSlice.actions;
18731871

18741872
export const {
18751873
canvasMetadataRecalled,
@@ -1969,6 +1967,8 @@ export const {
19691967
// inpaintMaskRecalled,
19701968
} = canvasSlice.actions;
19711969

1970+
const isCanvasSliceAction = isAnyOf(...Object.values(canvasSlice.actions));
1971+
19721972
let filter = true;
19731973

19741974
const canvasUndoableConfig: UndoableOptions<CanvasState, UnknownAction> = {
@@ -1978,7 +1978,7 @@ const canvasUndoableConfig: UndoableOptions<CanvasState, UnknownAction> = {
19781978
clearHistoryType: canvasClearHistory.type,
19791979
filter: (action, _state, _history) => {
19801980
// Ignore both all actions from other slices and canvas management actions
1981-
if (!action.type.startsWith(slice.name)) {
1981+
if (!action.type.startsWith(canvasSlice.name)) {
19821982
return false;
19831983
}
19841984
// Throttle rapid actions of the same type
@@ -1991,11 +1991,15 @@ const canvasUndoableConfig: UndoableOptions<CanvasState, UnknownAction> = {
19911991

19921992
const undoableCanvasReducer = undoable(canvasSlice.reducer, canvasUndoableConfig);
19931993

1994-
export const undoableCanvasSliceReducer = (
1994+
export const undoableCanvasesReducer = (
19951995
state: CanvasesStateWithHistory,
19961996
action: UnknownAction
19971997
): CanvasesStateWithHistory => {
1998-
state = slice.reducer(state, action);
1998+
state = canvasesSlice.reducer(state, action);
1999+
2000+
if (!isCanvasSliceAction(action)) {
2001+
return state;
2002+
}
19992003

20002004
return {
20012005
...state,
@@ -2005,28 +2009,14 @@ export const undoableCanvasSliceReducer = (
20052009
};
20062010
};
20072011

2008-
export const canvasSliceConfig: SliceConfig<typeof slice, CanvasesStateWithHistory, CanvasesStateWithoutHistory> = {
2009-
slice,
2012+
export const canvasSliceConfig: SliceConfig<
2013+
typeof canvasesSlice,
2014+
CanvasesStateWithHistory,
2015+
CanvasesStateWithoutHistory
2016+
> = {
2017+
slice: canvasesSlice,
20102018
getInitialState: getInitialCanvasesState,
20112019
schema: zCanvasesStateWithHistory,
2012-
undoableConfig: {
2013-
unwrapState: (state) => {
2014-
return {
2015-
_version: state._version,
2016-
selectedCanvasId: state.selectedCanvasId,
2017-
canvases: state.canvases.map((canvas) => canvas.present),
2018-
};
2019-
},
2020-
wrapState: (state) => {
2021-
const canvasesState = state as CanvasesStateWithoutHistory;
2022-
2023-
return {
2024-
_version: canvasesState._version,
2025-
selectedCanvasId: canvasesState.selectedCanvasId,
2026-
canvases: canvasesState.canvases.map((canvas) => newHistory([], canvas, [])),
2027-
};
2028-
},
2029-
},
20302020
persistConfig: {
20312021
migrate: (state) => {
20322022
assert(isPlainObject(state));
@@ -2049,6 +2039,22 @@ export const canvasSliceConfig: SliceConfig<typeof slice, CanvasesStateWithHisto
20492039
}
20502040
return zCanvasesStateWithoutHistory.parse(state);
20512041
},
2042+
wrapState: (state) => {
2043+
const canvasesState = state as CanvasesStateWithoutHistory;
2044+
2045+
return {
2046+
_version: canvasesState._version,
2047+
selectedCanvasId: canvasesState.selectedCanvasId,
2048+
canvases: canvasesState.canvases.map((canvas) => newHistory([], canvas, [])),
2049+
};
2050+
},
2051+
unwrapState: (state) => {
2052+
return {
2053+
_version: state._version,
2054+
selectedCanvasId: state.selectedCanvasId,
2055+
canvases: state.canvases.map((canvas) => canvas.present),
2056+
};
2057+
},
20522058
},
20532059
};
20542060

invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,14 +819,12 @@ export const nodesSliceConfig: SliceConfig<typeof slice, StateWithHistory<NodesS
819819
}
820820
return zNodesState.parse(state);
821821
},
822-
},
823-
undoableConfig: {
824-
unwrapState: (state) => state.present,
825822
wrapState: (state) => {
826823
const nodesState = state as NodesState;
827824

828825
return newHistory([], nodesState, []);
829826
},
827+
unwrapState: (state) => state.present,
830828
},
831829
};
832830

0 commit comments

Comments
 (0)