1
- import type { PayloadAction , Selector } from '@reduxjs/toolkit' ;
2
- import { createSelector , createSlice } from '@reduxjs/toolkit' ;
1
+ import type { PayloadAction , Selector , UnknownAction } from '@reduxjs/toolkit' ;
2
+ import { createSelector , createSlice , isAnyOf } from '@reduxjs/toolkit' ;
3
3
import type { RootState } from 'app/store/store' ;
4
4
import type { SliceConfig } from 'app/store/types' ;
5
5
import { isPlainObject } from 'es-toolkit' ;
@@ -18,7 +18,7 @@ import {
18
18
const zAutoSwitchMode = z . enum ( [ 'off' , 'switch_on_start' , 'switch_on_finish' ] ) ;
19
19
export type AutoSwitchMode = z . infer < typeof zAutoSwitchMode > ;
20
20
21
- const zCanvasSharedSettingsState = z . object ( {
21
+ const zCanvasSharedSettings = z . object ( {
22
22
/**
23
23
* Whether to show HUD (Heads-Up Display) on the canvas.
24
24
*/
@@ -89,9 +89,9 @@ const zCanvasSharedSettingsState = z.object({
89
89
*/
90
90
stagingAreaAutoSwitch : zAutoSwitchMode ,
91
91
} ) ;
92
- type CanvasSharedSettingsState = z . infer < typeof zCanvasSharedSettingsState > ;
92
+ type CanvasSharedSettings = z . infer < typeof zCanvasSharedSettings > ;
93
93
94
- const zCanvasInstanceSettingsState = z . object ( {
94
+ const zCanvasInstanceSettings = z . object ( {
95
95
canvasId : z . string ( ) ,
96
96
/**
97
97
* The width of the brush tool.
@@ -108,16 +108,16 @@ const zCanvasInstanceSettingsState = z.object({
108
108
bgColor : zRgbaColor ,
109
109
fgColor : zRgbaColor ,
110
110
} ) ;
111
- type CanvasInstanceSettingsState = z . infer < typeof zCanvasInstanceSettingsState > ;
111
+ type CanvasInstanceSettings = z . infer < typeof zCanvasInstanceSettings > ;
112
112
113
113
const zCanvasSettingsState = z . object ( {
114
114
_version : z . literal ( 1 ) ,
115
- shared : zCanvasSharedSettingsState ,
116
- canvases : z . array ( zCanvasInstanceSettingsState ) ,
115
+ shared : zCanvasSharedSettings ,
116
+ canvases : z . record ( z . string ( ) , zCanvasInstanceSettings ) ,
117
117
} ) ;
118
118
type CanvasSettingsState = z . infer < typeof zCanvasSettingsState > ;
119
119
120
- const getInitialCanvasSharedSettingsState = ( ) : CanvasSharedSettingsState => ( {
120
+ const getInitialCanvasSharedSettings = ( ) : CanvasSharedSettings => ( {
121
121
showHUD : true ,
122
122
clipToBbox : false ,
123
123
dynamicGrid : false ,
@@ -135,26 +135,26 @@ const getInitialCanvasSharedSettingsState = (): CanvasSharedSettingsState => ({
135
135
saveAllImagesToGallery : false ,
136
136
stagingAreaAutoSwitch : 'switch_on_start' ,
137
137
} ) ;
138
- const getInitialCanvasInstanceSettingsState = ( canvasId : string ) : CanvasInstanceSettingsState => ( {
138
+ const getInitialCanvasInstanceSettings = ( canvasId : string ) : CanvasInstanceSettings => ( {
139
139
canvasId,
140
140
brushWidth : 50 ,
141
141
eraserWidth : 50 ,
142
142
activeColor : 'fgColor' ,
143
143
bgColor : RGBA_BLACK ,
144
144
fgColor : RGBA_WHITE ,
145
145
} ) ;
146
- const getInitialState = ( ) : CanvasSettingsState => ( {
146
+ const getInitialCanvasSettingsState = ( ) : CanvasSettingsState => ( {
147
147
_version : 1 ,
148
- shared : getInitialCanvasSharedSettingsState ( ) ,
149
- canvases : [ ] ,
148
+ shared : getInitialCanvasSharedSettings ( ) ,
149
+ canvases : { } ,
150
150
} ) ;
151
151
152
- type CanvasPayload < T > = { canvasId : string } & T ;
153
- type CanvasPayloadAction < T > = PayloadAction < CanvasPayload < T > > ;
152
+ type PayloadWithCanvasId < P > = P & { canvasId : string } ;
153
+ type CanvasPayloadAction < P > = PayloadAction < PayloadWithCanvasId < P > > ;
154
154
155
- const slice = createSlice ( {
155
+ const canvasSettingsSlice = createSlice ( {
156
156
name : 'canvasSettings' ,
157
- initialState : getInitialState ( ) ,
157
+ initialState : getInitialCanvasSettingsState ( ) ,
158
158
reducers : {
159
159
settingsClipToBboxChanged : ( state , action : PayloadAction < { clipToBbox : boolean } > ) => {
160
160
const { clipToBbox } = action . payload ;
@@ -167,67 +167,6 @@ const slice = createSlice({
167
167
settingsShowHUDToggled : ( state ) => {
168
168
state . shared . showHUD = ! state . shared . showHUD ;
169
169
} ,
170
- settingsBrushWidthChanged : ( state , action : CanvasPayloadAction < { brushWidth : number } > ) => {
171
- const { canvasId, brushWidth } = action . payload ;
172
-
173
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
174
- if ( ! settings ) {
175
- return ;
176
- }
177
-
178
- settings . brushWidth = Math . round ( brushWidth ) ;
179
- } ,
180
- settingsEraserWidthChanged : ( state , action : CanvasPayloadAction < { eraserWidth : number } > ) => {
181
- const { canvasId, eraserWidth } = action . payload ;
182
-
183
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
184
- if ( ! settings ) {
185
- return ;
186
- }
187
-
188
- settings . eraserWidth = Math . round ( eraserWidth ) ;
189
- } ,
190
- settingsActiveColorToggled : ( state , action : CanvasPayloadAction < unknown > ) => {
191
- const { canvasId } = action . payload ;
192
-
193
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
194
- if ( ! settings ) {
195
- return ;
196
- }
197
-
198
- settings . activeColor = settings . activeColor === 'bgColor' ? 'fgColor' : 'bgColor' ;
199
- } ,
200
- settingsBgColorChanged : ( state , action : CanvasPayloadAction < { bgColor : Partial < RgbaColor > } > ) => {
201
- const { canvasId, bgColor } = action . payload ;
202
-
203
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
204
- if ( ! settings ) {
205
- return ;
206
- }
207
-
208
- settings . bgColor = { ...settings . bgColor , ...bgColor } ;
209
- } ,
210
- settingsFgColorChanged : ( state , action : CanvasPayloadAction < { fgColor : Partial < RgbaColor > } > ) => {
211
- const { canvasId, fgColor } = action . payload ;
212
-
213
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
214
- if ( ! settings ) {
215
- return ;
216
- }
217
-
218
- settings . fgColor = { ...settings . fgColor , ...fgColor } ;
219
- } ,
220
- settingsColorsSetToDefault : ( state , action : CanvasPayloadAction < unknown > ) => {
221
- const { canvasId } = action . payload ;
222
-
223
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
224
- if ( ! settings ) {
225
- return ;
226
- }
227
-
228
- settings . bgColor = RGBA_BLACK ;
229
- settings . fgColor = RGBA_WHITE ;
230
- } ,
231
170
settingsInvertScrollForToolWidthChanged : ( state , action : PayloadAction < { invertScrollForToolWidth : boolean } > ) => {
232
171
const { invertScrollForToolWidth } = action . payload ;
233
172
@@ -268,7 +207,7 @@ const slice = createSlice({
268
207
} ,
269
208
settingsStagingAreaAutoSwitchChanged : (
270
209
state ,
271
- action : PayloadAction < { stagingAreaAutoSwitch : CanvasSharedSettingsState [ 'stagingAreaAutoSwitch' ] } >
210
+ action : PayloadAction < { stagingAreaAutoSwitch : CanvasSharedSettings [ 'stagingAreaAutoSwitch' ] } >
272
211
) => {
273
212
const { stagingAreaAutoSwitch } = action . payload ;
274
213
@@ -277,32 +216,62 @@ const slice = createSlice({
277
216
} ,
278
217
extraReducers ( builder ) {
279
218
builder . addCase ( canvasCreated , ( state , action ) => {
280
- const canvasSettings = getInitialCanvasInstanceSettingsState ( action . payload . canvasId ) ;
281
- state . canvases . push ( canvasSettings ) ;
219
+ const canvasSettings = getInitialCanvasInstanceSettings ( action . payload . canvasId ) ;
220
+ state . canvases [ canvasSettings . canvasId ] = canvasSettings ;
282
221
} ) ;
283
222
builder . addCase ( canvasRemoved , ( state , action ) => {
284
- state . canvases = state . canvases . filter ( ( settings ) => settings . canvasId !== action . payload . canvasId ) ;
223
+ delete state . canvases [ action . payload . canvasId ] ;
285
224
} ) ;
286
225
builder . addCase ( canvasMultiCanvasMigrated , ( state , action ) => {
287
- const settings = state . canvases . find ( ( settings ) => settings . canvasId === MIGRATION_MULTI_CANVAS_ID_PLACEHOLDER ) ;
226
+ const settings = state . canvases [ MIGRATION_MULTI_CANVAS_ID_PLACEHOLDER ] ;
288
227
if ( ! settings ) {
289
228
return ;
290
229
}
291
230
settings . canvasId = action . payload . canvasId ;
231
+ state . canvases [ settings . canvasId ] = settings ;
232
+ delete state . canvases [ MIGRATION_MULTI_CANVAS_ID_PLACEHOLDER ] ;
292
233
} ) ;
293
234
} ,
294
235
} ) ;
295
236
237
+ const canvasInstanceSettingsSlice = createSlice ( {
238
+ name : 'canvasSettings' ,
239
+ initialState : { } as CanvasInstanceSettings ,
240
+ reducers : {
241
+ settingsBrushWidthChanged : ( state , action : CanvasPayloadAction < { brushWidth : number } > ) => {
242
+ const { brushWidth } = action . payload ;
243
+
244
+ state . brushWidth = Math . round ( brushWidth ) ;
245
+ } ,
246
+ settingsEraserWidthChanged : ( state , action : CanvasPayloadAction < { eraserWidth : number } > ) => {
247
+ const { eraserWidth } = action . payload ;
248
+
249
+ state . eraserWidth = Math . round ( eraserWidth ) ;
250
+ } ,
251
+ settingsActiveColorToggled : ( state , _action : CanvasPayloadAction < unknown > ) => {
252
+ state . activeColor = state . activeColor === 'bgColor' ? 'fgColor' : 'bgColor' ;
253
+ } ,
254
+ settingsBgColorChanged : ( state , action : CanvasPayloadAction < { bgColor : Partial < RgbaColor > } > ) => {
255
+ const { bgColor } = action . payload ;
256
+
257
+ state . bgColor = { ...state . bgColor , ...bgColor } ;
258
+ } ,
259
+ settingsFgColorChanged : ( state , action : CanvasPayloadAction < { fgColor : Partial < RgbaColor > } > ) => {
260
+ const { fgColor } = action . payload ;
261
+
262
+ state . fgColor = { ...state . fgColor , ...fgColor } ;
263
+ } ,
264
+ settingsColorsSetToDefault : ( state , _action : CanvasPayloadAction < unknown > ) => {
265
+ state . bgColor = RGBA_BLACK ;
266
+ state . fgColor = RGBA_WHITE ;
267
+ } ,
268
+ } ,
269
+ } ) ;
270
+
296
271
export const {
297
272
settingsClipToBboxChanged,
298
273
settingsDynamicGridToggled,
299
274
settingsShowHUDToggled,
300
- settingsBrushWidthChanged,
301
- settingsEraserWidthChanged,
302
- settingsActiveColorToggled,
303
- settingsBgColorChanged,
304
- settingsFgColorChanged,
305
- settingsColorsSetToDefault,
306
275
settingsInvertScrollForToolWidthChanged,
307
276
settingsOutputOnlyMaskedRegionsToggled,
308
277
settingsAutoProcessToggled,
@@ -316,28 +285,57 @@ export const {
316
285
settingsRuleOfThirdsToggled,
317
286
settingsSaveAllImagesToGalleryToggled,
318
287
settingsStagingAreaAutoSwitchChanged,
319
- } = slice . actions ;
288
+ } = canvasSettingsSlice . actions ;
289
+
290
+ export const {
291
+ settingsBrushWidthChanged,
292
+ settingsEraserWidthChanged,
293
+ settingsActiveColorToggled,
294
+ settingsBgColorChanged,
295
+ settingsFgColorChanged,
296
+ settingsColorsSetToDefault,
297
+ } = canvasInstanceSettingsSlice . actions ;
298
+
299
+ const isCanvasInstanceSettingsAction = isAnyOf ( ...Object . values ( canvasInstanceSettingsSlice . actions ) ) ;
300
+
301
+ export const canvasSettingsReducer = ( state : CanvasSettingsState , action : UnknownAction ) : CanvasSettingsState => {
302
+ state = canvasSettingsSlice . reducer ( state , action ) ;
320
303
321
- export const canvasSettingsSliceConfig : SliceConfig < typeof slice > = {
322
- slice,
304
+ if ( ! isCanvasInstanceSettingsAction ( action ) ) {
305
+ return state ;
306
+ }
307
+
308
+ const canvasId = action . payload . canvasId ;
309
+
310
+ return {
311
+ ...state ,
312
+ canvases : {
313
+ ...state . canvases ,
314
+ [ canvasId ] : canvasInstanceSettingsSlice . reducer ( state . canvases [ canvasId ] , action ) ,
315
+ } ,
316
+ } ;
317
+ } ;
318
+
319
+ export const canvasSettingsSliceConfig : SliceConfig < typeof canvasSettingsSlice > = {
320
+ slice : canvasSettingsSlice ,
323
321
schema : zCanvasSettingsState ,
324
- getInitialState,
322
+ getInitialState : getInitialCanvasSettingsState ,
325
323
persistConfig : {
326
324
migrate : ( state ) => {
327
325
assert ( isPlainObject ( state ) ) ;
328
326
if ( ! ( '_version' in state ) ) {
329
327
// Migrate from v1: slice represented a canvas settings instance -> slice represents multiple canvas settings instances
330
- const canvas = {
328
+ const settings = {
331
329
canvasId : MIGRATION_MULTI_CANVAS_ID_PLACEHOLDER ,
332
330
...state ,
333
- } as CanvasInstanceSettingsState ;
331
+ } as CanvasInstanceSettings ;
334
332
335
333
state = {
336
334
_version : 1 ,
337
335
shared : {
338
336
...state ,
339
337
} ,
340
- canvases : [ canvas ] ,
338
+ canvases : { [ settings . canvasId ] : settings } ,
341
339
} ;
342
340
}
343
341
@@ -359,52 +357,42 @@ export const buildSelectCanvasSettingsByCanvasId = (canvasId: string) =>
359
357
) ;
360
358
const selectCanvasSharedSettings = ( state : RootState ) => state . canvasSettings . shared ;
361
359
const selectCanvasInstanceSettings = ( state : RootState , canvasId : string ) => {
362
- const settings = state . canvasSettings . canvases . find ( ( settings ) => settings . canvasId === canvasId ) ;
360
+ const settings = state . canvasSettings . canvases [ canvasId ] ;
363
361
assert ( settings , 'Settings must exist for a canvas once the canvas has been created' ) ;
364
362
return settings ;
365
363
} ;
366
364
367
365
const buildCanvasSharedSettingsSelector =
368
- < T > ( selector : Selector < CanvasSharedSettingsState , T > ) =>
366
+ < T > ( selector : Selector < CanvasSharedSettings , T > ) =>
369
367
( state : RootState ) =>
370
368
selector ( selectCanvasSharedSettings ( state ) ) ;
371
369
const buildCanvasInstanceSettingsSelector =
372
- < T > ( selector : Selector < CanvasInstanceSettingsState , T > ) =>
370
+ < T > ( selector : Selector < CanvasInstanceSettings , T > ) =>
373
371
( state : RootState , canvasId : string ) =>
374
372
selector ( selectCanvasInstanceSettings ( state , canvasId ) ) ;
375
373
376
- export const selectPreserveMask = buildCanvasSharedSettingsSelector ( ( settings ) => settings . preserveMask ) ;
374
+ export const selectPreserveMask = buildCanvasSharedSettingsSelector ( ( state ) => state . preserveMask ) ;
377
375
export const selectOutputOnlyMaskedRegions = buildCanvasSharedSettingsSelector (
378
- ( settings ) => settings . outputOnlyMaskedRegions
376
+ ( state ) => state . outputOnlyMaskedRegions
379
377
) ;
380
- export const selectDynamicGrid = buildCanvasSharedSettingsSelector ( ( settings ) => settings . dynamicGrid ) ;
378
+ export const selectDynamicGrid = buildCanvasSharedSettingsSelector ( ( state ) => state . dynamicGrid ) ;
381
379
export const selectInvertScrollForToolWidth = buildCanvasSharedSettingsSelector (
382
- ( settings ) => settings . invertScrollForToolWidth
383
- ) ;
384
- export const selectBboxOverlay = buildCanvasSharedSettingsSelector ( ( settings ) => settings . bboxOverlay ) ;
385
- export const selectShowHUD = buildCanvasSharedSettingsSelector ( ( settings ) => settings . showHUD ) ;
386
- export const selectClipToBbox = buildCanvasSharedSettingsSelector ( ( settings ) => settings . clipToBbox ) ;
387
- export const selectAutoProcess = buildCanvasSharedSettingsSelector ( ( settings ) => settings . autoProcess ) ;
388
- export const selectSnapToGrid = buildCanvasSharedSettingsSelector ( ( settings ) => settings . snapToGrid ) ;
389
- export const selectShowProgressOnCanvas = buildCanvasSharedSettingsSelector (
390
- ( settings ) => settings . showProgressOnCanvas
391
- ) ;
392
- export const selectIsolatedStagingPreview = buildCanvasSharedSettingsSelector (
393
- ( settings ) => settings . isolatedStagingPreview
394
- ) ;
395
- export const selectIsolatedLayerPreview = buildCanvasSharedSettingsSelector (
396
- ( settings ) => settings . isolatedLayerPreview
397
- ) ;
398
- export const selectPressureSensitivity = buildCanvasSharedSettingsSelector ( ( settings ) => settings . pressureSensitivity ) ;
399
- export const selectRuleOfThirds = buildCanvasSharedSettingsSelector ( ( settings ) => settings . ruleOfThirds ) ;
400
- export const selectSaveAllImagesToGallery = buildCanvasSharedSettingsSelector (
401
- ( settings ) => settings . saveAllImagesToGallery
402
- ) ;
403
- export const selectStagingAreaAutoSwitch = buildCanvasSharedSettingsSelector (
404
- ( settings ) => settings . stagingAreaAutoSwitch
380
+ ( state ) => state . invertScrollForToolWidth
405
381
) ;
406
- export const selectActiveColor = buildCanvasInstanceSettingsSelector ( ( settings ) => settings . activeColor ) ;
407
- export const selectBgColor = buildCanvasInstanceSettingsSelector ( ( settings ) => settings . bgColor ) ;
408
- export const selectFgColor = buildCanvasInstanceSettingsSelector ( ( settings ) => settings . fgColor ) ;
409
- export const selectBrushWidth = buildCanvasInstanceSettingsSelector ( ( settings ) => settings . brushWidth ) ;
410
- export const selectEraserWidth = buildCanvasInstanceSettingsSelector ( ( settings ) => settings . eraserWidth ) ;
382
+ export const selectBboxOverlay = buildCanvasSharedSettingsSelector ( ( state ) => state . bboxOverlay ) ;
383
+ export const selectShowHUD = buildCanvasSharedSettingsSelector ( ( state ) => state . showHUD ) ;
384
+ export const selectClipToBbox = buildCanvasSharedSettingsSelector ( ( state ) => state . clipToBbox ) ;
385
+ export const selectAutoProcess = buildCanvasSharedSettingsSelector ( ( state ) => state . autoProcess ) ;
386
+ export const selectSnapToGrid = buildCanvasSharedSettingsSelector ( ( state ) => state . snapToGrid ) ;
387
+ export const selectShowProgressOnCanvas = buildCanvasSharedSettingsSelector ( ( state ) => state . showProgressOnCanvas ) ;
388
+ export const selectIsolatedStagingPreview = buildCanvasSharedSettingsSelector ( ( state ) => state . isolatedStagingPreview ) ;
389
+ export const selectIsolatedLayerPreview = buildCanvasSharedSettingsSelector ( ( state ) => state . isolatedLayerPreview ) ;
390
+ export const selectPressureSensitivity = buildCanvasSharedSettingsSelector ( ( state ) => state . pressureSensitivity ) ;
391
+ export const selectRuleOfThirds = buildCanvasSharedSettingsSelector ( ( state ) => state . ruleOfThirds ) ;
392
+ export const selectSaveAllImagesToGallery = buildCanvasSharedSettingsSelector ( ( state ) => state . saveAllImagesToGallery ) ;
393
+ export const selectStagingAreaAutoSwitch = buildCanvasSharedSettingsSelector ( ( state ) => state . stagingAreaAutoSwitch ) ;
394
+ export const selectActiveColor = buildCanvasInstanceSettingsSelector ( ( state ) => state . activeColor ) ;
395
+ export const selectBgColor = buildCanvasInstanceSettingsSelector ( ( state ) => state . bgColor ) ;
396
+ export const selectFgColor = buildCanvasInstanceSettingsSelector ( ( state ) => state . fgColor ) ;
397
+ export const selectBrushWidth = buildCanvasInstanceSettingsSelector ( ( state ) => state . brushWidth ) ;
398
+ export const selectEraserWidth = buildCanvasInstanceSettingsSelector ( ( state ) => state . eraserWidth ) ;
0 commit comments