Skip to content

Commit 3f40a1b

Browse files
author
Attila Cseh
committed
undo/redo refinements
1 parent d13bebf commit 3f40a1b

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
@@ -108,8 +108,6 @@ import {
108108
makeDefaultRasterLayerAdjustments,
109109
} from './util';
110110

111-
type CanvasDeletedPayloadAction = PayloadAction<{ id: string }>;
112-
113111
const getInitialCanvasState = (id: string, name: string): CanvasState => ({
114112
id,
115113
name,
@@ -155,7 +153,7 @@ const getInitialCanvasesHistoryState = (): CanvasesStateWithHistory => {
155153
};
156154
};
157155

158-
const slice = createSlice({
156+
const canvasesSlice = createSlice({
159157
name: 'canvas',
160158
initialState: getInitialCanvasesHistoryState(),
161159
reducers: {
@@ -197,7 +195,7 @@ const slice = createSlice({
197195

198196
canvas.name = name;
199197
},
200-
canvasDeleted: (state, action: CanvasDeletedPayloadAction) => {
198+
canvasDeleted: (state, action: PayloadAction<{ id: string }>) => {
201199
const { id } = action.payload;
202200

203201
if (state.canvases.length === 1) {
@@ -1868,7 +1866,7 @@ export const {
18681866
canvasSelected,
18691867
canvasNameChanged,
18701868
canvasDeleted,
1871-
} = slice.actions;
1869+
} = canvasesSlice.actions;
18721870

18731871
export const {
18741872
canvasMetadataRecalled,
@@ -1968,6 +1966,8 @@ export const {
19681966
// inpaintMaskRecalled,
19691967
} = canvasSlice.actions;
19701968

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

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

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

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

19992003
return {
20002004
...state,
@@ -2004,28 +2008,14 @@ export const undoableCanvasSliceReducer = (
20042008
};
20052009
};
20062010

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

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -960,14 +960,12 @@ export const nodesSliceConfig: SliceConfig<typeof slice, StateWithHistory<NodesS
960960
}
961961
return zNodesState.parse(state);
962962
},
963-
},
964-
undoableConfig: {
965-
unwrapState: (state) => state.present,
966963
wrapState: (state) => {
967964
const nodesState = state as NodesState;
968965

969966
return newHistory([], nodesState, []);
970967
},
968+
unwrapState: (state) => state.present,
971969
},
972970
};
973971

0 commit comments

Comments
 (0)