@wordpress/core-data 6.23.0 → 6.25.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 +5 -0
- package/README.md +51 -2
- package/build/actions.js +38 -0
- package/build/actions.js.map +1 -1
- package/build/entities.js +7 -3
- package/build/entities.js.map +1 -1
- package/build/entity-types/global-styles-revision.js +6 -0
- package/build/entity-types/global-styles-revision.js.map +1 -0
- package/build/entity-types/index.js.map +1 -1
- package/build/entity-types/post-revision.js +6 -0
- package/build/entity-types/post-revision.js.map +1 -0
- package/build/footnotes/get-footnotes-order.js +2 -9
- package/build/footnotes/get-footnotes-order.js.map +1 -1
- package/build/footnotes/index.js +5 -10
- package/build/footnotes/index.js.map +1 -1
- package/build/hooks/use-entity-record.js +16 -5
- package/build/hooks/use-entity-record.js.map +1 -1
- package/build/queried-data/reducer.js +1 -1
- package/build/queried-data/reducer.js.map +1 -1
- package/build/reducer.js +26 -2
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +133 -3
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +79 -1
- package/build/selectors.js.map +1 -1
- package/build-module/actions.js +36 -0
- package/build-module/actions.js.map +1 -1
- package/build-module/entities.js +7 -3
- package/build-module/entities.js.map +1 -1
- package/build-module/entity-types/global-styles-revision.js +2 -0
- package/build-module/entity-types/global-styles-revision.js.map +1 -0
- package/build-module/entity-types/index.js.map +1 -1
- package/build-module/entity-types/post-revision.js +2 -0
- package/build-module/entity-types/post-revision.js.map +1 -0
- package/build-module/footnotes/get-footnotes-order.js +2 -9
- package/build-module/footnotes/get-footnotes-order.js.map +1 -1
- package/build-module/footnotes/index.js +6 -11
- package/build-module/footnotes/index.js.map +1 -1
- package/build-module/hooks/use-entity-record.js +16 -5
- package/build-module/hooks/use-entity-record.js.map +1 -1
- package/build-module/queried-data/reducer.js +1 -1
- package/build-module/queried-data/reducer.js.map +1 -1
- package/build-module/reducer.js +26 -2
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +130 -2
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +76 -1
- package/build-module/selectors.js.map +1 -1
- package/build-types/actions.d.ts +5 -0
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/entities.d.ts +11 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/entity-types/global-styles-revision.d.ts +43 -0
- package/build-types/entity-types/global-styles-revision.d.ts.map +1 -0
- package/build-types/entity-types/index.d.ts +4 -2
- package/build-types/entity-types/index.d.ts.map +1 -1
- package/build-types/entity-types/post-revision.d.ts +76 -0
- package/build-types/entity-types/post-revision.d.ts.map +1 -0
- package/build-types/footnotes/get-footnotes-order.d.ts.map +1 -1
- package/build-types/footnotes/index.d.ts.map +1 -1
- package/build-types/hooks/use-entity-record.d.ts.map +1 -1
- package/build-types/index.d.ts +13 -4
- package/build-types/index.d.ts.map +1 -1
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +9 -0
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +39 -3
- package/build-types/selectors.d.ts.map +1 -1
- package/package.json +17 -17
- package/src/actions.js +45 -0
- package/src/entities.js +13 -1
- package/src/entity-types/global-styles-revision.ts +47 -0
- package/src/entity-types/index.ts +6 -0
- package/src/entity-types/post-revision.ts +93 -0
- package/src/footnotes/get-footnotes-order.js +5 -12
- package/src/footnotes/index.js +14 -8
- package/src/hooks/test/use-entity-record.js +41 -0
- package/src/hooks/use-entity-record.ts +30 -18
- package/src/queried-data/reducer.js +1 -1
- package/src/reducer.js +33 -2
- package/src/resolvers.js +182 -2
- package/src/selectors.ts +119 -1
- package/src/test/entities.js +9 -0
- package/src/test/reducer.js +235 -0
- package/src/test/selectors.js +96 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/footnotes/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import { create, toHTMLString } from '@wordpress/rich-text';
|
|
4
|
+
import { RichTextData, create, toHTMLString } from '@wordpress/rich-text';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal dependencies
|
|
@@ -53,15 +53,18 @@ export function updateFootnotesFromMeta( blocks, meta ) {
|
|
|
53
53
|
continue;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
// To do, remove support for string values?
|
|
57
|
+
if (
|
|
58
|
+
typeof value !== 'string' &&
|
|
59
|
+
! ( value instanceof RichTextData )
|
|
60
|
+
) {
|
|
57
61
|
continue;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const richTextValue = create( { html: value } );
|
|
64
|
+
const richTextValue =
|
|
65
|
+
typeof value === 'string'
|
|
66
|
+
? RichTextData.fromHTMLString( value )
|
|
67
|
+
: value;
|
|
65
68
|
|
|
66
69
|
richTextValue.replacements.forEach( ( replacement ) => {
|
|
67
70
|
if ( replacement.type === 'core/footnote' ) {
|
|
@@ -78,7 +81,10 @@ export function updateFootnotesFromMeta( blocks, meta ) {
|
|
|
78
81
|
}
|
|
79
82
|
} );
|
|
80
83
|
|
|
81
|
-
attributes[ key ] =
|
|
84
|
+
attributes[ key ] =
|
|
85
|
+
typeof value === 'string'
|
|
86
|
+
? richTextValue.toHTMLString()
|
|
87
|
+
: richTextValue;
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
return attributes;
|
|
@@ -23,6 +23,7 @@ describe( 'useEntityRecord', () => {
|
|
|
23
23
|
beforeEach( () => {
|
|
24
24
|
registry = createRegistry();
|
|
25
25
|
registry.register( coreDataStore );
|
|
26
|
+
triggerFetch.mockReset();
|
|
26
27
|
} );
|
|
27
28
|
|
|
28
29
|
const TEST_RECORD = { id: 1, hello: 'world' };
|
|
@@ -113,4 +114,44 @@ describe( 'useEntityRecord', () => {
|
|
|
113
114
|
expect( widget.editedRecord ).toEqual( { hello: 'foo', id: 1 } );
|
|
114
115
|
expect( widget.edits ).toEqual( { hello: 'foo' } );
|
|
115
116
|
} );
|
|
117
|
+
|
|
118
|
+
it( 'does not resolve entity record when disabled via options', async () => {
|
|
119
|
+
triggerFetch.mockImplementation( () => TEST_RECORD );
|
|
120
|
+
|
|
121
|
+
let data;
|
|
122
|
+
const TestComponent = ( { enabled } ) => {
|
|
123
|
+
data = useEntityRecord( 'root', 'widget', 1, { enabled } );
|
|
124
|
+
return <div />;
|
|
125
|
+
};
|
|
126
|
+
const UI = ( { enabled } ) => (
|
|
127
|
+
<RegistryProvider value={ registry }>
|
|
128
|
+
<TestComponent enabled={ enabled } />
|
|
129
|
+
</RegistryProvider>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const { rerender } = render( <UI enabled={ true } /> );
|
|
133
|
+
|
|
134
|
+
// A minimum delay for a fetch request. The same delay is used again as a control.
|
|
135
|
+
await act(
|
|
136
|
+
() => new Promise( ( resolve ) => setTimeout( resolve, 0 ) )
|
|
137
|
+
);
|
|
138
|
+
expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
|
|
139
|
+
|
|
140
|
+
rerender( <UI enabled={ false } /> );
|
|
141
|
+
|
|
142
|
+
expect( data ).toEqual( {
|
|
143
|
+
edit: expect.any( Function ),
|
|
144
|
+
editedRecord: {},
|
|
145
|
+
hasEdits: false,
|
|
146
|
+
edits: {},
|
|
147
|
+
record: null,
|
|
148
|
+
save: expect.any( Function ),
|
|
149
|
+
} );
|
|
150
|
+
|
|
151
|
+
// The same delay.
|
|
152
|
+
await act(
|
|
153
|
+
() => new Promise( ( resolve ) => setTimeout( resolve, 0 ) )
|
|
154
|
+
);
|
|
155
|
+
expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
|
|
156
|
+
} );
|
|
116
157
|
} );
|
|
@@ -56,6 +56,8 @@ export interface Options {
|
|
|
56
56
|
enabled: boolean;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const EMPTY_OBJECT = {};
|
|
60
|
+
|
|
59
61
|
/**
|
|
60
62
|
* Resolves the specified entity record.
|
|
61
63
|
*
|
|
@@ -167,24 +169,34 @@ export default function useEntityRecord< RecordType >(
|
|
|
167
169
|
);
|
|
168
170
|
|
|
169
171
|
const { editedRecord, hasEdits, edits } = useSelect(
|
|
170
|
-
( select ) =>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
172
|
+
( select ) => {
|
|
173
|
+
if ( ! options.enabled ) {
|
|
174
|
+
return {
|
|
175
|
+
editedRecord: EMPTY_OBJECT,
|
|
176
|
+
hasEdits: false,
|
|
177
|
+
edits: EMPTY_OBJECT,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
editedRecord: select( coreStore ).getEditedEntityRecord(
|
|
183
|
+
kind,
|
|
184
|
+
name,
|
|
185
|
+
recordId
|
|
186
|
+
),
|
|
187
|
+
hasEdits: select( coreStore ).hasEditsForEntityRecord(
|
|
188
|
+
kind,
|
|
189
|
+
name,
|
|
190
|
+
recordId
|
|
191
|
+
),
|
|
192
|
+
edits: select( coreStore ).getEntityRecordNonTransientEdits(
|
|
193
|
+
kind,
|
|
194
|
+
name,
|
|
195
|
+
recordId
|
|
196
|
+
),
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
[ kind, name, recordId, options.enabled ]
|
|
188
200
|
);
|
|
189
201
|
|
|
190
202
|
const { data: record, ...querySelectRest } = useQuerySelect(
|
|
@@ -57,7 +57,7 @@ export function getMergedItemIds( itemIds, nextItemIds, page, perPage ) {
|
|
|
57
57
|
for ( let i = 0; i < size; i++ ) {
|
|
58
58
|
// Preserve existing item ID except for subset of range of next items.
|
|
59
59
|
// We need to check against the possible maximum upper boundary because
|
|
60
|
-
// a page could
|
|
60
|
+
// a page could receive fewer than what was previously stored.
|
|
61
61
|
const isInNextItemsRange =
|
|
62
62
|
i >= nextItemIdsStartIndex && i < nextItemIdsStartIndex + perPage;
|
|
63
63
|
mergedItemIds[ i ] = isInNextItemsRange
|
package/src/reducer.js
CHANGED
|
@@ -238,14 +238,13 @@ function entity( entityConfig ) {
|
|
|
238
238
|
// Inject the entity config into the action.
|
|
239
239
|
replaceAction( ( action ) => {
|
|
240
240
|
return {
|
|
241
|
-
...action,
|
|
242
241
|
key: entityConfig.key || DEFAULT_ENTITY_KEY,
|
|
242
|
+
...action,
|
|
243
243
|
};
|
|
244
244
|
} ),
|
|
245
245
|
] )(
|
|
246
246
|
combineReducers( {
|
|
247
247
|
queriedData: queriedDataReducer,
|
|
248
|
-
|
|
249
248
|
edits: ( state = {}, action ) => {
|
|
250
249
|
switch ( action.type ) {
|
|
251
250
|
case 'RECEIVE_ITEMS':
|
|
@@ -355,6 +354,38 @@ function entity( entityConfig ) {
|
|
|
355
354
|
|
|
356
355
|
return state;
|
|
357
356
|
},
|
|
357
|
+
|
|
358
|
+
revisions: ( state = {}, action ) => {
|
|
359
|
+
// Use the same queriedDataReducer shape for revisions.
|
|
360
|
+
if ( action.type === 'RECEIVE_ITEM_REVISIONS' ) {
|
|
361
|
+
const recordKey = action.recordKey;
|
|
362
|
+
delete action.recordKey;
|
|
363
|
+
const newState = queriedDataReducer( state[ recordKey ], {
|
|
364
|
+
...action,
|
|
365
|
+
type: 'RECEIVE_ITEMS',
|
|
366
|
+
} );
|
|
367
|
+
return {
|
|
368
|
+
...state,
|
|
369
|
+
[ recordKey ]: newState,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if ( action.type === 'REMOVE_ITEMS' ) {
|
|
374
|
+
return Object.fromEntries(
|
|
375
|
+
Object.entries( state ).filter(
|
|
376
|
+
( [ id ] ) =>
|
|
377
|
+
! action.itemIds.some( ( itemId ) => {
|
|
378
|
+
if ( Number.isInteger( itemId ) ) {
|
|
379
|
+
return itemId === +id;
|
|
380
|
+
}
|
|
381
|
+
return itemId === id;
|
|
382
|
+
} )
|
|
383
|
+
)
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return state;
|
|
388
|
+
},
|
|
358
389
|
} )
|
|
359
390
|
);
|
|
360
391
|
}
|
package/src/resolvers.js
CHANGED
|
@@ -244,7 +244,7 @@ export const getEntityRecords =
|
|
|
244
244
|
|
|
245
245
|
// If we request fields but the result doesn't contain the fields,
|
|
246
246
|
// explicitly set these fields as "undefined"
|
|
247
|
-
// that way we consider the query "
|
|
247
|
+
// that way we consider the query "fulfilled".
|
|
248
248
|
if ( query._fields ) {
|
|
249
249
|
records = records.map( ( record ) => {
|
|
250
250
|
query._fields.split( ',' ).forEach( ( field ) => {
|
|
@@ -322,7 +322,7 @@ export const getCurrentTheme =
|
|
|
322
322
|
export const getThemeSupports = forwardResolver( 'getCurrentTheme' );
|
|
323
323
|
|
|
324
324
|
/**
|
|
325
|
-
* Requests a preview from the
|
|
325
|
+
* Requests a preview from the Embed API.
|
|
326
326
|
*
|
|
327
327
|
* @param {string} url URL to get the preview for.
|
|
328
328
|
*/
|
|
@@ -718,3 +718,183 @@ export const getDefaultTemplateId =
|
|
|
718
718
|
dispatch.receiveDefaultTemplateId( query, template.id );
|
|
719
719
|
}
|
|
720
720
|
};
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Requests an entity's revisions from the REST API.
|
|
724
|
+
*
|
|
725
|
+
* @param {string} kind Entity kind.
|
|
726
|
+
* @param {string} name Entity name.
|
|
727
|
+
* @param {number|string} recordKey The key of the entity record whose revisions you want to fetch.
|
|
728
|
+
* @param {Object|undefined} query Optional object of query parameters to
|
|
729
|
+
* include with request. If requesting specific
|
|
730
|
+
* fields, fields must always include the ID.
|
|
731
|
+
*/
|
|
732
|
+
export const getRevisions =
|
|
733
|
+
( kind, name, recordKey, query = {} ) =>
|
|
734
|
+
async ( { dispatch } ) => {
|
|
735
|
+
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
|
|
736
|
+
const entityConfig = configs.find(
|
|
737
|
+
( config ) => config.name === name && config.kind === kind
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
if ( ! entityConfig || entityConfig?.__experimentalNoFetch ) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if ( query._fields ) {
|
|
745
|
+
// If requesting specific fields, items and query association to said
|
|
746
|
+
// records are stored by ID reference. Thus, fields must always include
|
|
747
|
+
// the ID.
|
|
748
|
+
query = {
|
|
749
|
+
...query,
|
|
750
|
+
_fields: [
|
|
751
|
+
...new Set( [
|
|
752
|
+
...( getNormalizedCommaSeparable( query._fields ) ||
|
|
753
|
+
[] ),
|
|
754
|
+
entityConfig.revisionKey || DEFAULT_ENTITY_KEY,
|
|
755
|
+
] ),
|
|
756
|
+
].join(),
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
const path = addQueryArgs(
|
|
761
|
+
entityConfig.getRevisionsUrl( recordKey ),
|
|
762
|
+
query
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
let records, response;
|
|
766
|
+
const meta = {};
|
|
767
|
+
const isPaginated =
|
|
768
|
+
entityConfig.supportsPagination && query.per_page !== -1;
|
|
769
|
+
try {
|
|
770
|
+
response = await apiFetch( { path, parse: ! isPaginated } );
|
|
771
|
+
} catch ( error ) {
|
|
772
|
+
// Do nothing if our request comes back with an API error.
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if ( response ) {
|
|
777
|
+
if ( isPaginated ) {
|
|
778
|
+
records = Object.values( await response.json() );
|
|
779
|
+
meta.totalItems = parseInt(
|
|
780
|
+
response.headers.get( 'X-WP-Total' )
|
|
781
|
+
);
|
|
782
|
+
} else {
|
|
783
|
+
records = Object.values( response );
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// If we request fields but the result doesn't contain the fields,
|
|
787
|
+
// explicitly set these fields as "undefined"
|
|
788
|
+
// that way we consider the query "fulfilled".
|
|
789
|
+
if ( query._fields ) {
|
|
790
|
+
records = records.map( ( record ) => {
|
|
791
|
+
query._fields.split( ',' ).forEach( ( field ) => {
|
|
792
|
+
if ( ! record.hasOwnProperty( field ) ) {
|
|
793
|
+
record[ field ] = undefined;
|
|
794
|
+
}
|
|
795
|
+
} );
|
|
796
|
+
|
|
797
|
+
return record;
|
|
798
|
+
} );
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
dispatch.receiveRevisions(
|
|
802
|
+
kind,
|
|
803
|
+
name,
|
|
804
|
+
recordKey,
|
|
805
|
+
records,
|
|
806
|
+
query,
|
|
807
|
+
false,
|
|
808
|
+
meta
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
// When requesting all fields, the list of results can be used to
|
|
812
|
+
// resolve the `getRevision` selector in addition to `getRevisions`.
|
|
813
|
+
if ( ! query?._fields && ! query.context ) {
|
|
814
|
+
const key = entityConfig.key || DEFAULT_ENTITY_KEY;
|
|
815
|
+
const resolutionsArgs = records
|
|
816
|
+
.filter( ( record ) => record[ key ] )
|
|
817
|
+
.map( ( record ) => [
|
|
818
|
+
kind,
|
|
819
|
+
name,
|
|
820
|
+
recordKey,
|
|
821
|
+
record[ key ],
|
|
822
|
+
] );
|
|
823
|
+
|
|
824
|
+
dispatch( {
|
|
825
|
+
type: 'START_RESOLUTIONS',
|
|
826
|
+
selectorName: 'getRevision',
|
|
827
|
+
args: resolutionsArgs,
|
|
828
|
+
} );
|
|
829
|
+
dispatch( {
|
|
830
|
+
type: 'FINISH_RESOLUTIONS',
|
|
831
|
+
selectorName: 'getRevision',
|
|
832
|
+
args: resolutionsArgs,
|
|
833
|
+
} );
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
// Invalidate cache when a new revision is created.
|
|
839
|
+
getRevisions.shouldInvalidate = ( action, kind, name, recordKey ) =>
|
|
840
|
+
action.type === 'SAVE_ENTITY_RECORD_FINISH' &&
|
|
841
|
+
name === action.name &&
|
|
842
|
+
kind === action.kind &&
|
|
843
|
+
! action.error &&
|
|
844
|
+
recordKey === action.recordId;
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Requests a specific Entity revision from the REST API.
|
|
848
|
+
*
|
|
849
|
+
* @param {string} kind Entity kind.
|
|
850
|
+
* @param {string} name Entity name.
|
|
851
|
+
* @param {number|string} recordKey The key of the entity record whose revisions you want to fetch.
|
|
852
|
+
* @param {number|string} revisionKey The revision's key.
|
|
853
|
+
* @param {Object|undefined} query Optional object of query parameters to
|
|
854
|
+
* include with request. If requesting specific
|
|
855
|
+
* fields, fields must always include the ID.
|
|
856
|
+
*/
|
|
857
|
+
export const getRevision =
|
|
858
|
+
( kind, name, recordKey, revisionKey, query ) =>
|
|
859
|
+
async ( { dispatch } ) => {
|
|
860
|
+
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
|
|
861
|
+
const entityConfig = configs.find(
|
|
862
|
+
( config ) => config.name === name && config.kind === kind
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
if ( ! entityConfig || entityConfig?.__experimentalNoFetch ) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if ( query !== undefined && query._fields ) {
|
|
870
|
+
// If requesting specific fields, items and query association to said
|
|
871
|
+
// records are stored by ID reference. Thus, fields must always include
|
|
872
|
+
// the ID.
|
|
873
|
+
query = {
|
|
874
|
+
...query,
|
|
875
|
+
_fields: [
|
|
876
|
+
...new Set( [
|
|
877
|
+
...( getNormalizedCommaSeparable( query._fields ) ||
|
|
878
|
+
[] ),
|
|
879
|
+
entityConfig.revisionKey || DEFAULT_ENTITY_KEY,
|
|
880
|
+
] ),
|
|
881
|
+
].join(),
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
const path = addQueryArgs(
|
|
885
|
+
entityConfig.getRevisionsUrl( recordKey, revisionKey ),
|
|
886
|
+
query
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
let record;
|
|
890
|
+
try {
|
|
891
|
+
record = await apiFetch( { path } );
|
|
892
|
+
} catch ( error ) {
|
|
893
|
+
// Do nothing if our request comes back with an API error.
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if ( record ) {
|
|
898
|
+
dispatch.receiveRevisions( kind, name, recordKey, record, query );
|
|
899
|
+
}
|
|
900
|
+
};
|
package/src/selectors.ts
CHANGED
|
@@ -62,6 +62,16 @@ interface QueriedData {
|
|
|
62
62
|
queries: Record< ET.Context, Record< string, Array< number > > >;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
type RevisionRecord =
|
|
66
|
+
| Record< ET.Context, Record< number, ET.PostRevision > >
|
|
67
|
+
| Record< ET.Context, Record< number, ET.GlobalStylesRevision > >;
|
|
68
|
+
|
|
69
|
+
interface RevisionsQueriedData {
|
|
70
|
+
items: RevisionRecord;
|
|
71
|
+
itemIsComplete: Record< ET.Context, Record< number, boolean > >;
|
|
72
|
+
queries: Record< ET.Context, Record< string, Array< number > > >;
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
interface EntityState< EntityRecord extends ET.EntityRecord > {
|
|
66
76
|
edits: Record< string, Partial< EntityRecord > >;
|
|
67
77
|
saving: Record<
|
|
@@ -70,6 +80,7 @@ interface EntityState< EntityRecord extends ET.EntityRecord > {
|
|
|
70
80
|
>;
|
|
71
81
|
deleting: Record< string, Partial< { pending: boolean; error: Error } > >;
|
|
72
82
|
queriedData: QueriedData;
|
|
83
|
+
revisions?: RevisionsQueriedData;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
interface EntityConfig {
|
|
@@ -1342,13 +1353,20 @@ export function getUserPatternCategories(
|
|
|
1342
1353
|
/**
|
|
1343
1354
|
* Returns the revisions of the current global styles theme.
|
|
1344
1355
|
*
|
|
1345
|
-
* @
|
|
1356
|
+
* @deprecated since WordPress 6.5.0. Callers should use `select( 'core' ).getRevisions( 'root', 'globalStyles', ${ recordKey } )` instead, where `recordKey` is the id of the global styles parent post.
|
|
1357
|
+
*
|
|
1358
|
+
* @param state Data state.
|
|
1346
1359
|
*
|
|
1347
1360
|
* @return The current global styles.
|
|
1348
1361
|
*/
|
|
1349
1362
|
export function getCurrentThemeGlobalStylesRevisions(
|
|
1350
1363
|
state: State
|
|
1351
1364
|
): Array< object > | null {
|
|
1365
|
+
deprecated( "select( 'core' ).getCurrentThemeGlobalStylesRevisions()", {
|
|
1366
|
+
since: '6.5.0',
|
|
1367
|
+
alternative:
|
|
1368
|
+
"select( 'core' ).getRevisions( 'root', 'globalStyles', ${ recordKey } )",
|
|
1369
|
+
} );
|
|
1352
1370
|
const currentGlobalStylesId =
|
|
1353
1371
|
__experimentalGetCurrentGlobalStylesId( state );
|
|
1354
1372
|
|
|
@@ -1373,3 +1391,103 @@ export function getDefaultTemplateId(
|
|
|
1373
1391
|
): string {
|
|
1374
1392
|
return state.defaultTemplates[ JSON.stringify( query ) ];
|
|
1375
1393
|
}
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* Returns an entity's revisions.
|
|
1397
|
+
*
|
|
1398
|
+
* @param state State tree
|
|
1399
|
+
* @param kind Entity kind.
|
|
1400
|
+
* @param name Entity name.
|
|
1401
|
+
* @param recordKey The key of the entity record whose revisions you want to fetch.
|
|
1402
|
+
* @param query Optional query. If requesting specific
|
|
1403
|
+
* fields, fields must always include the ID. For valid query parameters see revisions schema in [the REST API Handbook](https://developer.wordpress.org/rest-api/reference/). Then see the arguments available "Retrieve a [Entity kind]".
|
|
1404
|
+
*
|
|
1405
|
+
* @return Record.
|
|
1406
|
+
*/
|
|
1407
|
+
export const getRevisions = (
|
|
1408
|
+
state: State,
|
|
1409
|
+
kind: string,
|
|
1410
|
+
name: string,
|
|
1411
|
+
recordKey: EntityRecordKey,
|
|
1412
|
+
query?: GetRecordsHttpQuery
|
|
1413
|
+
): RevisionRecord[] | null => {
|
|
1414
|
+
const queriedStateRevisions =
|
|
1415
|
+
state.entities.records?.[ kind ]?.[ name ]?.revisions?.[ recordKey ];
|
|
1416
|
+
if ( ! queriedStateRevisions ) {
|
|
1417
|
+
return null;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
return getQueriedItems( queriedStateRevisions, query );
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* Returns a single, specific revision of a parent entity.
|
|
1425
|
+
*
|
|
1426
|
+
* @param state State tree
|
|
1427
|
+
* @param kind Entity kind.
|
|
1428
|
+
* @param name Entity name.
|
|
1429
|
+
* @param recordKey The key of the entity record whose revisions you want to fetch.
|
|
1430
|
+
* @param revisionKey The revision's key.
|
|
1431
|
+
* @param query Optional query. If requesting specific
|
|
1432
|
+
* fields, fields must always include the ID. For valid query parameters see revisions schema in [the REST API Handbook](https://developer.wordpress.org/rest-api/reference/). Then see the arguments available "Retrieve a [entity kind]".
|
|
1433
|
+
*
|
|
1434
|
+
* @return Record.
|
|
1435
|
+
*/
|
|
1436
|
+
export const getRevision = createSelector(
|
|
1437
|
+
(
|
|
1438
|
+
state: State,
|
|
1439
|
+
kind: string,
|
|
1440
|
+
name: string,
|
|
1441
|
+
recordKey: EntityRecordKey,
|
|
1442
|
+
revisionKey: EntityRecordKey,
|
|
1443
|
+
query?: GetRecordsHttpQuery
|
|
1444
|
+
): RevisionRecord | Record< PropertyKey, never > | undefined => {
|
|
1445
|
+
const queriedState =
|
|
1446
|
+
state.entities.records?.[ kind ]?.[ name ]?.revisions?.[
|
|
1447
|
+
recordKey
|
|
1448
|
+
];
|
|
1449
|
+
|
|
1450
|
+
if ( ! queriedState ) {
|
|
1451
|
+
return undefined;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
const context = query?.context ?? 'default';
|
|
1455
|
+
|
|
1456
|
+
if ( query === undefined ) {
|
|
1457
|
+
// If expecting a complete item, validate that completeness.
|
|
1458
|
+
if ( ! queriedState.itemIsComplete[ context ]?.[ revisionKey ] ) {
|
|
1459
|
+
return undefined;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
return queriedState.items[ context ][ revisionKey ];
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
const item = queriedState.items[ context ]?.[ revisionKey ];
|
|
1466
|
+
if ( item && query._fields ) {
|
|
1467
|
+
const filteredItem = {};
|
|
1468
|
+
const fields = getNormalizedCommaSeparable( query._fields ) ?? [];
|
|
1469
|
+
|
|
1470
|
+
for ( let f = 0; f < fields.length; f++ ) {
|
|
1471
|
+
const field = fields[ f ].split( '.' );
|
|
1472
|
+
let value = item;
|
|
1473
|
+
field.forEach( ( fieldName ) => {
|
|
1474
|
+
value = value?.[ fieldName ];
|
|
1475
|
+
} );
|
|
1476
|
+
setNestedValue( filteredItem, field, value );
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
return filteredItem;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
return item;
|
|
1483
|
+
},
|
|
1484
|
+
( state: State, kind, name, recordKey, revisionKey, query ) => {
|
|
1485
|
+
const context = query?.context ?? 'default';
|
|
1486
|
+
return [
|
|
1487
|
+
state.entities.records?.[ kind ]?.[ name ]?.revisions?.[ recordKey ]
|
|
1488
|
+
?.items?.[ context ]?.[ revisionKey ],
|
|
1489
|
+
state.entities.records?.[ kind ]?.[ name ]?.revisions?.[ recordKey ]
|
|
1490
|
+
?.itemIsComplete?.[ context ]?.[ revisionKey ],
|
|
1491
|
+
];
|
|
1492
|
+
}
|
|
1493
|
+
);
|
package/src/test/entities.js
CHANGED
|
@@ -80,6 +80,9 @@ describe( 'getKindEntities', () => {
|
|
|
80
80
|
labels: {
|
|
81
81
|
singular_name: 'post',
|
|
82
82
|
},
|
|
83
|
+
supports: {
|
|
84
|
+
revisions: true,
|
|
85
|
+
},
|
|
83
86
|
},
|
|
84
87
|
];
|
|
85
88
|
const dispatch = jest.fn();
|
|
@@ -95,6 +98,12 @@ describe( 'getKindEntities', () => {
|
|
|
95
98
|
expect( dispatch.mock.calls[ 0 ][ 0 ].entities[ 0 ].baseURL ).toBe(
|
|
96
99
|
'/wp/v2/posts'
|
|
97
100
|
);
|
|
101
|
+
expect(
|
|
102
|
+
dispatch.mock.calls[ 0 ][ 0 ].entities[ 0 ].getRevisionsUrl( 1 )
|
|
103
|
+
).toBe( '/wp/v2/posts/1/revisions' );
|
|
104
|
+
expect(
|
|
105
|
+
dispatch.mock.calls[ 0 ][ 0 ].entities[ 0 ].getRevisionsUrl( 1, 2 )
|
|
106
|
+
).toBe( '/wp/v2/posts/1/revisions/2' );
|
|
98
107
|
} );
|
|
99
108
|
} );
|
|
100
109
|
|