@wordpress/core-data 7.40.2-next.v.202602241322.0 → 7.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/build/actions.cjs +1 -1
- package/build/actions.cjs.map +2 -2
- package/build/awareness/types.cjs.map +1 -1
- package/build/entities.cjs +17 -10
- package/build/entities.cjs.map +2 -2
- package/build/hooks/use-post-editor-awareness-state.cjs +38 -0
- package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
- package/build/private-actions.cjs +7 -2
- package/build/private-actions.cjs.map +2 -2
- package/build/private-apis.cjs +4 -1
- package/build/private-apis.cjs.map +2 -2
- package/build/private-selectors.cjs +7 -2
- package/build/private-selectors.cjs.map +2 -2
- package/build/reducer.cjs +11 -1
- package/build/reducer.cjs.map +2 -2
- package/build/resolvers.cjs +15 -12
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs.map +2 -2
- package/build/sync.cjs +5 -5
- package/build/sync.cjs.map +1 -1
- package/build/types.cjs.map +1 -1
- package/build/utils/crdt-blocks.cjs +50 -31
- package/build/utils/crdt-blocks.cjs.map +2 -2
- package/build/utils/crdt-selection.cjs +46 -18
- package/build/utils/crdt-selection.cjs.map +2 -2
- package/build/utils/crdt.cjs +12 -1
- package/build/utils/crdt.cjs.map +2 -2
- package/build-module/actions.mjs +1 -1
- package/build-module/actions.mjs.map +2 -2
- package/build-module/entities.mjs +19 -11
- package/build-module/entities.mjs.map +2 -2
- package/build-module/hooks/use-post-editor-awareness-state.mjs +37 -0
- package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
- package/build-module/private-actions.mjs +5 -1
- package/build-module/private-actions.mjs.map +2 -2
- package/build-module/private-apis.mjs +6 -2
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/private-selectors.mjs +5 -1
- package/build-module/private-selectors.mjs.map +2 -2
- package/build-module/reducer.mjs +10 -1
- package/build-module/reducer.mjs.map +2 -2
- package/build-module/resolvers.mjs +15 -12
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/sync.mjs +3 -3
- package/build-module/sync.mjs.map +1 -1
- package/build-module/utils/crdt-blocks.mjs +50 -31
- package/build-module/utils/crdt-blocks.mjs.map +2 -2
- package/build-module/utils/crdt-selection.mjs +45 -18
- package/build-module/utils/crdt-selection.mjs.map +2 -2
- package/build-module/utils/crdt.mjs +16 -6
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-types/awareness/types.d.ts +5 -0
- package/build-types/awareness/types.d.ts.map +1 -1
- package/build-types/entities.d.ts +1 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts +10 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-actions.d.ts +1 -0
- package/build-types/private-actions.d.ts.map +1 -1
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +7 -0
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +15 -0
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +1 -0
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/sync.d.ts +2 -2
- package/build-types/sync.d.ts.map +1 -1
- package/build-types/types.d.ts +1 -0
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/crdt-blocks.d.ts +1 -1
- package/build-types/utils/crdt-blocks.d.ts.map +1 -1
- package/build-types/utils/crdt-selection.d.ts +10 -0
- package/build-types/utils/crdt-selection.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts +1 -0
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/package.json +18 -18
- package/src/actions.js +2 -2
- package/src/awareness/types.ts +6 -0
- package/src/entities.js +23 -11
- package/src/hooks/use-post-editor-awareness-state.ts +70 -0
- package/src/private-actions.js +13 -0
- package/src/private-apis.js +4 -0
- package/src/private-selectors.ts +10 -0
- package/src/reducer.js +21 -0
- package/src/resolvers.js +21 -15
- package/src/selectors.ts +1 -0
- package/src/sync.ts +2 -2
- package/src/test/entities.js +47 -14
- package/src/test/resolvers.js +46 -80
- package/src/types.ts +1 -0
- package/src/utils/crdt-blocks.ts +113 -47
- package/src/utils/crdt-selection.ts +84 -24
- package/src/utils/crdt.ts +23 -7
- package/src/utils/test/crdt-blocks.ts +938 -0
- package/src/utils/test/crdt.ts +136 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crdt-selection.d.ts","sourceRoot":"","sources":["../../src/utils/crdt-selection.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,OAAO,EAIN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAoB,WAAW,EAAE,MAAM,UAAU,CAAC;AAsB9D,wBAAgB,mBAAmB,CAAE,IAAI,EAAE,OAAO,GAAI,cAAc,EAAE,CAErE;AAED,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,WAAW,GACtB,IAAI,CAEN;
|
|
1
|
+
{"version":3,"file":"crdt-selection.d.ts","sourceRoot":"","sources":["../../src/utils/crdt-selection.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,OAAO,EAIN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAoB,WAAW,EAAE,MAAM,UAAU,CAAC;AAsB9D,wBAAgB,mBAAmB,CAAE,IAAI,EAAE,OAAO,GAAI,cAAc,EAAE,CAErE;AAED,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,WAAW,GACtB,IAAI,CAEN;AAiGD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC/B,gBAAgB,EAAE,cAAc,EAAE,EAClC,IAAI,EAAE,CAAC,CAAC,GAAG,GACT,IAAI,CA+DN;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,CAAC,CAAC,GAAG,EACX,gBAAgB,EAAE,cAAc,EAAE,GAChC,WAAW,GAAG,IAAI,CA+BpB"}
|
|
@@ -30,6 +30,7 @@ export interface YPostRecord extends YMapRecord {
|
|
|
30
30
|
template: string;
|
|
31
31
|
title: Y.Text;
|
|
32
32
|
}
|
|
33
|
+
export declare const POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE = "_crdt_document";
|
|
33
34
|
/**
|
|
34
35
|
* Given a set of local changes to a post record, apply those changes to the
|
|
35
36
|
* local Y.Doc.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crdt.d.ts","sourceRoot":"","sources":["../../src/utils/crdt.ts"],"names":[],"mappings":"AAUA,OAAO,EACN,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,CAAC,EACD,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAGN,KAAK,KAAK,EAEV,KAAK,OAAO,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"crdt.d.ts","sourceRoot":"","sources":["../../src/utils/crdt.ts"],"names":[],"mappings":"AAUA,OAAO,EACN,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,CAAC,EACD,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAGN,KAAK,KAAK,EAEV,KAAK,OAAO,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,OAAO,EAIN,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,MAAM,cAAc,CAAC;AAGtB,MAAM,MAAM,WAAW,GAAG,OAAO,CAAE,IAAI,CAAE,GAAG;IAC3C,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAE,SAAS,CAAE,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,IAAI,CAAE,SAAS,CAAE,GAAG,MAAM,CAAC;IACrC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,KAAK,CAAC,EAAE,IAAI,CAAE,OAAO,CAAE,GAAG,MAAM,CAAC;CACjC,CAAC;AAGF,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC9C,MAAM,EAAE,MAAM,CAAC;IAEf,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAE,UAAU,CAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC;CACd;AAED,eAAO,MAAM,sCAAsC,mBAAmB,CAAC;AA2DvE;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,IAAI,GACb,IAAI,CAoIN;AAMD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,OAAO,EACb,YAAY,EAAE,IAAI,EAClB,SAAS,EAAE,IAAI,GACb,WAAW,CAuIb;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAI/B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/core-data",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.41.0",
|
|
4
4
|
"description": "Access to and manipulation of core WordPress entities.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -49,22 +49,22 @@
|
|
|
49
49
|
"build-module/index.mjs"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@wordpress/api-fetch": "^7.
|
|
53
|
-
"@wordpress/block-editor": "^15.
|
|
54
|
-
"@wordpress/blocks": "^15.
|
|
55
|
-
"@wordpress/compose": "^7.
|
|
56
|
-
"@wordpress/data": "^10.
|
|
57
|
-
"@wordpress/deprecated": "^4.
|
|
58
|
-
"@wordpress/element": "^6.
|
|
59
|
-
"@wordpress/html-entities": "^4.
|
|
60
|
-
"@wordpress/i18n": "^6.
|
|
61
|
-
"@wordpress/is-shallow-equal": "^5.
|
|
62
|
-
"@wordpress/private-apis": "^1.
|
|
63
|
-
"@wordpress/rich-text": "^7.
|
|
64
|
-
"@wordpress/sync": "^1.
|
|
65
|
-
"@wordpress/undo-manager": "^1.
|
|
66
|
-
"@wordpress/url": "^4.
|
|
67
|
-
"@wordpress/warning": "^3.
|
|
52
|
+
"@wordpress/api-fetch": "^7.41.0",
|
|
53
|
+
"@wordpress/block-editor": "^15.14.0",
|
|
54
|
+
"@wordpress/blocks": "^15.14.0",
|
|
55
|
+
"@wordpress/compose": "^7.41.0",
|
|
56
|
+
"@wordpress/data": "^10.41.0",
|
|
57
|
+
"@wordpress/deprecated": "^4.41.0",
|
|
58
|
+
"@wordpress/element": "^6.41.0",
|
|
59
|
+
"@wordpress/html-entities": "^4.41.0",
|
|
60
|
+
"@wordpress/i18n": "^6.14.0",
|
|
61
|
+
"@wordpress/is-shallow-equal": "^5.41.0",
|
|
62
|
+
"@wordpress/private-apis": "^1.41.0",
|
|
63
|
+
"@wordpress/rich-text": "^7.41.0",
|
|
64
|
+
"@wordpress/sync": "^1.41.0",
|
|
65
|
+
"@wordpress/undo-manager": "^1.41.0",
|
|
66
|
+
"@wordpress/url": "^4.41.0",
|
|
67
|
+
"@wordpress/warning": "^3.41.0",
|
|
68
68
|
"change-case": "^4.1.2",
|
|
69
69
|
"equivalent-key-map": "^0.2.2",
|
|
70
70
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"publishConfig": {
|
|
85
85
|
"access": "public"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "8bfc179b9aed74c0a6dd6e8edf7a49e40e4f87cc"
|
|
88
88
|
}
|
package/src/actions.js
CHANGED
|
@@ -773,10 +773,10 @@ export const saveEntityRecord =
|
|
|
773
773
|
if ( entityConfig.__unstablePrePersist ) {
|
|
774
774
|
edits = {
|
|
775
775
|
...edits,
|
|
776
|
-
...entityConfig.__unstablePrePersist(
|
|
776
|
+
...( await entityConfig.__unstablePrePersist(
|
|
777
777
|
persistedRecord,
|
|
778
778
|
edits
|
|
779
|
-
),
|
|
779
|
+
) ),
|
|
780
780
|
};
|
|
781
781
|
}
|
|
782
782
|
updatedRecord = await __unstableFetch( {
|
package/src/awareness/types.ts
CHANGED
|
@@ -94,3 +94,9 @@ export type EqualityFieldCheck< State, FieldName extends keyof State > = (
|
|
|
94
94
|
value1?: State[ FieldName ],
|
|
95
95
|
value2?: State[ FieldName ]
|
|
96
96
|
) => boolean;
|
|
97
|
+
|
|
98
|
+
export interface PostSaveEvent {
|
|
99
|
+
savedAt: number;
|
|
100
|
+
savedByClientId: number;
|
|
101
|
+
postStatus: string | undefined;
|
|
102
|
+
}
|
package/src/entities.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
applyPostChangesToCRDTDoc,
|
|
20
20
|
defaultSyncConfig,
|
|
21
21
|
getPostChangesFromCRDTDoc,
|
|
22
|
+
POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE,
|
|
22
23
|
} from './utils/crdt';
|
|
23
24
|
|
|
24
25
|
export const DEFAULT_ENTITY_KEY = 'id';
|
|
@@ -274,9 +275,9 @@ export const additionalEntityConfigLoaders = [
|
|
|
274
275
|
* @param {Object} edits Edits.
|
|
275
276
|
* @param {string} name Post type name.
|
|
276
277
|
* @param {boolean} isTemplate Whether the post type is a template.
|
|
277
|
-
* @return {Object} Updated edits.
|
|
278
|
+
* @return {Promise< Object >} Updated edits.
|
|
278
279
|
*/
|
|
279
|
-
export const prePersistPostType = (
|
|
280
|
+
export const prePersistPostType = async (
|
|
280
281
|
persistedRecord,
|
|
281
282
|
edits,
|
|
282
283
|
name,
|
|
@@ -305,11 +306,17 @@ export const prePersistPostType = (
|
|
|
305
306
|
if ( persistedRecord ) {
|
|
306
307
|
const objectType = `postType/${ name }`;
|
|
307
308
|
const objectId = persistedRecord.id;
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
const serializedDoc = await getSyncManager()?.createPersistedCRDTDoc(
|
|
310
|
+
objectType,
|
|
311
|
+
objectId
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if ( serializedDoc ) {
|
|
315
|
+
newEdits.meta = {
|
|
316
|
+
...edits.meta,
|
|
317
|
+
[ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE ]: serializedDoc,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
313
320
|
}
|
|
314
321
|
|
|
315
322
|
return newEdits;
|
|
@@ -404,12 +411,17 @@ async function loadPostTypeEntities() {
|
|
|
404
411
|
getPostChangesFromCRDTDoc( crdtDoc, editedRecord, postType ),
|
|
405
412
|
|
|
406
413
|
/**
|
|
407
|
-
*
|
|
414
|
+
* Extract changes from a CRDT document that can be used to update the
|
|
415
|
+
* local editor state.
|
|
408
416
|
*
|
|
409
|
-
* @
|
|
417
|
+
* @param {import('@wordpress/sync').ObjectData} record
|
|
418
|
+
* @return {Partial< import('@wordpress/sync').ObjectData >} Changes to record
|
|
410
419
|
*/
|
|
411
|
-
|
|
412
|
-
|
|
420
|
+
getPersistedCRDTDoc: ( record ) => {
|
|
421
|
+
return (
|
|
422
|
+
record?.meta[ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE ] ||
|
|
423
|
+
null
|
|
424
|
+
);
|
|
413
425
|
},
|
|
414
426
|
};
|
|
415
427
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { useEffect, useState } from '@wordpress/element';
|
|
5
|
+
import type { Y } from '@wordpress/sync';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Internal dependencies
|
|
@@ -9,6 +10,7 @@ import { useEffect, useState } from '@wordpress/element';
|
|
|
9
10
|
import { getSyncManager } from '../sync';
|
|
10
11
|
import type {
|
|
11
12
|
PostEditorAwarenessState as ActiveCollaborator,
|
|
13
|
+
PostSaveEvent,
|
|
12
14
|
YDocDebugData,
|
|
13
15
|
} from '../awareness/types';
|
|
14
16
|
import type { SelectionState } from '../types';
|
|
@@ -156,3 +158,71 @@ export function useIsDisconnected(
|
|
|
156
158
|
return usePostEditorAwarenessState( postId, postType )
|
|
157
159
|
.isCurrentCollaboratorDisconnected;
|
|
158
160
|
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Hook that subscribes to the CRDT state map and returns the most recent
|
|
164
|
+
* save event (timestamp + client ID). The state map is updated by
|
|
165
|
+
* `markEntityAsSaved` in `@wordpress/sync`
|
|
166
|
+
*
|
|
167
|
+
* @param postId The ID of the post.
|
|
168
|
+
* @param postType The type of the post.
|
|
169
|
+
*/
|
|
170
|
+
export function useLastPostSave(
|
|
171
|
+
postId: number | null,
|
|
172
|
+
postType: string | null
|
|
173
|
+
): PostSaveEvent | null {
|
|
174
|
+
const [ lastSave, setLastSave ] = useState< PostSaveEvent | null >( null );
|
|
175
|
+
|
|
176
|
+
useEffect( () => {
|
|
177
|
+
if ( null === postId || null === postType ) {
|
|
178
|
+
setLastSave( null );
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const awareness = getSyncManager()?.getAwareness< PostEditorAwareness >(
|
|
183
|
+
`postType/${ postType }`,
|
|
184
|
+
postId.toString()
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if ( ! awareness ) {
|
|
188
|
+
setLastSave( null );
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
awareness.setUp();
|
|
193
|
+
|
|
194
|
+
const stateMap = awareness.doc.getMap( 'state' );
|
|
195
|
+
const recordMap = awareness.doc.getMap( 'document' );
|
|
196
|
+
|
|
197
|
+
// Only notify for saves that occur after the observer is
|
|
198
|
+
// set up. This prevents false notifications when the Y.Doc
|
|
199
|
+
// syncs historical state on page load or peer reconnect.
|
|
200
|
+
const setupTime = Date.now();
|
|
201
|
+
|
|
202
|
+
const observer = ( event: Y.YMapEvent< unknown > ) => {
|
|
203
|
+
if ( event.keysChanged.has( 'savedAt' ) ) {
|
|
204
|
+
const savedAt = stateMap.get( 'savedAt' ) as number;
|
|
205
|
+
const savedByClientId = stateMap.get( 'savedBy' ) as number;
|
|
206
|
+
|
|
207
|
+
if (
|
|
208
|
+
typeof savedAt === 'number' &&
|
|
209
|
+
typeof savedByClientId === 'number' &&
|
|
210
|
+
savedAt > setupTime
|
|
211
|
+
) {
|
|
212
|
+
const postStatus = recordMap.get( 'status' ) as
|
|
213
|
+
| string
|
|
214
|
+
| undefined;
|
|
215
|
+
setLastSave( { savedAt, savedByClientId, postStatus } );
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
stateMap.observe( observer );
|
|
221
|
+
|
|
222
|
+
return () => {
|
|
223
|
+
stateMap.unobserve( observer );
|
|
224
|
+
};
|
|
225
|
+
}, [ postId, postType ] );
|
|
226
|
+
|
|
227
|
+
return lastSave;
|
|
228
|
+
}
|
package/src/private-actions.js
CHANGED
|
@@ -160,3 +160,16 @@ export function receiveEditorAssets( assets ) {
|
|
|
160
160
|
assets,
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Returns an action object used to set whether collaboration is supported.
|
|
166
|
+
*
|
|
167
|
+
* @param {boolean} supported Whether collaboration is supported.
|
|
168
|
+
*
|
|
169
|
+
* @return {Object} Action object.
|
|
170
|
+
*/
|
|
171
|
+
export const setCollaborationSupported =
|
|
172
|
+
( supported ) =>
|
|
173
|
+
( { dispatch } ) => {
|
|
174
|
+
dispatch( { type: 'SET_COLLABORATION_SUPPORTED', supported } );
|
|
175
|
+
};
|
package/src/private-apis.js
CHANGED
|
@@ -6,13 +6,17 @@ import { RECEIVE_INTERMEDIATE_RESULTS } from './utils';
|
|
|
6
6
|
import {
|
|
7
7
|
useActiveCollaborators,
|
|
8
8
|
useResolvedSelection,
|
|
9
|
+
useLastPostSave,
|
|
9
10
|
} from './hooks/use-post-editor-awareness-state';
|
|
10
11
|
import { lock } from './lock-unlock';
|
|
12
|
+
import { retrySyncConnection } from './sync';
|
|
11
13
|
|
|
12
14
|
export const privateApis = {};
|
|
13
15
|
lock( privateApis, {
|
|
14
16
|
useEntityRecordsWithPermissions,
|
|
15
17
|
RECEIVE_INTERMEDIATE_RESULTS,
|
|
18
|
+
retrySyncConnection,
|
|
16
19
|
useActiveCollaborators,
|
|
17
20
|
useResolvedSelection,
|
|
21
|
+
useLastPostSave,
|
|
18
22
|
} );
|
package/src/private-selectors.ts
CHANGED
|
@@ -304,3 +304,13 @@ export function getEditorSettings(
|
|
|
304
304
|
export function getEditorAssets( state: State ): Record< string, any > | null {
|
|
305
305
|
return state.editorAssets;
|
|
306
306
|
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Returns whether collaboration is supported.
|
|
310
|
+
*
|
|
311
|
+
* @param state Data state.
|
|
312
|
+
* @return Whether collaboration is supported.
|
|
313
|
+
*/
|
|
314
|
+
export function isCollaborationSupported( state: State ): boolean {
|
|
315
|
+
return state.collaborationSupported;
|
|
316
|
+
}
|
package/src/reducer.js
CHANGED
|
@@ -687,6 +687,26 @@ export function syncConnectionStatuses( state = {}, action ) {
|
|
|
687
687
|
return state;
|
|
688
688
|
}
|
|
689
689
|
|
|
690
|
+
/**
|
|
691
|
+
* Reducer managing whether collaboration is supported.
|
|
692
|
+
*
|
|
693
|
+
* Default to true, as collaboration is supported by default
|
|
694
|
+
* unless explicitly disabled due to unsupported conditions
|
|
695
|
+
* such as metaboxes.
|
|
696
|
+
*
|
|
697
|
+
* @param {boolean} state Current state.
|
|
698
|
+
* @param {Object} action Dispatched action.
|
|
699
|
+
*
|
|
700
|
+
* @return {boolean} Updated state.
|
|
701
|
+
*/
|
|
702
|
+
export function collaborationSupported( state = true, action ) {
|
|
703
|
+
switch ( action.type ) {
|
|
704
|
+
case 'SET_COLLABORATION_SUPPORTED':
|
|
705
|
+
return action.supported;
|
|
706
|
+
}
|
|
707
|
+
return state;
|
|
708
|
+
}
|
|
709
|
+
|
|
690
710
|
export default combineReducers( {
|
|
691
711
|
users,
|
|
692
712
|
currentTheme,
|
|
@@ -710,4 +730,5 @@ export default combineReducers( {
|
|
|
710
730
|
editorSettings,
|
|
711
731
|
editorAssets,
|
|
712
732
|
syncConnectionStatuses,
|
|
733
|
+
collaborationSupported,
|
|
713
734
|
} );
|
package/src/resolvers.js
CHANGED
|
@@ -235,6 +235,12 @@ export const getEntityRecord =
|
|
|
235
235
|
resolveSelect
|
|
236
236
|
.getEditedEntityRecord( kind, name, key )
|
|
237
237
|
.then( ( editedRecord ) => {
|
|
238
|
+
// Don't trigger a save if the record is still an auto-draft.
|
|
239
|
+
const { status } = editedRecord;
|
|
240
|
+
if ( 'auto-draft' === status ) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
238
244
|
dispatch.saveEntityRecord(
|
|
239
245
|
kind,
|
|
240
246
|
name,
|
|
@@ -707,21 +713,21 @@ export const canUser =
|
|
|
707
713
|
const permissions = getUserPermissionsFromAllowHeader(
|
|
708
714
|
response.headers?.get( 'allow' )
|
|
709
715
|
);
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
resource,
|
|
721
|
-
id,
|
|
722
|
-
] );
|
|
723
|
-
}
|
|
716
|
+
const receiveUserPermissionArgs = {};
|
|
717
|
+
const canUserResolutionsArgs = [];
|
|
718
|
+
for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
|
|
719
|
+
receiveUserPermissionArgs[
|
|
720
|
+
getUserPermissionCacheKey( action, resource, id )
|
|
721
|
+
] = permissions[ action ];
|
|
722
|
+
|
|
723
|
+
// Mark related action resolutions as finished.
|
|
724
|
+
if ( action !== requestedAction ) {
|
|
725
|
+
canUserResolutionsArgs.push( [ action, resource, id ] );
|
|
724
726
|
}
|
|
727
|
+
}
|
|
728
|
+
registry.batch( () => {
|
|
729
|
+
dispatch.receiveUserPermissions( receiveUserPermissionArgs );
|
|
730
|
+
dispatch.finishResolutions( 'canUser', canUserResolutionsArgs );
|
|
725
731
|
} );
|
|
726
732
|
};
|
|
727
733
|
|
|
@@ -1099,7 +1105,7 @@ export const getRevisions =
|
|
|
1099
1105
|
// When requesting all fields, the list of results can be used to
|
|
1100
1106
|
// resolve the `getRevision` selector in addition to `getRevisions`.
|
|
1101
1107
|
if ( ! query?._fields && ! query.context ) {
|
|
1102
|
-
const key = entityConfig.
|
|
1108
|
+
const key = entityConfig.revisionKey || DEFAULT_ENTITY_KEY;
|
|
1103
1109
|
const resolutionsArgs = records
|
|
1104
1110
|
.filter( ( record ) => record[ key ] )
|
|
1105
1111
|
.map( ( record ) => [
|
package/src/selectors.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface State {
|
|
|
54
54
|
editorSettings: Record< string, any > | null;
|
|
55
55
|
editorAssets: Record< string, any > | null;
|
|
56
56
|
syncConnectionStatuses?: Record< string, ConnectionStatus >;
|
|
57
|
+
collaborationSupported: boolean;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
type EntityRecordKey = string | number;
|
package/src/sync.ts
CHANGED
|
@@ -17,7 +17,7 @@ const {
|
|
|
17
17
|
CRDT_DOC_META_PERSISTENCE_KEY,
|
|
18
18
|
CRDT_RECORD_MAP_KEY,
|
|
19
19
|
LOCAL_EDITOR_ORIGIN,
|
|
20
|
-
|
|
20
|
+
retrySyncConnection,
|
|
21
21
|
} = unlock( syncPrivateApis );
|
|
22
22
|
|
|
23
23
|
export {
|
|
@@ -25,7 +25,7 @@ export {
|
|
|
25
25
|
CRDT_DOC_META_PERSISTENCE_KEY,
|
|
26
26
|
CRDT_RECORD_MAP_KEY,
|
|
27
27
|
LOCAL_EDITOR_ORIGIN,
|
|
28
|
-
|
|
28
|
+
retrySyncConnection,
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
let syncManager: SyncManager;
|
package/src/test/entities.js
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
import apiFetch from '@wordpress/api-fetch';
|
|
5
5
|
|
|
6
6
|
jest.mock( '@wordpress/api-fetch' );
|
|
7
|
+
jest.mock( '../sync', () => ( {
|
|
8
|
+
...jest.requireActual( '../sync' ),
|
|
9
|
+
getSyncManager: jest.fn(),
|
|
10
|
+
} ) );
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* Internal dependencies
|
|
@@ -14,6 +18,8 @@ import {
|
|
|
14
18
|
prePersistPostType,
|
|
15
19
|
additionalEntityConfigLoaders,
|
|
16
20
|
} from '../entities';
|
|
21
|
+
import { getSyncManager } from '../sync';
|
|
22
|
+
import { POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE } from '../utils/crdt';
|
|
17
23
|
|
|
18
24
|
describe( 'getMethodName', () => {
|
|
19
25
|
it( 'should return the right method name for an entity with the root kind', () => {
|
|
@@ -45,52 +51,79 @@ describe( 'getMethodName', () => {
|
|
|
45
51
|
} );
|
|
46
52
|
|
|
47
53
|
describe( 'prePersistPostType', () => {
|
|
48
|
-
it( 'set the status to draft and empty the title when saving auto-draft posts', () => {
|
|
54
|
+
it( 'set the status to draft and empty the title when saving auto-draft posts', async () => {
|
|
49
55
|
let record = {
|
|
50
56
|
status: 'auto-draft',
|
|
51
57
|
};
|
|
52
58
|
const edits = {};
|
|
53
|
-
expect(
|
|
59
|
+
expect(
|
|
60
|
+
await prePersistPostType( record, edits, 'post', false )
|
|
61
|
+
).toEqual( {
|
|
54
62
|
status: 'draft',
|
|
55
63
|
title: '',
|
|
56
|
-
meta: {},
|
|
57
64
|
} );
|
|
58
65
|
|
|
59
66
|
record = {
|
|
60
67
|
status: 'publish',
|
|
61
68
|
};
|
|
62
|
-
expect(
|
|
63
|
-
|
|
64
|
-
} );
|
|
69
|
+
expect(
|
|
70
|
+
await prePersistPostType( record, edits, 'post', false )
|
|
71
|
+
).toEqual( {} );
|
|
65
72
|
|
|
66
73
|
record = {
|
|
67
74
|
status: 'auto-draft',
|
|
68
75
|
title: 'Auto Draft',
|
|
69
76
|
};
|
|
70
|
-
expect(
|
|
77
|
+
expect(
|
|
78
|
+
await prePersistPostType( record, edits, 'post', false )
|
|
79
|
+
).toEqual( {
|
|
71
80
|
status: 'draft',
|
|
72
81
|
title: '',
|
|
73
|
-
meta: {},
|
|
74
82
|
} );
|
|
75
83
|
|
|
76
84
|
record = {
|
|
77
85
|
status: 'publish',
|
|
78
86
|
title: 'My Title',
|
|
79
87
|
};
|
|
80
|
-
expect(
|
|
81
|
-
|
|
82
|
-
} );
|
|
88
|
+
expect(
|
|
89
|
+
await prePersistPostType( record, edits, 'post', false )
|
|
90
|
+
).toEqual( {} );
|
|
83
91
|
} );
|
|
84
92
|
|
|
85
|
-
it( 'does not set the status to draft and empty the title when saving templates', () => {
|
|
93
|
+
it( 'does not set the status to draft and empty the title when saving templates', async () => {
|
|
86
94
|
const record = {
|
|
87
95
|
status: 'auto-draft',
|
|
88
96
|
title: 'Auto Draft',
|
|
89
97
|
};
|
|
90
98
|
const edits = {};
|
|
91
|
-
expect(
|
|
92
|
-
|
|
99
|
+
expect(
|
|
100
|
+
await prePersistPostType( record, edits, 'post', true )
|
|
101
|
+
).toEqual( {} );
|
|
102
|
+
} );
|
|
103
|
+
|
|
104
|
+
it( 'adds meta with serialized CRDT doc when createPersistedCRDTDoc returns a value', async () => {
|
|
105
|
+
const mockSerializedDoc = 'serialized-crdt-doc-data';
|
|
106
|
+
getSyncManager.mockReturnValue( {
|
|
107
|
+
createPersistedCRDTDoc: jest
|
|
108
|
+
.fn()
|
|
109
|
+
.mockReturnValue( mockSerializedDoc ),
|
|
93
110
|
} );
|
|
111
|
+
|
|
112
|
+
const record = { id: 123, status: 'publish' };
|
|
113
|
+
const edits = {};
|
|
114
|
+
const result = await prePersistPostType( record, edits, 'post', false );
|
|
115
|
+
|
|
116
|
+
expect( result.meta ).toEqual( {
|
|
117
|
+
[ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE ]: mockSerializedDoc,
|
|
118
|
+
} );
|
|
119
|
+
|
|
120
|
+
expect( getSyncManager ).toHaveBeenCalled();
|
|
121
|
+
expect( getSyncManager().createPersistedCRDTDoc ).toHaveBeenCalledWith(
|
|
122
|
+
'postType/post',
|
|
123
|
+
123
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
getSyncManager.mockReset();
|
|
94
127
|
} );
|
|
95
128
|
} );
|
|
96
129
|
|