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 / common / settings / provider.tsx

provider.tsx in Parse.ly 3.24.2, at src/content-helper/common/settings/provider.tsx

196 lines 5.3 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 apiFetch from '@wordpress/api-fetch';
5 import { dispatch, useSelect } from '@wordpress/data';
6 import {
7 createContext,
8 useCallback,
9 useContext,
10 useEffect,
11 useMemo,
12 useRef,
13 useState,
14 } from '@wordpress/element';
15 import type { ReactNode } from 'react';
16
17 /**
18 * Internal dependencies
19 */
20 import { SettingsStore } from './store';
21 import { Settings } from './types';
22
23 /**
24 * The context type for the settings context.
25 *
26 * @since 3.14.0
27 */
28 interface SettingsContextType<T> {
29 settings: T;
30 setSettings: ( settings: Partial<T> ) => void;
31 }
32
33 /**
34 * The Settings context.
35 *
36 * @since 3.14.0
37 */
38 const SettingsContext = createContext<SettingsContextType<Settings>>( {
39 settings: {} as Settings,
40 setSettings: () => {
41 // eslint-disable-next-line no-console
42 console.error( 'WP Parse.ly: setSettings not implemented' );
43 },
44 } );
45
46 /**
47 * Hook to get the settings from the context.
48 * Should only be used within a SettingsProvider.
49 *
50 * @since 3.14.0
51 *
52 * @template T - The type of settings to retrieve, defaults to Settings.
53 *
54 * @return {SettingsContextType<T>} The settings context.
55 *
56 * @throws {Error} Throws an error if used outside of a SettingsProvider.
57 *
58 * @example
59 * // Using the useSettings hook with a specific type
60 * const { settings, setSettings } = useSettings<SidebarSettings>();
61 */
62 export function useSettings<T = Settings>(): SettingsContextType<T> {
63 const context = useContext( SettingsContext );
64 if ( context === undefined ) {
65 throw new Error( 'useSettings must be used within a SettingsProvider' );
66 }
67 return context as unknown as SettingsContextType<T>;
68 }
69
70 /**
71 * Custom types for brevity and for avoiding a "type React is undefined" error.
72 */
73 type ReactDeps = React.DependencyList | undefined;
74
75 /**
76 * Saves the settings into the WordPress database whenever a dependency update
77 * occurs.
78 *
79 * @since 3.13.0
80 * @since 3.14.0 Moved from `content-helper/common/hooks/useSaveSettings.ts`.
81 * @since 3.17.0 Updated to the new API endpoints.
82 *
83 * @param {string} endpoint The settings endpoint to send the data to.
84 * @param {Settings} data The data to send.
85 * @param {ReactDeps} deps The deps array that triggers saving.
86 */
87 const useSaveSettings = (
88 endpoint: string, data: Settings, deps: ReactDeps
89 ): void => {
90 const isFirstRender = useRef( true );
91 const previousData = useRef<Settings>( data );
92
93 useEffect( () => {
94 // Don't save settings on the first render.
95 if ( isFirstRender.current ) {
96 isFirstRender.current = false;
97 previousData.current = data;
98 return;
99 }
100
101 // Only save if the data has actually changed.
102 if ( JSON.stringify( previousData.current ) === JSON.stringify( data ) ) {
103 return;
104 }
105
106 previousData.current = data;
107
108 apiFetch( {
109 path: '/wp-parsely/v2/settings/' + endpoint,
110 method: 'PUT',
111 data,
112 } );
113 }, deps ); // eslint-disable-line react-hooks/exhaustive-deps
114 };
115
116 /**
117 * The SettingsProvider component properties.
118 *
119 * @since 3.14.0
120 */
121 interface SettingsProviderProps {
122 children: ReactNode;
123 endpoint: string;
124 defaultSettings: Settings;
125 }
126
127 /**
128 * The SettingsProvider component.
129 *
130 * Provides the settings context to its children.
131 * It also saves the settings to the WordPress database whenever a setting change occurs.
132 * The settings are saved to the WordPress database using the useSaveSettings hook.
133 *
134 * @since 3.14.0
135 *
136 * @param {SettingsProviderProps} props The component's props.
137 *
138 * @return {import('react').JSX.Element} The SettingsProvider component.
139 */
140 export const SettingsProvider = (
141 { children, endpoint, defaultSettings }: Readonly<SettingsProviderProps>
142 ): React.JSX.Element => {
143 // Get the current settings from the store.
144 const { storedSettings } = useSelect( ( select ) => {
145 let settings = select( SettingsStore ).getSettings( endpoint );
146
147 // Set the default settings if empty.
148 if ( ! settings ) {
149 settings = defaultSettings;
150 dispatch( SettingsStore ).setSettings( endpoint, defaultSettings ).then( () => {} );
151 }
152
153 return {
154 storedSettings: settings,
155 };
156 }, [ defaultSettings, endpoint ] );
157
158 // Internal state for storing the settings.
159 const [ internalSettings, setInternalSettings ] = useState<Settings>( storedSettings );
160 const { setPartialSettings } = dispatch( SettingsStore );
161
162 /**
163 * Updates the settings in the internal state and in the store.
164 *
165 * @since 3.14.0
166 *
167 * @param {Partial<Settings>} updatedSettings The updated settings.
168 */
169 const updateSettings = useCallback( ( updatedSettings: Partial<Settings> ) => {
170 setInternalSettings( ( currentSettings ) => ( { ...currentSettings, ...updatedSettings } ) );
171 setPartialSettings( endpoint, updatedSettings );
172 }, [ endpoint, setPartialSettings ] );
173
174 /**
175 * Saves the settings into the WordPress database whenever a setting change
176 * occurs.
177 *
178 * internalSettings is the dependency, because we only want to save the settings
179 * when they change, and save it with the value in the store (storedSettings).
180 *
181 * @since 3.14.0
182 */
183 useSaveSettings( endpoint, storedSettings, [ internalSettings ] );
184
185 // Memoize the provider value to avoid unnecessary re-renders.
186 const providerValue = useMemo( () => (
187 { settings: storedSettings, setSettings: updateSettings }
188 ), [ storedSettings, updateSettings ] );
189
190 return (
191 <SettingsContext.Provider value={ providerValue }>
192 { children }
193 </SettingsContext.Provider>
194 );
195 };
196