PluginProbe
Parse.ly / 3.24.3
Parse.ly v3.24.3
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 / editor-sidebar.tsx

editor-sidebar.tsx in Parse.ly 3.24.3, at src/content-helper/editor-sidebar/editor-sidebar.tsx

258 lines 7.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 {
5 Panel,
6 TabPanel,
7 } from '@wordpress/components';
8 import { useSelect } from '@wordpress/data';
9 import domReady from '@wordpress/dom-ready';
10 import { useEffect } from '@wordpress/element';
11 import { __ } from '@wordpress/i18n';
12 import { chartBar as ChartIcon } from '@wordpress/icons';
13 import { registerPlugin } from '@wordpress/plugins';
14 import { PluginSidebar } from '../../@types/gutenberg/wrapper';
15
16 /**
17 * Internal dependencies
18 */
19 import { Telemetry } from '../../js/telemetry/telemetry';
20 import { EditIcon } from '../common/icons/edit-icon';
21 import { LeafIcon } from '../common/icons/leaf-icon';
22 import {
23 SettingsProvider,
24 SidebarSettings,
25 useSettings,
26 } from '../common/settings';
27 import {
28 DEFAULT_PERSONA,
29 DEFAULT_TONE,
30 Metric,
31 Period,
32 } from '../common/utils/constants';
33 import { getContentHelperPermissions } from '../common/utils/permissions';
34 import { DEFAULT_EXCERPT_LENGTH } from './excerpt-suggestions/constants';
35 import { initExcerptSuggestions } from './excerpt-suggestions/excerpt-suggestions';
36 import {
37 DEFAULT_MAX_LINKS,
38 initSmartLinking,
39 } from './smart-linking/smart-linking';
40 import { SidebarPerformanceTab } from './tabs/sidebar-performance-tab';
41 import { SidebarToolsTab } from './tabs/sidebar-tools-tab';
42
43 const BLOCK_PLUGIN_ID = 'wp-parsely-block-editor-sidebar';
44 export { BLOCK_PLUGIN_ID as PARSELY_SIDEBAR_PLUGIN_ID };
45
46 export type OnSettingChangeFunction = ( key: keyof SidebarSettings, value: string | boolean | number ) => void;
47
48 /**
49 * Defines the data structure exposed by the Sidebar about the currently opened
50 * Post.
51 *
52 * @since 3.11.0
53 */
54 export interface SidebarPostData {
55 authors: string[];
56 categories: SidebarPostDataCategory[];
57 tags: string[];
58 }
59
60 /**
61 * Defines the structure of a SidebarPostData category.
62 *
63 * @since 3.18.0
64 */
65 export interface SidebarPostDataCategory {
66 name: string;
67 slug: string;
68 }
69
70 /**
71 * Gets the settings from the passed JSON.
72 *
73 * The settings endpoint resolves stored values against its specifications, so
74 * the defaults here only apply when its payload is missing or unparseable.
75 *
76 * @since 3.13.0
77 * @since 3.24.0 Individual values are resolved by the settings endpoint.
78 *
79 * @param {string} settingsJson The JSON containing the settings.
80 *
81 * @return {SidebarSettings} The resulting settings object.
82 */
83 export const getSettingsFromJson = ( settingsJson: string = '' ): SidebarSettings => {
84 // Default settings object.
85 const defaultSettings: SidebarSettings = {
86 InitialTabName: 'tools',
87 PerformanceStats: {
88 Period: Period.Days7,
89 VisiblePanels: [ 'overview', 'categories', 'referrers' ],
90 VisibleDataPoints: [ 'views', 'visitors', 'avgEngaged', 'recirculation' ],
91 },
92 RelatedPosts: {
93 Metric: Metric.Views,
94 Open: false,
95 Period: Period.Days7,
96 },
97 SmartLinking: {
98 MaxLinks: DEFAULT_MAX_LINKS,
99 Open: false,
100 },
101 TitleSuggestions: {
102 Open: false,
103 Tone: DEFAULT_TONE,
104 Persona: DEFAULT_PERSONA,
105 },
106 ExcerptSuggestions: {
107 Length: DEFAULT_EXCERPT_LENGTH,
108 Persona: DEFAULT_PERSONA,
109 Tone: DEFAULT_TONE,
110 },
111 };
112
113 // If the settings are empty, try to get them from the global variable.
114 if ( '' === settingsJson ) {
115 settingsJson = window.wpParselyContentHelperSettings;
116 }
117
118 let parsedSettings: SidebarSettings;
119 try {
120 parsedSettings = JSON.parse( settingsJson );
121 } catch ( e ) {
122 // Return defaults when parsing failed or the string is empty.
123 return defaultSettings;
124 }
125
126 return { ...defaultSettings, ...parsedSettings };
127 };
128
129 /**
130 * Returns the Content Intelligence Editor Sidebar.
131 *
132 * @since 3.4.0
133 *
134 * @return {import('react').JSX.Element} The Content Intelligence Editor Sidebar.
135 */
136 const ContentHelperEditorSidebar = (): React.JSX.Element => {
137 const { settings, setSettings } = useSettings<SidebarSettings>();
138 const permissions = getContentHelperPermissions();
139
140 /**
141 * Track sidebar opening.
142 *
143 * @since 3.12.0
144 */
145 const activeComplementaryArea = useSelect( ( select ) => {
146 // By checking for the PluginSidebar, we can determine if the new unified editor is being used, and that the
147 // WordPress version is 6.6 or higher.
148 if ( window.wp.editor?.PluginSidebar ) {
149 // @ts-ignore getActiveComplementaryArea exists in the interface store.
150 return select( 'core/interface' ).getActiveComplementaryArea( 'core' );
151 }
152
153 // Fallback for WordPress <= 6.5.
154 // See https://make.wordpress.org/core/2024/03/05/unification-of-the-site-and-post-editors-in-6-5/
155 // @ts-ignore getActiveComplementaryArea exists in the interface store.
156 return select( 'core/interface' ).getActiveComplementaryArea( 'core/edit-post' );
157 }, [ ] );
158
159 useEffect( () => {
160 if ( activeComplementaryArea === 'wp-parsely-block-editor-sidebar/wp-parsely-content-helper' ) {
161 Telemetry.trackEvent( 'editor_sidebar_opened' );
162 }
163 }, [ activeComplementaryArea ] );
164
165 /**
166 * Track sidebar panel opening and closing.
167 *
168 * @since 3.12.0
169 *
170 * @param {string} panel The panel name.
171 * @param {boolean} next Whether the panel is open or closed.
172 */
173 const trackToggle = ( panel: string, next: boolean ): void => {
174 if ( next ) {
175 Telemetry.trackEvent( 'editor_sidebar_panel_opened', { panel } );
176 } else {
177 Telemetry.trackEvent( 'editor_sidebar_panel_closed', { panel } );
178 }
179 };
180
181 return (
182 <PluginSidebar icon={ <LeafIcon className="wp-parsely-sidebar-icon" /> }
183 name="wp-parsely-content-helper"
184 className="wp-parsely-content-helper"
185 title={ __( 'Parse.ly', 'wp-parsely' ) }
186 >
187 <SettingsProvider
188 endpoint="editor-sidebar"
189 defaultSettings={ getSettingsFromJson() }
190 >
191 <Panel className="wp-parsely-sidebar-main-panel">
192 <TabPanel
193 className="wp-parsely-sidebar-tabs"
194 initialTabName={ settings.InitialTabName }
195 tabs={ [
196 {
197 icon: <EditIcon />,
198 name: 'tools',
199 title: __( 'Tools', 'wp-parsely' ),
200 },
201 {
202 icon: ChartIcon,
203 name: 'performance',
204 title: __( 'Performance', 'wp-parsely' ),
205 },
206 ] }
207 onSelect={ ( tabName ) => {
208 setSettings( { ...settings, InitialTabName: tabName } );
209 Telemetry.trackEvent( 'editor_sidebar_tab_selected', { tab: tabName } );
210 } }
211 >
212 { ( tab ) => (
213 <>
214 { tab.name === 'tools' && (
215 <SidebarToolsTab
216 permissions={ permissions }
217 trackToggle={ trackToggle }
218 />
219 ) }
220 { tab.name === 'performance' && (
221 <SidebarPerformanceTab
222 period={ settings.PerformanceStats.Period }
223 />
224 ) }
225 </>
226 ) }
227 </TabPanel>
228 </Panel>
229 </SettingsProvider>
230 </PluginSidebar>
231 );
232 };
233
234 // Initialize Excerpt Suggestions.
235 if ( initExcerptSuggestions ) {
236 initExcerptSuggestions();
237 }
238
239 // Registering Plugin to WordPress Block Editor.
240 registerPlugin( BLOCK_PLUGIN_ID, {
241 icon: LeafIcon,
242 render: () => (
243 <SettingsProvider
244 endpoint="editor-sidebar"
245 defaultSettings={ getSettingsFromJson() }
246 >
247 <ContentHelperEditorSidebar />
248 </SettingsProvider>
249 ),
250 } );
251
252 // Initialize Smart Linking.
253 domReady( () => {
254 if ( initSmartLinking ) {
255 initSmartLinking();
256 }
257 } );
258