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 / dashboard-widget / components / top-posts.tsx

top-posts.tsx in Parse.ly 3.24.2, at src/content-helper/dashboard-widget/components/top-posts.tsx

206 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 import { Spinner } from '@wordpress/components';
5 import { useEffect, useState } from '@wordpress/element';
6 import { __, sprintf } from '@wordpress/i18n';
7
8 /**
9 * Internal dependencies
10 */
11 import { Telemetry } from '../../../js/telemetry/telemetry';
12 import { ContentHelperError } from '../../common/content-helper-error';
13 import { Select } from '../../common/select';
14 import { TopPostsSettings, useSettings } from '../../common/settings';
15 import {
16 Metric,
17 Period,
18 getMetricDescription,
19 getPeriodDescription,
20 isInEnum,
21 } from '../../common/utils/constants';
22 import { PostData } from '../../common/utils/post';
23 import { DashboardWidgetProvider, TOP_POSTS_DEFAULT_LIMIT } from '../provider';
24 import { TopPostListItem } from './top-posts-list-item';
25
26 /**
27 * The maximum number of retries for fetching the top posts.
28 *
29 * @since 3.4.0
30 * @since 3.20.0 Renamed from FETCH_RETRIES to MAX_FETCH_RETRIES.
31 */
32 const MAX_FETCH_RETRIES = 1;
33
34 /**
35 * List of the top posts.
36 *
37 * @since 3.7.0
38 */
39 export function TopPosts(): React.JSX.Element {
40 const { settings, setSettings } = useSettings<TopPostsSettings>();
41 const [ loading, setLoading ] = useState<boolean>( true );
42 const [ error, setError ] = useState<ContentHelperError>();
43 const [ posts, setPosts ] = useState<PostData[]>( [] );
44 const [ page, setPage ] = useState<number>( 1 );
45
46 /**
47 * Fetches the top posts.
48 *
49 * @since 3.7.0
50 */
51 useEffect( () => {
52 const provider = DashboardWidgetProvider.getInstance();
53
54 const fetchPosts = async ( retries: number ) => {
55 provider.getTopPosts( settings, page )
56 .then( ( result ): void => {
57 setPosts( result );
58 setLoading( false );
59 } )
60 .catch( async ( err ) => {
61 if ( retries > 0 && err.retryFetch ) {
62 await new Promise( ( r ) => setTimeout( r, 500 ) );
63 await fetchPosts( retries - 1 );
64 } else {
65 setLoading( false );
66 setError( err );
67 }
68 } );
69 };
70
71 setLoading( true );
72 fetchPosts( MAX_FETCH_RETRIES );
73
74 return (): void => {
75 setLoading( false );
76 setPosts( [] );
77 setError( undefined );
78 };
79 }, [ settings, page ] );
80
81 /**
82 * Tracks the filter changes.
83 *
84 * @since 3.12.0
85 *
86 * @param {string} filter The filter name.
87 * @param {Object} props The filter properties.
88 */
89 const trackFilterChanges = ( filter: string, props: object ): void => {
90 Telemetry.trackEvent( 'dash_widget_filter_changed', { filter, ...props } );
91 };
92
93 const filters: React.JSX.Element = (
94 <div className="parsely-top-posts-filters">
95 <Select
96 defaultValue={ settings.Period }
97 items={
98 Object.values( Period ).map(
99 ( value ) => [ value, getPeriodDescription( value ) ]
100 )
101 }
102 onChange={ ( event ) => {
103 if ( isInEnum( event.target.value, Period ) ) {
104 setSettings( {
105 Period: event.target.value as Period,
106 } );
107 trackFilterChanges( 'period', { period: event.target.value } );
108 setPage( 1 );
109 }
110 } }
111 />
112 <Select
113 defaultValue={ settings.Metric }
114 items={
115 Object.values( Metric ).map(
116 ( value ) => [ value, getMetricDescription( value ) ]
117 )
118 }
119 onChange={ ( event ) => {
120 if ( isInEnum( event.target.value, Metric ) ) {
121 setSettings( {
122 Metric: event.target.value as Metric,
123 } );
124 trackFilterChanges( 'metric', { metric: event.target.value } );
125 setPage( 1 );
126 }
127 } }
128 />
129 </div>
130 );
131
132 const navigation: React.JSX.Element = (
133 <div className="parsely-top-posts-navigation">
134 <button
135 className="parsely-top-posts-navigation-prev"
136 disabled={ page <= 1 }
137 aria-label={ __( 'Previous page', 'wp-parsely' ) }
138 onClick={ () => {
139 setPage( page - 1 );
140 Telemetry.trackEvent( 'dash_widget_navigation', {
141 navigation: 'previous',
142 to_page: page - 1,
143 } );
144 } }
145 >
146 { __( '<< Previous', 'wp-parsely' ) }
147 </button>
148 {
149 sprintf( /* translators: 1: Current page */
150 __( 'Page %1$d', 'wp-parsely' ),
151 page
152 )
153 }
154 <button
155 className="parsely-top-posts-navigation-next"
156 disabled={ ! loading && posts.length < TOP_POSTS_DEFAULT_LIMIT }
157 aria-label={ __( 'Next page', 'wp-parsely' ) }
158 onClick={ () => {
159 setPage( page + 1 );
160 Telemetry.trackEvent( 'dash_widget_navigation', {
161 navigation: 'next',
162 to_page: page + 1,
163 } );
164 } }
165 >
166 { __( 'Next >>', 'wp-parsely' ) }
167 </button>
168 </div>
169 );
170
171 // Show error message.
172 if ( error ) {
173 return (
174 <>
175 { filters }
176 { error.Message() }
177 { page > 1 && navigation }
178 </>
179 );
180 }
181
182 const spinner: React.JSX.Element = (
183 <div className="parsely-spinner-wrapper"><Spinner /></div>
184 );
185
186 return (
187 <>
188 { filters }
189 {
190 loading ? ( spinner ) : (
191 <ol className="parsely-top-posts" style={ { counterReset: 'item ' + ( ( page - 1 ) * TOP_POSTS_DEFAULT_LIMIT ) } }>
192 { posts.map( ( post: PostData ): React.JSX.Element =>
193 <TopPostListItem
194 key={ post.id }
195 metric={ settings.Metric }
196 post={ post }
197 />
198 ) }
199 </ol>
200 )
201 }
202 { ( posts.length >= TOP_POSTS_DEFAULT_LIMIT || page > 1 ) && navigation }
203 </>
204 );
205 }
206