@wordpress/core-data 7.41.2-next.v.202603102151.0 → 7.42.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 +1 -1
- package/README.md +19 -0
- package/build/actions.cjs +17 -25
- package/build/actions.cjs.map +2 -2
- package/build/awareness/post-editor-awareness.cjs +46 -6
- package/build/awareness/post-editor-awareness.cjs.map +2 -2
- package/build/awareness/types.cjs.map +1 -1
- package/build/entities.cjs +33 -7
- package/build/entities.cjs.map +2 -2
- package/build/hooks/use-entity-prop.cjs +2 -1
- package/build/hooks/use-entity-prop.cjs.map +2 -2
- package/build/hooks/use-post-editor-awareness-state.cjs +84 -3
- package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
- package/build/index.cjs +3 -0
- package/build/index.cjs.map +2 -2
- package/build/private-apis.cjs +3 -1
- package/build/private-apis.cjs.map +2 -2
- package/build/queried-data/get-query-parts.cjs +7 -0
- package/build/queried-data/get-query-parts.cjs.map +2 -2
- package/build/queried-data/selectors.cjs +19 -5
- package/build/queried-data/selectors.cjs.map +2 -2
- package/build/reducer.cjs +6 -0
- package/build/reducer.cjs.map +2 -2
- package/build/resolvers.cjs +110 -74
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs +29 -0
- package/build/selectors.cjs.map +2 -2
- package/build/sync.cjs +3 -0
- package/build/sync.cjs.map +2 -2
- package/build/types.cjs +16 -0
- package/build/types.cjs.map +3 -3
- package/build/utils/block-selection-history.cjs +1 -1
- package/build/utils/block-selection-history.cjs.map +2 -2
- package/build/utils/crdt-blocks.cjs +17 -3
- package/build/utils/crdt-blocks.cjs.map +2 -2
- package/build/utils/crdt-selection.cjs +4 -1
- package/build/utils/crdt-selection.cjs.map +2 -2
- package/build/utils/crdt-user-selections.cjs +9 -6
- package/build/utils/crdt-user-selections.cjs.map +2 -2
- package/build/utils/crdt-utils.cjs +54 -2
- package/build/utils/crdt-utils.cjs.map +2 -2
- package/build/utils/crdt.cjs +4 -23
- package/build/utils/crdt.cjs.map +2 -2
- package/build/utils/index.cjs +3 -0
- package/build/utils/index.cjs.map +2 -2
- package/build/utils/normalize-query-for-resolution.cjs +35 -0
- package/build/utils/normalize-query-for-resolution.cjs.map +7 -0
- package/build-module/actions.mjs +17 -25
- package/build-module/actions.mjs.map +2 -2
- package/build-module/awareness/post-editor-awareness.mjs +46 -6
- package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
- package/build-module/entities.mjs +33 -7
- package/build-module/entities.mjs.map +2 -2
- package/build-module/hooks/use-entity-prop.mjs +2 -1
- package/build-module/hooks/use-entity-prop.mjs.map +2 -2
- package/build-module/hooks/use-post-editor-awareness-state.mjs +81 -2
- package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
- package/build-module/index.mjs +2 -0
- package/build-module/index.mjs.map +2 -2
- package/build-module/private-apis.mjs +6 -2
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/queried-data/get-query-parts.mjs +7 -0
- package/build-module/queried-data/get-query-parts.mjs.map +2 -2
- package/build-module/queried-data/selectors.mjs +19 -5
- package/build-module/queried-data/selectors.mjs.map +2 -2
- package/build-module/reducer.mjs +6 -0
- package/build-module/reducer.mjs.map +2 -2
- package/build-module/resolvers.mjs +112 -75
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs +28 -0
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/sync.mjs +2 -0
- package/build-module/sync.mjs.map +2 -2
- package/build-module/types.mjs +9 -0
- package/build-module/types.mjs.map +4 -4
- package/build-module/utils/block-selection-history.mjs +5 -2
- package/build-module/utils/block-selection-history.mjs.map +2 -2
- package/build-module/utils/crdt-blocks.mjs +17 -3
- package/build-module/utils/crdt-blocks.mjs.map +2 -2
- package/build-module/utils/crdt-selection.mjs +8 -2
- package/build-module/utils/crdt-selection.mjs.map +2 -2
- package/build-module/utils/crdt-user-selections.mjs +10 -7
- package/build-module/utils/crdt-user-selections.mjs.map +2 -2
- package/build-module/utils/crdt-utils.mjs +51 -1
- package/build-module/utils/crdt-utils.mjs.map +2 -2
- package/build-module/utils/crdt.mjs +4 -23
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-module/utils/index.mjs +2 -0
- package/build-module/utils/index.mjs.map +2 -2
- package/build-module/utils/normalize-query-for-resolution.mjs +14 -0
- package/build-module/utils/normalize-query-for-resolution.mjs.map +7 -0
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/awareness/post-editor-awareness.d.ts +2 -2
- package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
- package/build-types/awareness/types.d.ts +1 -1
- 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-entity-prop.d.ts.map +1 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts +34 -10
- package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
- package/build-types/index.d.ts +2 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/queried-data/get-query-parts.d.ts +7 -0
- package/build-types/queried-data/get-query-parts.d.ts.map +1 -1
- package/build-types/queried-data/selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +2 -1
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +17 -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 +18 -1
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/block-selection-history.d.ts.map +1 -1
- package/build-types/utils/crdt-blocks.d.ts.map +1 -1
- package/build-types/utils/crdt-selection.d.ts.map +1 -1
- package/build-types/utils/crdt-user-selections.d.ts +9 -5
- package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
- package/build-types/utils/crdt-utils.d.ts +20 -0
- package/build-types/utils/crdt-utils.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts +6 -7
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/build-types/utils/index.d.ts +1 -0
- package/build-types/utils/normalize-query-for-resolution.d.ts +12 -0
- package/build-types/utils/normalize-query-for-resolution.d.ts.map +1 -0
- package/build-types/utils/test/crdt-utils.d.ts +2 -0
- package/build-types/utils/test/crdt-utils.d.ts.map +1 -0
- package/package.json +18 -18
- package/src/actions.js +25 -40
- package/src/awareness/post-editor-awareness.ts +106 -7
- package/src/awareness/test/post-editor-awareness.ts +50 -10
- package/src/awareness/types.ts +1 -1
- package/src/entities.js +38 -6
- package/src/hooks/test/use-post-editor-awareness-state.ts +446 -3
- package/src/hooks/use-entity-prop.js +2 -0
- package/src/hooks/use-post-editor-awareness-state.ts +160 -8
- package/src/index.js +1 -0
- package/src/private-apis.js +6 -2
- package/src/queried-data/get-query-parts.js +13 -0
- package/src/queried-data/selectors.js +33 -8
- package/src/queried-data/test/get-query-parts.js +34 -0
- package/src/queried-data/test/selectors.js +183 -0
- package/src/reducer.js +11 -0
- package/src/resolvers.js +136 -88
- package/src/selectors.ts +56 -0
- package/src/sync.ts +2 -0
- package/src/test/entities.js +185 -1
- package/src/test/resolvers.js +64 -11
- package/src/test/selectors.js +150 -0
- package/src/test/store.js +66 -0
- package/src/types.ts +26 -1
- package/src/utils/block-selection-history.ts +5 -2
- package/src/utils/crdt-blocks.ts +32 -3
- package/src/utils/crdt-selection.ts +8 -2
- package/src/utils/crdt-user-selections.ts +20 -8
- package/src/utils/crdt-utils.ts +99 -0
- package/src/utils/crdt.ts +8 -32
- package/src/utils/index.js +1 -0
- package/src/utils/normalize-query-for-resolution.js +23 -0
- package/src/utils/test/crdt-blocks.ts +146 -0
- package/src/utils/test/crdt-user-selections.ts +5 -0
- package/src/utils/test/crdt-utils.ts +387 -0
- package/src/utils/test/crdt.ts +120 -53
package/src/actions.js
CHANGED
|
@@ -678,40 +678,25 @@ export const saveEntityRecord =
|
|
|
678
678
|
? select.getRawEntityRecord( kind, name, recordId )
|
|
679
679
|
: {};
|
|
680
680
|
|
|
681
|
+
// Most of this autosave logic is very specific to posts.
|
|
682
|
+
// This is fine for now as it is the only supported autosave,
|
|
683
|
+
// but ideally this should all be handled in the back end,
|
|
684
|
+
// so the client just sends and receives objects.
|
|
681
685
|
if ( isAutosave ) {
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
const
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
currentUserId
|
|
694
|
-
);
|
|
695
|
-
// Autosaves need all expected fields to be present.
|
|
696
|
-
// So we fallback to the previous autosave and then
|
|
697
|
-
// to the actual persisted entity if the edits don't
|
|
698
|
-
// have a value.
|
|
699
|
-
let data = {
|
|
700
|
-
...persistedRecord,
|
|
701
|
-
...autosavePost,
|
|
702
|
-
...record,
|
|
703
|
-
};
|
|
704
|
-
data = Object.keys( data ).reduce(
|
|
686
|
+
// Build the autosave payload from the persisted
|
|
687
|
+
// record and the incoming edits. The previous autosave
|
|
688
|
+
// is intentionally excluded to avoid stale values
|
|
689
|
+
// overriding reverted fields.
|
|
690
|
+
const merged = { ...persistedRecord, ...record };
|
|
691
|
+
const data = [
|
|
692
|
+
'title',
|
|
693
|
+
'excerpt',
|
|
694
|
+
'content',
|
|
695
|
+
'meta',
|
|
696
|
+
].reduce(
|
|
705
697
|
( acc, key ) => {
|
|
706
|
-
if (
|
|
707
|
-
[
|
|
708
|
-
'title',
|
|
709
|
-
'excerpt',
|
|
710
|
-
'content',
|
|
711
|
-
'meta',
|
|
712
|
-
].includes( key )
|
|
713
|
-
) {
|
|
714
|
-
acc[ key ] = data[ key ];
|
|
698
|
+
if ( key in merged ) {
|
|
699
|
+
acc[ key ] = merged[ key ];
|
|
715
700
|
}
|
|
716
701
|
return acc;
|
|
717
702
|
},
|
|
@@ -721,7 +706,7 @@ export const saveEntityRecord =
|
|
|
721
706
|
// because it can lead to unexpected results. An example would be to
|
|
722
707
|
// have a draft post and change the status to publish.
|
|
723
708
|
status:
|
|
724
|
-
|
|
709
|
+
merged.status === 'auto-draft'
|
|
725
710
|
? 'draft'
|
|
726
711
|
: undefined,
|
|
727
712
|
}
|
|
@@ -745,9 +730,12 @@ export const saveEntityRecord =
|
|
|
745
730
|
( acc, key ) => {
|
|
746
731
|
// These properties are persisted in autosaves.
|
|
747
732
|
if (
|
|
748
|
-
[
|
|
749
|
-
|
|
750
|
-
|
|
733
|
+
[
|
|
734
|
+
'title',
|
|
735
|
+
'excerpt',
|
|
736
|
+
'content',
|
|
737
|
+
'meta',
|
|
738
|
+
].includes( key )
|
|
751
739
|
) {
|
|
752
740
|
acc[ key ] = newRecord[ key ];
|
|
753
741
|
} else if ( key === 'status' ) {
|
|
@@ -1114,10 +1102,7 @@ export const receiveRevisions =
|
|
|
1114
1102
|
const entityConfig = configs.find(
|
|
1115
1103
|
( config ) => config.kind === kind && config.name === name
|
|
1116
1104
|
);
|
|
1117
|
-
const key =
|
|
1118
|
-
entityConfig && entityConfig?.revisionKey
|
|
1119
|
-
? entityConfig.revisionKey
|
|
1120
|
-
: DEFAULT_ENTITY_KEY;
|
|
1105
|
+
const key = entityConfig?.revisionKey ?? DEFAULT_ENTITY_KEY;
|
|
1121
1106
|
|
|
1122
1107
|
dispatch( {
|
|
1123
1108
|
type: 'RECEIVE_ITEM_REVISIONS',
|
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS,
|
|
17
17
|
} from './config';
|
|
18
18
|
import { STORE_NAME as coreStore } from '../name';
|
|
19
|
+
import { htmlIndexToRichTextOffset } from '../utils/crdt-utils';
|
|
19
20
|
import {
|
|
20
21
|
areSelectionsStatesEqual,
|
|
21
22
|
getSelectionState,
|
|
22
23
|
SelectionType,
|
|
23
24
|
} from '../utils/crdt-user-selections';
|
|
24
25
|
|
|
26
|
+
import { SelectionDirection } from '../types';
|
|
25
27
|
import type { SelectionState, WPBlockSelection } from '../types';
|
|
26
28
|
import type { YBlocks } from '../utils/crdt-blocks';
|
|
27
29
|
import type {
|
|
@@ -69,6 +71,18 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
69
71
|
let selectionEnd = getSelectionEnd();
|
|
70
72
|
let localCursorTimeout: NodeJS.Timeout | null = null;
|
|
71
73
|
|
|
74
|
+
// During rapid selection changes (e.g. undo restoring content and
|
|
75
|
+
// selection), the debounce discards intermediate events. If we use the
|
|
76
|
+
// last intermediate state instead of the overall change it can produce
|
|
77
|
+
// the wrong direction.
|
|
78
|
+
// Use selectionBeforeDebounce to capture the selection state from
|
|
79
|
+
// before the debounce window so that direction is computed across the
|
|
80
|
+
// full window when it fires.
|
|
81
|
+
let selectionBeforeDebounce: {
|
|
82
|
+
start: WPBlockSelection;
|
|
83
|
+
end: WPBlockSelection;
|
|
84
|
+
} | null = null;
|
|
85
|
+
|
|
72
86
|
subscribe( () => {
|
|
73
87
|
const newSelectionStart = getSelectionStart();
|
|
74
88
|
const newSelectionEnd = getSelectionEnd();
|
|
@@ -80,6 +94,15 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
80
94
|
return;
|
|
81
95
|
}
|
|
82
96
|
|
|
97
|
+
// On the first change of a debounce window, snapshot the state
|
|
98
|
+
// we're moving away from.
|
|
99
|
+
if ( ! selectionBeforeDebounce ) {
|
|
100
|
+
selectionBeforeDebounce = {
|
|
101
|
+
start: selectionStart,
|
|
102
|
+
end: selectionEnd,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
83
106
|
selectionStart = newSelectionStart;
|
|
84
107
|
selectionEnd = newSelectionEnd;
|
|
85
108
|
|
|
@@ -103,10 +126,29 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
localCursorTimeout = setTimeout( () => {
|
|
129
|
+
// Compute direction across the full debounce window.
|
|
130
|
+
const selectionStateOptions: {
|
|
131
|
+
selectionDirection?: SelectionDirection;
|
|
132
|
+
} = {};
|
|
133
|
+
|
|
134
|
+
if ( selectionBeforeDebounce ) {
|
|
135
|
+
selectionStateOptions.selectionDirection =
|
|
136
|
+
detectSelectionDirection(
|
|
137
|
+
selectionBeforeDebounce.start,
|
|
138
|
+
selectionBeforeDebounce.end,
|
|
139
|
+
selectionStart,
|
|
140
|
+
selectionEnd
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Reset debounced selection state.
|
|
144
|
+
selectionBeforeDebounce = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
106
147
|
const selectionState = getSelectionState(
|
|
107
148
|
selectionStart,
|
|
108
149
|
selectionEnd,
|
|
109
|
-
this.doc
|
|
150
|
+
this.doc,
|
|
151
|
+
selectionStateOptions
|
|
110
152
|
);
|
|
111
153
|
|
|
112
154
|
this.setThrottledLocalStateField(
|
|
@@ -171,6 +213,10 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
171
213
|
return state1 === state2;
|
|
172
214
|
}
|
|
173
215
|
|
|
216
|
+
if ( ! state1.selection || ! state2.selection ) {
|
|
217
|
+
return state1.selection === state2.selection;
|
|
218
|
+
}
|
|
219
|
+
|
|
174
220
|
return areSelectionsStatesEqual( state1.selection, state2.selection );
|
|
175
221
|
}
|
|
176
222
|
|
|
@@ -188,14 +234,14 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
188
234
|
* clientIds (e.g. in "Show Template" mode where blocks are cloned).
|
|
189
235
|
*
|
|
190
236
|
* @param selection - The selection state.
|
|
191
|
-
* @return The text
|
|
237
|
+
* @return The rich-text offset and block client ID, or nulls if not resolvable.
|
|
192
238
|
*/
|
|
193
239
|
public convertSelectionStateToAbsolute( selection: SelectionState ): {
|
|
194
|
-
|
|
240
|
+
richTextOffset: number | null;
|
|
195
241
|
localClientId: string | null;
|
|
196
242
|
} {
|
|
197
243
|
if ( selection.type === SelectionType.None ) {
|
|
198
|
-
return {
|
|
244
|
+
return { richTextOffset: null, localClientId: null };
|
|
199
245
|
}
|
|
200
246
|
|
|
201
247
|
if ( selection.type === SelectionType.WholeBlock ) {
|
|
@@ -218,7 +264,7 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
218
264
|
}
|
|
219
265
|
}
|
|
220
266
|
|
|
221
|
-
return {
|
|
267
|
+
return { richTextOffset: null, localClientId };
|
|
222
268
|
}
|
|
223
269
|
|
|
224
270
|
// Text-based selections: resolve cursor position and navigate up.
|
|
@@ -233,7 +279,7 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
233
279
|
);
|
|
234
280
|
|
|
235
281
|
if ( ! absolutePosition ) {
|
|
236
|
-
return {
|
|
282
|
+
return { richTextOffset: null, localClientId: null };
|
|
237
283
|
}
|
|
238
284
|
|
|
239
285
|
// Navigate up: Y.Text -> attributes Y.Map -> block Y.Map
|
|
@@ -242,7 +288,13 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
242
288
|
yType instanceof Y.Map ? getBlockPathInYdoc( yType ) : null;
|
|
243
289
|
const localClientId = path ? resolveBlockClientIdByPath( path ) : null;
|
|
244
290
|
|
|
245
|
-
return {
|
|
291
|
+
return {
|
|
292
|
+
richTextOffset: htmlIndexToRichTextOffset(
|
|
293
|
+
absolutePosition.type.toString(),
|
|
294
|
+
absolutePosition.index
|
|
295
|
+
),
|
|
296
|
+
localClientId,
|
|
297
|
+
};
|
|
246
298
|
}
|
|
247
299
|
|
|
248
300
|
/**
|
|
@@ -325,3 +377,50 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
325
377
|
};
|
|
326
378
|
}
|
|
327
379
|
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Detect the direction of a selection change by comparing old and new edges.
|
|
383
|
+
*
|
|
384
|
+
* When the user extends a selection backward (e.g. Shift+Left), the
|
|
385
|
+
* selectionStart edge moves while selectionEnd stays fixed, so the caret
|
|
386
|
+
* is at the start. The reverse is true for forward extension.
|
|
387
|
+
*
|
|
388
|
+
* @param prevStart - The previous selectionStart.
|
|
389
|
+
* @param prevEnd - The previous selectionEnd.
|
|
390
|
+
* @param newStart - The new selectionStart.
|
|
391
|
+
* @param newEnd - The new selectionEnd.
|
|
392
|
+
* @return The detected direction, defaulting to Forward when indeterminate.
|
|
393
|
+
*/
|
|
394
|
+
function detectSelectionDirection(
|
|
395
|
+
prevStart: WPBlockSelection,
|
|
396
|
+
prevEnd: WPBlockSelection,
|
|
397
|
+
newStart: WPBlockSelection,
|
|
398
|
+
newEnd: WPBlockSelection
|
|
399
|
+
): SelectionDirection {
|
|
400
|
+
const startMoved = ! areBlockSelectionsEqual( prevStart, newStart );
|
|
401
|
+
const endMoved = ! areBlockSelectionsEqual( prevEnd, newEnd );
|
|
402
|
+
|
|
403
|
+
if ( startMoved && ! endMoved ) {
|
|
404
|
+
return SelectionDirection.Backward;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return SelectionDirection.Forward;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Compare two WPBlockSelection objects by value.
|
|
412
|
+
*
|
|
413
|
+
* @param a - First selection.
|
|
414
|
+
* @param b - Second selection.
|
|
415
|
+
* @return True if all fields are equal.
|
|
416
|
+
*/
|
|
417
|
+
function areBlockSelectionsEqual(
|
|
418
|
+
a: WPBlockSelection,
|
|
419
|
+
b: WPBlockSelection
|
|
420
|
+
): boolean {
|
|
421
|
+
return (
|
|
422
|
+
a.clientId === b.clientId &&
|
|
423
|
+
a.attributeKey === b.attributeKey &&
|
|
424
|
+
a.offset === b.offset
|
|
425
|
+
);
|
|
426
|
+
}
|
|
@@ -23,6 +23,11 @@ jest.mock( '@wordpress/data', () => ( {
|
|
|
23
23
|
select: jest.fn(),
|
|
24
24
|
subscribe: jest.fn(),
|
|
25
25
|
resolveSelect: jest.fn(),
|
|
26
|
+
// Needed because @wordpress/rich-text initialises its store at import time.
|
|
27
|
+
combineReducers: jest.fn( () => jest.fn( () => ( {} ) ) ),
|
|
28
|
+
createReduxStore: jest.fn( () => ( {} ) ),
|
|
29
|
+
register: jest.fn(),
|
|
30
|
+
createSelector: ( selector: Function ) => selector,
|
|
26
31
|
} ) );
|
|
27
32
|
|
|
28
33
|
jest.mock( '@wordpress/block-editor', () => ( {
|
|
@@ -422,6 +427,41 @@ describe( 'PostEditorAwareness', () => {
|
|
|
422
427
|
// Callback should not be called for equal editor states
|
|
423
428
|
expect( callback ).not.toHaveBeenCalled();
|
|
424
429
|
} );
|
|
430
|
+
|
|
431
|
+
test( 'should not notify when editorState without selection is unchanged', () => {
|
|
432
|
+
const awareness = new PostEditorAwareness(
|
|
433
|
+
doc,
|
|
434
|
+
'postType',
|
|
435
|
+
'post',
|
|
436
|
+
123
|
|
437
|
+
);
|
|
438
|
+
awareness.setUp();
|
|
439
|
+
|
|
440
|
+
awareness.setLocalStateField( 'editorState', {} );
|
|
441
|
+
|
|
442
|
+
const callback = jest.fn();
|
|
443
|
+
awareness.onStateChange( callback );
|
|
444
|
+
|
|
445
|
+
awareness.emit( 'change', [
|
|
446
|
+
{
|
|
447
|
+
added: [],
|
|
448
|
+
updated: [ awareness.clientID ],
|
|
449
|
+
removed: [],
|
|
450
|
+
},
|
|
451
|
+
] );
|
|
452
|
+
callback.mockClear();
|
|
453
|
+
|
|
454
|
+
awareness.setLocalStateField( 'editorState', {} );
|
|
455
|
+
awareness.emit( 'change', [
|
|
456
|
+
{
|
|
457
|
+
added: [],
|
|
458
|
+
updated: [ awareness.clientID ],
|
|
459
|
+
removed: [],
|
|
460
|
+
},
|
|
461
|
+
] );
|
|
462
|
+
|
|
463
|
+
expect( callback ).not.toHaveBeenCalled();
|
|
464
|
+
} );
|
|
425
465
|
} );
|
|
426
466
|
|
|
427
467
|
describe( 'convertSelectionStateToAbsolute', () => {
|
|
@@ -456,7 +496,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
456
496
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
457
497
|
|
|
458
498
|
// Should return nulls when the relative position's type cannot be found
|
|
459
|
-
expect( result.
|
|
499
|
+
expect( result.richTextOffset ).toBeNull();
|
|
460
500
|
expect( result.localClientId ).toBeNull();
|
|
461
501
|
} );
|
|
462
502
|
|
|
@@ -494,7 +534,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
494
534
|
const result =
|
|
495
535
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
496
536
|
|
|
497
|
-
expect( result.
|
|
537
|
+
expect( result.richTextOffset ).toBe( 5 );
|
|
498
538
|
expect( result.localClientId ).toBe( 'block-1' );
|
|
499
539
|
} );
|
|
500
540
|
|
|
@@ -526,7 +566,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
526
566
|
const result =
|
|
527
567
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
528
568
|
|
|
529
|
-
expect( result.
|
|
569
|
+
expect( result.richTextOffset ).toBeNull();
|
|
530
570
|
expect( result.localClientId ).toBe( 'block-1' );
|
|
531
571
|
} );
|
|
532
572
|
} );
|
|
@@ -732,7 +772,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
732
772
|
const result =
|
|
733
773
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
734
774
|
|
|
735
|
-
expect( result.
|
|
775
|
+
expect( result.richTextOffset ).toBe( 2 );
|
|
736
776
|
expect( result.localClientId ).toBe( 'local-2' );
|
|
737
777
|
|
|
738
778
|
nestedDoc.destroy();
|
|
@@ -805,7 +845,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
805
845
|
const result =
|
|
806
846
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
807
847
|
|
|
808
|
-
expect( result.
|
|
848
|
+
expect( result.richTextOffset ).toBe( 5 );
|
|
809
849
|
expect( result.localClientId ).toBe( 'local-inner-1' );
|
|
810
850
|
|
|
811
851
|
nestedDoc.destroy();
|
|
@@ -860,7 +900,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
860
900
|
const result =
|
|
861
901
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
862
902
|
|
|
863
|
-
expect( result.
|
|
903
|
+
expect( result.richTextOffset ).toBeNull();
|
|
864
904
|
expect( result.localClientId ).toBe( 'local-img' );
|
|
865
905
|
|
|
866
906
|
nestedDoc.destroy();
|
|
@@ -954,7 +994,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
954
994
|
const result =
|
|
955
995
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
956
996
|
|
|
957
|
-
expect( result.
|
|
997
|
+
expect( result.richTextOffset ).toBe( 7 );
|
|
958
998
|
expect( result.localClientId ).toBe( 'local-deep-1' );
|
|
959
999
|
|
|
960
1000
|
nestedDoc.destroy();
|
|
@@ -1055,7 +1095,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
1055
1095
|
const result =
|
|
1056
1096
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
1057
1097
|
|
|
1058
|
-
expect( result.
|
|
1098
|
+
expect( result.richTextOffset ).toBe( 4 );
|
|
1059
1099
|
// Should resolve to the post-content inner block, not a template block
|
|
1060
1100
|
expect( result.localClientId ).toBe( 'local-para-1' );
|
|
1061
1101
|
// Verify getBlocks was called with the post-content clientId
|
|
@@ -1127,7 +1167,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
1127
1167
|
const result =
|
|
1128
1168
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
1129
1169
|
|
|
1130
|
-
expect( result.
|
|
1170
|
+
expect( result.richTextOffset ).toBeNull();
|
|
1131
1171
|
expect( result.localClientId ).toBe( 'local-img' );
|
|
1132
1172
|
|
|
1133
1173
|
templateDoc.destroy();
|
|
@@ -1176,7 +1216,7 @@ describe( 'PostEditorAwareness', () => {
|
|
|
1176
1216
|
const result =
|
|
1177
1217
|
awareness.convertSelectionStateToAbsolute( selection );
|
|
1178
1218
|
|
|
1179
|
-
expect( result.
|
|
1219
|
+
expect( result.richTextOffset ).toBe( 3 );
|
|
1180
1220
|
expect( result.localClientId ).toBe( 'local-para' );
|
|
1181
1221
|
|
|
1182
1222
|
normalDoc.destroy();
|
package/src/awareness/types.ts
CHANGED
package/src/entities.js
CHANGED
|
@@ -226,6 +226,7 @@ export const rootEntitiesConfig = [
|
|
|
226
226
|
baseURLParams: { context: 'view' },
|
|
227
227
|
plural: 'fontCollections',
|
|
228
228
|
key: 'slug',
|
|
229
|
+
supportsPagination: true,
|
|
229
230
|
},
|
|
230
231
|
{
|
|
231
232
|
label: __( 'Icons' ),
|
|
@@ -328,15 +329,42 @@ export const prePersistPostType = async (
|
|
|
328
329
|
* @return {Promise} Entities promise
|
|
329
330
|
*/
|
|
330
331
|
async function loadPostTypeEntities() {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
const postTypesPromise = apiFetch( { path: '/wp/v2/types?context=view' } );
|
|
333
|
+
const taxonomiesPromise = window._wpCollaborationEnabled
|
|
334
|
+
? apiFetch( { path: '/wp/v2/taxonomies?context=view' } )
|
|
335
|
+
: Promise.resolve( {} );
|
|
336
|
+
const [ postTypes, taxonomies ] = await Promise.all( [
|
|
337
|
+
postTypesPromise,
|
|
338
|
+
taxonomiesPromise,
|
|
339
|
+
] );
|
|
340
|
+
|
|
334
341
|
return Object.entries( postTypes ?? {} ).map( ( [ name, postType ] ) => {
|
|
335
342
|
const isTemplate = [ 'wp_template', 'wp_template_part' ].includes(
|
|
336
343
|
name
|
|
337
344
|
);
|
|
338
345
|
const namespace = postType?.rest_namespace ?? 'wp/v2';
|
|
339
346
|
|
|
347
|
+
const syncedProperties = new Set( [
|
|
348
|
+
'author',
|
|
349
|
+
'blocks',
|
|
350
|
+
'content',
|
|
351
|
+
'comment_status',
|
|
352
|
+
'date',
|
|
353
|
+
'excerpt',
|
|
354
|
+
'featured_media',
|
|
355
|
+
'format',
|
|
356
|
+
'meta',
|
|
357
|
+
'ping_status',
|
|
358
|
+
'slug',
|
|
359
|
+
'status',
|
|
360
|
+
'sticky',
|
|
361
|
+
'template',
|
|
362
|
+
'title',
|
|
363
|
+
...( postType.taxonomies
|
|
364
|
+
?.map( ( taxonomy ) => taxonomies?.[ taxonomy ]?.rest_base )
|
|
365
|
+
?.filter( Boolean ) ?? [] ),
|
|
366
|
+
] );
|
|
367
|
+
|
|
340
368
|
const entity = {
|
|
341
369
|
kind: 'postType',
|
|
342
370
|
baseURL: `/${ namespace }/${ postType.rest_base }`,
|
|
@@ -384,7 +412,7 @@ async function loadPostTypeEntities() {
|
|
|
384
412
|
* @return {void}
|
|
385
413
|
*/
|
|
386
414
|
applyChangesToCRDTDoc: ( crdtDoc, changes ) =>
|
|
387
|
-
applyPostChangesToCRDTDoc( crdtDoc, changes,
|
|
415
|
+
applyPostChangesToCRDTDoc( crdtDoc, changes, syncedProperties ),
|
|
388
416
|
|
|
389
417
|
/**
|
|
390
418
|
* Create the awareness instance for the entity's CRDT document.
|
|
@@ -408,7 +436,11 @@ async function loadPostTypeEntities() {
|
|
|
408
436
|
* @return {Partial< import('@wordpress/sync').ObjectData >} Changes to record
|
|
409
437
|
*/
|
|
410
438
|
getChangesFromCRDTDoc: ( crdtDoc, editedRecord ) =>
|
|
411
|
-
getPostChangesFromCRDTDoc(
|
|
439
|
+
getPostChangesFromCRDTDoc(
|
|
440
|
+
crdtDoc,
|
|
441
|
+
editedRecord,
|
|
442
|
+
syncedProperties
|
|
443
|
+
),
|
|
412
444
|
|
|
413
445
|
/**
|
|
414
446
|
* Extract changes from a CRDT document that can be used to update the
|
|
@@ -419,7 +451,7 @@ async function loadPostTypeEntities() {
|
|
|
419
451
|
*/
|
|
420
452
|
getPersistedCRDTDoc: ( record ) => {
|
|
421
453
|
return (
|
|
422
|
-
record?.meta[ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE ] ||
|
|
454
|
+
record?.meta?.[ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE ] ||
|
|
423
455
|
null
|
|
424
456
|
);
|
|
425
457
|
},
|