PluginProbe
Parse.ly / 3.24.2
Parse.ly v3.24.2
3.24.3 3.24.2 3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 All 107 releases
wp-parsely / src / content-helper / editor-sidebar / related-posts / hooks.ts

hooks.ts in Parse.ly 3.24.2, at src/content-helper/editor-sidebar/related-posts/hooks.ts

154 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * WordPress dependencies
3 */
4 // eslint-disable-next-line import/named
5 import { store as coreStore, Taxonomy, User } from '@wordpress/core-data';
6 import { useSelect } from '@wordpress/data';
7 import { useEffect, useState } from '@wordpress/element';
8
9 /**
10 * Internal dependencies
11 */
12 import { GutenbergFunction } from '../../../@types/gutenberg/types';
13
14 /**
15 * Defines the states of post data as handled in the usePostData() function.
16 *
17 * - When `undefined`, the data is still being fetched.
18 * - When `null`, an error has occurred before fetching the data.
19 * - When an array, the data has been fetched and is available.
20 *
21 * @since 3.14.3
22 * @since 3.14.4 Renamed to PostData from PostDataStore.
23 */
24 interface PostData {
25 authors: User[] | null | undefined;
26 categories: Taxonomy[] | null | undefined;
27 tags: Taxonomy[] | null | undefined;
28 isReady: boolean;
29 }
30
31 /**
32 * Hook to get the post data for the current post.
33 *
34 * Note: in some rare cases this function could process or return unexpected
35 * data due to Gutenberg functions not working as expected. Consumers should
36 * validate the data before using it.
37 *
38 * @since 3.14.3
39 * @since 3.14.4 Implemented checks to reduce risk of invalid data being processed.
40 * @since 3.16.2 Added `isLoading` and `hasResolved` to check if the data is still being fetched or if it
41 * has been resolved.
42 *
43 * @see https://github.com/Parsely/wp-parsely/issues/2423
44 *
45 * @return {PostData} The post data for the current post.
46 */
47 export function usePostData(): PostData {
48 const [ postData, setPostData ] = useState<PostData>( {
49 authors: undefined,
50 categories: undefined,
51 tags: undefined,
52 isReady: false,
53 } );
54
55 /**
56 * Fetches the post attributes from the editor.
57 * This includes the author, categories, and tags, and if the data is not available, it will be set to `null`.
58 *
59 * @since 3.14.4
60 * @since 3.16.2 Added `isLoading` and `hasResolved` to check if the data is still being fetched or if it
61 * has been resolved.
62 */
63 const postAttributes = useSelect( ( select ) => {
64 // @ts-ignore `hasFinishedResolution` and `isResolving` are not part of the core-data store type.
65 const { getEntityRecords, hasFinishedResolution, isResolving } = select( coreStore );
66
67 const editor = select( 'core/editor' ) as GutenbergFunction;
68 const authorId = editor.getEditedPostAttribute( 'author' );
69 const categoryIds = editor.getEditedPostAttribute( 'categories' );
70 const tagIds = editor.getEditedPostAttribute( 'tags' );
71
72 let authorRecords: User[] | null | undefined;
73 let categoryRecords: Taxonomy[] | null | undefined;
74 let tagRecords: Taxonomy[] | null | undefined;
75
76 if ( Number.isInteger( authorId ) ) {
77 authorRecords = getEntityRecords(
78 'root', 'user', { include: [ authorId ], context: 'view' }
79 ) ?? undefined; // Coalescing null to undefined
80 } else {
81 authorRecords = null;
82 }
83
84 if ( Array.isArray( categoryIds ) && categoryIds.length > 0 &&
85 categoryIds.every( Number.isInteger )
86 ) {
87 categoryRecords = getEntityRecords(
88 'taxonomy', 'category', { include: categoryIds, context: 'view' }
89 ) ?? undefined; // Coalescing null to undefined
90 } else {
91 categoryRecords = null;
92 }
93
94 if ( Array.isArray( tagIds ) && tagIds.length > 0 &&
95 tagIds.every( Number.isInteger )
96 ) {
97 tagRecords = getEntityRecords(
98 'taxonomy', 'post_tag', { include: tagIds, context: 'view' }
99 ) ?? undefined; // Coalescing null to undefined
100 } else {
101 tagRecords = null;
102 }
103
104 // Check if the data is still being fetched.
105 const isLoading = (
106 isResolving( 'getEntityRecords', [ 'root', 'user', { include: [ authorId ], context: 'view' } ] ) ||
107 isResolving( 'getEntityRecords', [ 'taxonomy', 'category', { include: categoryIds, context: 'view' } ] ) ||
108 isResolving( 'getEntityRecords', [ 'taxonomy', 'post_tag', { include: tagIds, context: 'view' } ] )
109 );
110
111 // Check if all the data has been resolved. If the data is not available, it will be set to `null`,
112 // so it is considered resolved.
113 const hasResolved = (
114 ( hasFinishedResolution( 'getEntityRecords', [ 'root', 'user', { include: [ authorId ], context: 'view' } ] ) ||
115 authorRecords === null ) &&
116 ( hasFinishedResolution( 'getEntityRecords', [ 'taxonomy', 'category', { include: categoryIds, context: 'view' } ] ) ||
117 categoryRecords === null ) &&
118 ( hasFinishedResolution( 'getEntityRecords', [ 'taxonomy', 'post_tag', { include: tagIds, context: 'view' } ] ) ||
119 tagRecords === null )
120 );
121
122 return { authorRecords, categoryRecords, tagRecords, isLoading, hasResolved };
123 }, [] );
124
125 /**
126 * Sets the post data when all the data is ready.
127 * This is done when all the data is fetched and resolved.
128 *
129 * @since 3.16.2
130 */
131 useEffect( () => {
132 const {
133 authorRecords,
134 categoryRecords,
135 tagRecords,
136 isLoading,
137 hasResolved,
138 } = postAttributes;
139
140 const isPostDataReady: boolean = hasResolved && ! isLoading;
141
142 if ( isPostDataReady ) {
143 setPostData( {
144 authors: authorRecords,
145 categories: categoryRecords,
146 tags: tagRecords,
147 isReady: true,
148 } );
149 }
150 }, [ postAttributes ] );
151
152 return postData;
153 }
154