PluginProbe
WebberZone Top 10 — Popular Posts / 4.4.0
WebberZone Top 10 — Popular Posts v4.4.0
4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 All 117 releases
top-10 / includes / frontend / blocks / src / post-count / post-count-block.js

post-count-block.js in WebberZone Top 10 — Popular Posts 4.4.0, at includes/frontend/blocks/src/post-count/post-count-block.js

166 lines 3.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useEffect, useState } from '@wordpress/element';
2 import { __ } from '@wordpress/i18n';
3 import { useBlockProps } from '@wordpress/block-editor';
4 import apiFetch from '@wordpress/api-fetch';
5 import clsx from 'clsx';
6
7 const fetchCount = async (postId, counterType, blogId, fromDate, toDate) => {
8 try {
9 const response = await apiFetch({
10 path: `/top-10/v1/counter/${postId}?counter=${counterType}&blog_id=${blogId}&from_date=${fromDate}&to_date=${toDate}`,
11 method: 'GET',
12 });
13 return response;
14 } catch (error) {
15 console.error(`Error fetching ${counterType} count:`, error);
16 return null;
17 }
18 };
19
20 const fetchDefaultPostId = async () => {
21 try {
22 const response = await apiFetch({ path: '/wp/v2/posts?per_page=1' });
23 return response.length > 0 ? response[0].id : null;
24 } catch (error) {
25 console.error('Error fetching default post ID:', error);
26 return null;
27 }
28 };
29
30 const PostCountBlock = ({ attributes, context }) => {
31 const [counts, setCounts] = useState({
32 total: null,
33 daily: null,
34 overall: null,
35 });
36 const [postId, setPostId] = useState(context?.postId || null);
37 const {
38 counter: counterType = 'total',
39 blogId = 1,
40 fromDate,
41 toDate,
42 textBefore = '',
43 textAfter = '',
44 textAdvanced = '',
45 advancedMode = false,
46 svgCode = '',
47 svgIconSize = '1',
48 svgIconSizeUnit = 'em',
49 svgPaddingValues = [0, 0, 0, 0],
50 svgPaddingUnits = ['px', 'px', 'px', 'px'],
51 svgIconLocation = 'before',
52 numberFormat = false,
53 textAlign,
54 } = attributes;
55
56 const blockProps = useBlockProps({
57 className: clsx({
58 [`has-text-align-${textAlign}`]: textAlign,
59 }),
60 });
61
62 useEffect(() => {
63 const fetchCounts = async () => {
64 if (!postId) {
65 const defaultPostId = await fetchDefaultPostId();
66 setPostId(defaultPostId);
67 return;
68 }
69
70 const countTypes = ['total', 'daily', 'overall'];
71 const fetchedCounts = {};
72
73 for (const type of countTypes) {
74 if (
75 type === counterType ||
76 textAdvanced.includes(`%${type}count%`)
77 ) {
78 fetchedCounts[type] = await fetchCount(
79 postId,
80 type,
81 blogId,
82 fromDate,
83 toDate
84 );
85 }
86 }
87
88 setCounts(fetchedCounts);
89 };
90
91 fetchCounts();
92 }, [postId, counterType, blogId, fromDate, toDate, textAdvanced]);
93
94 if (postId === null) {
95 return (
96 <div {...blockProps}>
97 {__('No valid post ID found.', 'text-domain')}
98 </div>
99 );
100 }
101
102 if (Object.values(counts).every((count) => count === null)) {
103 return <div {...blockProps}>Loading...</div>;
104 }
105
106 const formatNumber = (num) => {
107 return numberFormat && num !== null && num !== undefined
108 ? num.toLocaleString()
109 : num;
110 };
111
112 const renderContent = () => {
113 if (!advancedMode || !textAdvanced) {
114 return (
115 <span className="tptn-post-count-text">
116 {textBefore}
117 {formatNumber(counts[counterType])}
118 {textAfter}
119 </span>
120 );
121 } else {
122 const replacedText = textAdvanced.replace(
123 /%(\w+)count%/g,
124 (match, type) => formatNumber(counts[type] ?? 'N/A')
125 );
126 return <span className="tptn-post-count-text">{replacedText}</span>;
127 }
128 };
129 const renderIcon = () => {
130 if (!svgCode) {
131 return null;
132 }
133
134 const paddingStyle = `padding:${svgPaddingValues.map((val, index) => `${val}${svgPaddingUnits[index]}`).join(' ')};`;
135 const svgStyle = `width: ${svgIconSize}${svgIconSizeUnit}; height: ${svgIconSize}${svgIconSizeUnit}; ${paddingStyle}`;
136
137 const svgWithStyle = svgCode.replace(
138 '<svg',
139 ` <svg style="${svgStyle}"`
140 );
141
142 return (
143 <span
144 className="tptn-post-count-icon"
145 dangerouslySetInnerHTML={{ __html: svgWithStyle }}
146 />
147 );
148 };
149
150 const content = renderContent();
151 const icon = renderIcon();
152
153 return (
154 <div
155 {...blockProps}
156 className={`wp-block-tptn-post-count tptn-post-count ${textAlign ? `has-text-align-${textAlign}` : ''} ${advancedMode ? 'tptn-advanced-mode' : ''}`}
157 >
158 {svgIconLocation === 'before' && icon}
159 {content}
160 {svgIconLocation === 'after' && icon}
161 </div>
162 );
163 };
164
165 export default PostCountBlock;
166