1
1
'use client'
2
2
3
- import { useRouter , useSearchParams } from 'next/navigation.js'
4
- import {
5
- type ClientCollectionConfig ,
6
- type ClientGlobalConfig ,
7
- type ClientSideEditViewProps ,
8
- type ClientUser ,
3
+ import type {
4
+ ClientCollectionConfig ,
5
+ ClientGlobalConfig ,
6
+ ClientSideEditViewProps ,
7
+ ClientUser ,
8
+ FormState ,
9
9
} from 'payload'
10
+
11
+ import { useRouter , useSearchParams } from 'next/navigation.js'
10
12
import React , { Fragment , useCallback , useEffect , useRef , useState } from 'react'
11
13
12
14
import type { FormProps } from '../../forms/Form/index.js'
15
+ import type { LockedState } from '../../utilities/buildFormState.js'
13
16
14
17
import { DocumentControls } from '../../elements/DocumentControls/index.js'
15
18
import { DocumentDrawerHeader } from '../../elements/DocumentDrawer/DrawerHeader/index.js'
@@ -34,9 +37,9 @@ import { handleBackToDashboard } from '../../utilities/handleBackToDashboard.js'
34
37
import { handleGoBack } from '../../utilities/handleGoBack.js'
35
38
import { handleTakeOver } from '../../utilities/handleTakeOver.js'
36
39
import { Auth } from './Auth/index.js'
37
- import './index.scss'
38
40
import { SetDocumentStepNav } from './SetDocumentStepNav/index.js'
39
41
import { SetDocumentTitle } from './SetDocumentTitle/index.js'
42
+ import './index.scss'
40
43
41
44
const baseClass = 'collection-edit'
42
45
@@ -118,6 +121,7 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
118
121
const { getFormState } = useServerFunctions ( )
119
122
120
123
const onChangeAbortControllerRef = useRef < AbortController > ( null )
124
+ const onSaveAbortControllerRef = useRef < AbortController > ( null )
121
125
122
126
const locale = params . get ( 'locale' )
123
127
@@ -139,15 +143,18 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
139
143
const lockDurationInMilliseconds = lockDuration * 1000
140
144
141
145
let preventLeaveWithoutSaving = true
146
+ let autosaveEnabled = false
142
147
143
148
if ( collectionConfig ) {
144
- preventLeaveWithoutSaving = ! (
145
- collectionConfig ?. versions ?. drafts && collectionConfig ?. versions ?. drafts ?. autosave
149
+ autosaveEnabled = Boolean (
150
+ collectionConfig ?. versions ?. drafts && collectionConfig ?. versions ?. drafts ?. autosave ,
146
151
)
152
+ preventLeaveWithoutSaving = ! autosaveEnabled
147
153
} else if ( globalConfig ) {
148
- preventLeaveWithoutSaving = ! (
149
- globalConfig ?. versions ?. drafts && globalConfig ?. versions ?. drafts ?. autosave
154
+ autosaveEnabled = Boolean (
155
+ globalConfig ?. versions ?. drafts && globalConfig ?. versions ?. drafts ?. autosave ,
150
156
)
157
+ preventLeaveWithoutSaving = ! autosaveEnabled
151
158
} else if ( typeof disableLeaveWithoutSaving !== 'undefined' ) {
152
159
preventLeaveWithoutSaving = ! disableLeaveWithoutSaving
153
160
}
@@ -197,8 +204,40 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
197
204
return false
198
205
} )
199
206
207
+ const handleDocumentLocking = useCallback (
208
+ ( lockedState : LockedState ) => {
209
+ setDocumentIsLocked ( true )
210
+ const previousOwnerId =
211
+ typeof documentLockStateRef . current ?. user === 'object'
212
+ ? documentLockStateRef . current ?. user ?. id
213
+ : documentLockStateRef . current ?. user
214
+
215
+ if ( lockedState ) {
216
+ const lockedUserID =
217
+ typeof lockedState . user === 'string' || typeof lockedState . user === 'number'
218
+ ? lockedState . user
219
+ : lockedState . user . id
220
+
221
+ if ( ! documentLockStateRef . current || lockedUserID !== previousOwnerId ) {
222
+ if ( previousOwnerId === user . id && lockedUserID !== user . id ) {
223
+ setShowTakeOverModal ( true )
224
+ documentLockStateRef . current . hasShownLockedModal = true
225
+ }
226
+
227
+ documentLockStateRef . current = {
228
+ hasShownLockedModal : documentLockStateRef . current ?. hasShownLockedModal || false ,
229
+ isLocked : true ,
230
+ user : lockedState . user as ClientUser ,
231
+ }
232
+ setCurrentEditor ( lockedState . user as ClientUser )
233
+ }
234
+ }
235
+ } ,
236
+ [ setCurrentEditor , setDocumentIsLocked , user . id ] ,
237
+ )
238
+
200
239
const onSave = useCallback (
201
- async ( json ) => {
240
+ async ( json ) : Promise < FormState > => {
202
241
reportUpdate ( {
203
242
id,
204
243
entitySlug,
@@ -224,11 +263,6 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
224
263
} )
225
264
}
226
265
227
- // Unlock the document after save
228
- if ( ( id || globalSlug ) && isLockingEnabled ) {
229
- setDocumentIsLocked ( false )
230
- }
231
-
232
266
if ( ! isEditing && depth < 2 ) {
233
267
// Redirect to the same locale if it's been set
234
268
const redirectRoute = formatAdminURL ( {
@@ -241,28 +275,63 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
241
275
}
242
276
243
277
await getDocPermissions ( json )
278
+
279
+ if ( ( id || globalSlug ) && ! autosaveEnabled ) {
280
+ abortAndIgnore ( onSaveAbortControllerRef . current )
281
+ const controller = new AbortController ( )
282
+ onSaveAbortControllerRef . current = controller
283
+
284
+ const docPreferences = await getDocPreferences ( )
285
+
286
+ const { state } = await getFormState ( {
287
+ id,
288
+ collectionSlug,
289
+ data : json ?. doc ,
290
+ docPermissions,
291
+ docPreferences,
292
+ globalSlug,
293
+ operation,
294
+ renderAllFields : true ,
295
+ returnLockStatus : false ,
296
+ schemaPath : schemaPathSegments . join ( '.' ) ,
297
+ signal : controller . signal ,
298
+ } )
299
+
300
+ // Unlock the document after save
301
+ if ( isLockingEnabled ) {
302
+ setDocumentIsLocked ( false )
303
+ }
304
+
305
+ return state
306
+ }
244
307
} ,
245
308
[
246
- updateSavedDocumentData ,
247
- reportUpdate ,
248
- id ,
249
- entitySlug ,
250
- user ,
309
+ adminRoute ,
251
310
collectionSlug ,
252
- userSlug ,
253
- incrementVersionCount ,
254
- onSaveFromContext ,
255
- globalSlug ,
256
- isLockingEnabled ,
257
- isEditing ,
258
311
depth ,
312
+ docPermissions ,
313
+ entitySlug ,
259
314
getDocPermissions ,
260
- refreshCookieAsync ,
261
- setDocumentIsLocked ,
262
- adminRoute ,
315
+ getDocPreferences ,
316
+ getFormState ,
317
+ globalSlug ,
318
+ id ,
319
+ incrementVersionCount ,
320
+ isEditing ,
321
+ isLockingEnabled ,
263
322
locale ,
264
- router ,
323
+ onSaveFromContext ,
324
+ operation ,
325
+ refreshCookieAsync ,
326
+ reportUpdate ,
265
327
resetUploadEdits ,
328
+ router ,
329
+ schemaPathSegments ,
330
+ setDocumentIsLocked ,
331
+ updateSavedDocumentData ,
332
+ user ,
333
+ userSlug ,
334
+ autosaveEnabled ,
266
335
] ,
267
336
)
268
337
@@ -293,92 +362,54 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
293
362
globalSlug,
294
363
operation,
295
364
// Performance optimization: Setting it to false ensure that only fields that have explicit requireRender set in the form state will be rendered (e.g. new array rows).
296
- // We only wanna render ALL fields on initial render, not in onChange.
365
+ // We only want to render ALL fields on initial render, not in onChange.
297
366
renderAllFields : false ,
298
- returnLockStatus : isLockingEnabled ? true : false ,
367
+ returnLockStatus : isLockingEnabled ,
299
368
schemaPath : schemaPathSegments . join ( '.' ) ,
300
369
signal : controller . signal ,
301
370
updateLastEdited,
302
371
} )
303
372
304
- setDocumentIsLocked ( true )
305
-
306
373
if ( isLockingEnabled ) {
307
- const previousOwnerId =
308
- typeof documentLockStateRef . current ?. user === 'object'
309
- ? documentLockStateRef . current ?. user ?. id
310
- : documentLockStateRef . current ?. user
311
-
312
- if ( lockedState ) {
313
- const lockedUserID =
314
- typeof lockedState . user === 'string' || typeof lockedState . user === 'number'
315
- ? lockedState . user
316
- : lockedState . user . id
317
-
318
- if ( ! documentLockStateRef . current || lockedUserID !== previousOwnerId ) {
319
- if ( previousOwnerId === user . id && lockedUserID !== user . id ) {
320
- setShowTakeOverModal ( true )
321
- documentLockStateRef . current . hasShownLockedModal = true
322
- }
323
-
324
- documentLockStateRef . current = documentLockStateRef . current = {
325
- hasShownLockedModal : documentLockStateRef . current ?. hasShownLockedModal || false ,
326
- isLocked : true ,
327
- user : lockedState . user as ClientUser ,
328
- }
329
- setCurrentEditor ( lockedState . user as ClientUser )
330
- }
331
- }
374
+ handleDocumentLocking ( lockedState )
332
375
}
333
376
334
377
return state
335
378
} ,
336
379
[
337
- editSessionStartTime ,
338
- isLockingEnabled ,
339
- getDocPreferences ,
340
- getFormState ,
341
380
id ,
342
381
collectionSlug ,
343
- docPermissions ,
382
+ getDocPreferences ,
383
+ getFormState ,
344
384
globalSlug ,
385
+ handleDocumentLocking ,
386
+ isLockingEnabled ,
345
387
operation ,
346
388
schemaPathSegments ,
347
- setDocumentIsLocked ,
348
- user . id ,
349
- setCurrentEditor ,
389
+ docPermissions ,
390
+ editSessionStartTime ,
350
391
] ,
351
392
)
352
393
353
394
// Clean up when the component unmounts or when the document is unlocked
354
395
useEffect ( ( ) => {
355
396
return ( ) => {
356
- if ( ! isLockingEnabled ) {
357
- return
358
- }
359
-
360
- const currentPath = window . location . pathname
361
-
362
- const documentId = id || globalSlug
363
-
364
- // Routes where we do NOT want to unlock the document
365
- const stayWithinDocumentPaths = [ 'preview' , 'api' , 'versions' ]
366
-
367
- const isStayingWithinDocument = stayWithinDocumentPaths . some ( ( path ) =>
368
- currentPath . includes ( path ) ,
369
- )
370
-
371
- // Unlock the document only if we're actually navigating away from the document
372
- if ( documentId && documentIsLocked && ! isStayingWithinDocument ) {
373
- // Check if this user is still the current editor
374
- if (
375
- typeof documentLockStateRef . current ?. user === 'object'
376
- ? documentLockStateRef . current ?. user ?. id === user ?. id
377
- : documentLockStateRef . current ?. user === user ?. id
378
- ) {
379
- void unlockDocument ( id , collectionSlug ?? globalSlug )
380
- setDocumentIsLocked ( false )
381
- setCurrentEditor ( null )
397
+ if ( isLockingEnabled && documentIsLocked && ( id || globalSlug ) ) {
398
+ // Only retain the lock if the user is still viewing the document
399
+ const shouldUnlockDocument = ! [ 'preview' , 'api' , 'versions' ] . some ( ( path ) =>
400
+ window . location . pathname . includes ( path ) ,
401
+ )
402
+ if ( shouldUnlockDocument ) {
403
+ // Check if this user is still the current editor
404
+ if (
405
+ typeof documentLockStateRef . current ?. user === 'object'
406
+ ? documentLockStateRef . current ?. user ?. id === user ?. id
407
+ : documentLockStateRef . current ?. user === user ?. id
408
+ ) {
409
+ void unlockDocument ( id , collectionSlug ?? globalSlug )
410
+ setDocumentIsLocked ( false )
411
+ setCurrentEditor ( null )
412
+ }
382
413
}
383
414
}
384
415
@@ -399,6 +430,7 @@ export const DefaultEditView: React.FC<ClientSideEditViewProps> = ({
399
430
useEffect ( ( ) => {
400
431
return ( ) => {
401
432
abortAndIgnore ( onChangeAbortControllerRef . current )
433
+ abortAndIgnore ( onSaveAbortControllerRef . current )
402
434
}
403
435
} , [ ] )
404
436
0 commit comments