@@ -2,22 +2,14 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
import equal from "react-fast-compare" ;
3
3
import { Box , CircularProgress , Divider , IconButton , Tab , Tabs , Typography , useTheme } from "@mui/material" ;
4
4
import CloseIcon from "@mui/icons-material/Close" ;
5
- import {
6
- Entity ,
7
- EntityCollection ,
8
- EntityStatus ,
9
- EntityValues ,
10
- FireCMSPlugin ,
11
- FormContext ,
12
- ResolvedEntityCollection ,
13
- User
14
- } from "../../types" ;
5
+ import { Entity , EntityCollection , EntityStatus , EntityValues , FireCMSPlugin , FormContext , User } from "../../types" ;
15
6
import { CircularProgressCenter , EntityCollectionView , EntityPreview , ErrorBoundary } from "../components" ;
16
7
import {
17
8
canEditEntity ,
18
9
fullPathToCollectionSegments ,
19
10
removeInitialAndTrailingSlashes ,
20
- resolveDefaultSelectedView
11
+ resolveDefaultSelectedView ,
12
+ useDebounce
21
13
} from "../util" ;
22
14
23
15
import { ADDITIONAL_TAB_WIDTH , CONTAINER_FULL_WIDTH , FORM_CONTAINER_WIDTH } from "./common" ;
@@ -33,6 +25,8 @@ import {
33
25
import { EntityForm } from "../../form" ;
34
26
import { useSideDialogContext } from "../SideDialogs" ;
35
27
import { useLargeSideLayout } from "./useLargeSideLayout" ;
28
+ import { EntityFormSaveParams } from "../../form/EntityForm" ;
29
+ import { setIn } from "formik" ;
36
30
37
31
export interface EntityViewProps < M extends Record < string , any > > {
38
32
path : string ;
@@ -69,6 +63,17 @@ export const EntityView = React.memo<EntityViewProps<any>>(
69
63
console . warn ( `The collection ${ collection . path } has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored` ) ;
70
64
}
71
65
66
+ const [ saving , setSaving ] = useState ( false ) ;
67
+ /**
68
+ * These are the values that are being saved. They are debounced.
69
+ * We use this only when autoSave is enabled.
70
+ */
71
+ const [ valuesToBeSaved , setValuesToBeSaved ] = useState < EntityValues < M > | undefined > ( undefined ) ;
72
+ useDebounce ( valuesToBeSaved , ( ) => {
73
+ if ( valuesToBeSaved )
74
+ saveEntity ( { values : valuesToBeSaved , closeAfterSave : false } ) ;
75
+ } , false , 2000 ) ;
76
+
72
77
const theme = useTheme ( ) ;
73
78
const largeLayout = useLargeSideLayout ( ) ;
74
79
const largeLayoutTabSelected = useRef ( ! largeLayout ) ;
@@ -85,7 +90,6 @@ export const EntityView = React.memo<EntityViewProps<any>>(
85
90
const [ formContext , setFormContext ] = useState < FormContext < M > | undefined > ( undefined ) ;
86
91
87
92
const [ status , setStatus ] = useState < EntityStatus > ( copy ? "copy" : ( entityId ? "existing" : "new" ) ) ;
88
- // const [currentEntityId, setCurrentEntityId] = useState<string | undefined>(entityId);
89
93
90
94
const modifiedValuesRef = useRef < EntityValues < M > | undefined > ( undefined ) ;
91
95
const modifiedValues = modifiedValuesRef . current ;
@@ -176,6 +180,7 @@ export const EntityView = React.memo<EntityViewProps<any>>(
176
180
} , [ defaultSelectedView , largeLayout , selectedSubPath ] ) ;
177
181
178
182
const onPreSaveHookError = useCallback ( ( e : Error ) => {
183
+ setSaving ( false ) ;
179
184
snackbarController . open ( {
180
185
type : "error" ,
181
186
message : "Error before saving: " + e ?. message
@@ -184,6 +189,7 @@ export const EntityView = React.memo<EntityViewProps<any>>(
184
189
} , [ snackbarController ] ) ;
185
190
186
191
const onSaveSuccessHookError = useCallback ( ( e : Error ) => {
192
+ setSaving ( false ) ;
187
193
snackbarController . open ( {
188
194
type : "error" ,
189
195
message : "Error after saving (entity is saved): " + e ?. message
@@ -193,10 +199,12 @@ export const EntityView = React.memo<EntityViewProps<any>>(
193
199
194
200
const onSaveSuccess = ( updatedEntity : Entity < M > , closeAfterSave : boolean ) => {
195
201
196
- snackbarController . open ( {
197
- type : "success" ,
198
- message : `${ collection . singularName ?? collection . name } : Saved correctly`
199
- } ) ;
202
+ setSaving ( false ) ;
203
+ if ( ! autoSave )
204
+ snackbarController . open ( {
205
+ type : "success" ,
206
+ message : `${ collection . singularName ?? collection . name } : Saved correctly`
207
+ } ) ;
200
208
201
209
setUsedEntity ( updatedEntity ) ;
202
210
setStatus ( "existing" ) ;
@@ -224,6 +232,7 @@ export const EntityView = React.memo<EntityViewProps<any>>(
224
232
225
233
const onSaveFailure = useCallback ( ( e : Error ) => {
226
234
235
+ setSaving ( false ) ;
227
236
snackbarController . open ( {
228
237
type : "error" ,
229
238
message : "Error saving: " + e ?. message
@@ -233,26 +242,13 @@ export const EntityView = React.memo<EntityViewProps<any>>(
233
242
console . error ( e ) ;
234
243
} , [ entityId , path , snackbarController ] ) ;
235
244
236
- const onEntitySave = useCallback ( async ( {
237
- collection,
238
- path,
239
- entityId,
240
- values,
241
- previousValues,
242
- closeAfterSave
243
- } : {
244
- collection : ResolvedEntityCollection < M > ,
245
- path : string ,
246
- entityId : string | undefined ,
247
- values : EntityValues < M > ,
248
- previousValues ?: EntityValues < M > ,
249
- closeAfterSave : boolean
250
- } ) : Promise < void > => {
251
-
252
- if ( ! status )
253
- return ;
254
-
255
- return saveEntityWithCallbacks ( {
245
+ function saveEntity ( { values, previousValues, closeAfterSave } : {
246
+ values : M ,
247
+ previousValues ?: M ,
248
+ closeAfterSave : boolean ,
249
+ } ) {
250
+ setSaving ( true ) ;
251
+ saveEntityWithCallbacks ( {
256
252
path,
257
253
entityId,
258
254
values,
@@ -265,8 +261,29 @@ export const EntityView = React.memo<EntityViewProps<any>>(
265
261
onSaveFailure,
266
262
onPreSaveHookError,
267
263
onSaveSuccessHookError
268
- } ) ;
269
- } , [ status , collection , dataSource , context , onSaveSuccess , onSaveFailure , onPreSaveHookError , onSaveSuccessHookError ] ) ;
264
+ } ) . then ( ) ;
265
+ }
266
+
267
+ const onSaveEntityRequest = async ( {
268
+ values,
269
+ previousValues,
270
+ closeAfterSave,
271
+ autoSave
272
+ } : EntityFormSaveParams < M > ) : Promise < void > => {
273
+
274
+ if ( ! status )
275
+ return ;
276
+
277
+ if ( autoSave ) {
278
+ setValuesToBeSaved ( values ) ;
279
+ } else {
280
+ saveEntity ( {
281
+ values,
282
+ previousValues,
283
+ closeAfterSave
284
+ } ) ;
285
+ }
286
+ } ;
270
287
271
288
const customViewsView : React . ReactNode [ ] | undefined = customViews && customViews . map (
272
289
( customView , colIndex ) => {
@@ -306,7 +323,9 @@ export const EntityView = React.memo<EntityViewProps<any>>(
306
323
}
307
324
) . filter ( Boolean ) ;
308
325
309
- const loading = ( dataLoading && ! usedEntity ) || ( ( ! usedEntity || readOnly === undefined ) && ( status === "existing" || status === "copy" ) ) ;
326
+ const globalLoading = ( dataLoading && ! usedEntity ) ||
327
+ ( ( ! usedEntity || readOnly === undefined ) && ( status === "existing" || status === "copy" ) ) ;
328
+ const loading = globalLoading || saving ;
310
329
311
330
const subCollectionsViews = subcollections && subcollections . map (
312
331
( subcollection , colIndex ) => {
@@ -331,7 +350,7 @@ export const EntityView = React.memo<EntityViewProps<any>>(
331
350
332
351
{ loading && < CircularProgressCenter /> }
333
352
334
- { ! loading &&
353
+ { ! globalLoading &&
335
354
( usedEntity && fullPath
336
355
? < EntityCollectionView
337
356
fullPath = { fullPath }
@@ -401,16 +420,21 @@ export const EntityView = React.memo<EntityViewProps<any>>(
401
420
setCurrentEntityId ( id ) ;
402
421
} , [ ] ) ;
403
422
423
+ const onModified = ( dirty : boolean ) => {
424
+ if ( ! autoSave )
425
+ onValuesAreModified ( dirty ) ;
426
+ }
427
+
404
428
function buildForm ( ) {
405
429
const plugins = context . plugins ;
406
430
let form = < EntityForm
407
431
status = { status }
408
432
path = { path }
409
433
collection = { collection }
410
- onEntitySave = { onEntitySave }
434
+ onEntitySaveRequested = { onSaveEntityRequest }
411
435
onDiscard = { onDiscard }
412
436
onValuesChanged = { onValuesChanged }
413
- onModified = { onValuesAreModified }
437
+ onModified = { onModified }
414
438
entity = { usedEntity }
415
439
onIdChange = { onIdChange }
416
440
onFormContextChange = { setFormContext }
@@ -425,10 +449,9 @@ export const EntityView = React.memo<EntityViewProps<any>>(
425
449
status = { status }
426
450
path = { path }
427
451
collection = { collection }
428
- onEntitySave = { onEntitySave }
429
452
onDiscard = { onDiscard }
430
453
onValuesChanged = { onValuesChanged }
431
- onModified = { onValuesAreModified }
454
+ onModified = { onModified }
432
455
entity = { usedEntity }
433
456
context = { context }
434
457
currentEntityId = { currentEntityId }
@@ -504,7 +527,7 @@ export const EntityView = React.memo<EntityViewProps<any>>(
504
527
505
528
< Box flexGrow = { 1 } />
506
529
507
- { loading && < Box
530
+ { globalLoading && < Box
508
531
sx = { {
509
532
alignSelf : "center"
510
533
} } >
@@ -595,7 +618,7 @@ export const EntityView = React.memo<EntityViewProps<any>>(
595
618
}
596
619
} } >
597
620
598
- { loading
621
+ { globalLoading
599
622
? < CircularProgressCenter />
600
623
: form }
601
624
0 commit comments