PluginProbe
ElasticPress / 5.0.1
ElasticPress v5.0.1
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / assets / js / features / provider.js

provider.js in ElasticPress 5.0.1, at assets/js/features/provider.js

196 lines 4.6 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 {
6 createContext,
7 useCallback,
8 useContext,
9 useMemo,
10 useState,
11 WPElement,
12 } from '@wordpress/element';
13 import { isEqual } from 'lodash'; // eslint-disable-line import/no-extraneous-dependencies
14
15 /**
16 * Sync context.
17 */
18 const Context = createContext();
19
20 /**
21 * Feature settings provider.
22 *
23 * Provides data and methods for interacting with ElasticPress feature
24 * settings.
25 *
26 * @param {object} props Component props.
27 * @param {string} props.apiUrl API URL.
28 * @param {Function} props.children Component children
29 * @param {object} props.defaultSettings Default settings values.
30 * @param {string} props.epioLogoUrl ElasticPress.io logo URL.
31 * @param {object} props.features Features data.
32 * @param {object} props.indexMeta Index meta.
33 * @param {object} props.syncedSettings Settings at last sync.
34 * @returns {WPElement}
35 */
36 export const FeatureSettingsProvider = ({
37 apiUrl,
38 children,
39 defaultSettings,
40 epioLogoUrl,
41 features,
42 indexMeta,
43 syncedSettings,
44 }) => {
45 const [isBusy, setIsBusy] = useState(false);
46 const [isSyncing, setIsSyncing] = useState(!!indexMeta);
47 const [settings, setSettings] = useState({ ...defaultSettings });
48 const [savedSettings, setSavedSettings] = useState({ ...syncedSettings });
49
50 /**
51 * Get a feature's data by its slug.
52 *
53 * @param {string} slug Feature slug.
54 * @returns {object} Feature data.
55 */
56 const getFeature = useCallback(
57 (slug) => {
58 return features.find((f) => f.slug === slug);
59 },
60 [features],
61 );
62
63 /**
64 * Whether the settings have changes.
65 */
66 const isModified = useMemo(() => !isEqual(settings, savedSettings), [settings, savedSettings]);
67
68 /**
69 * Return whether a setting change will require a sync, based on the
70 * current value.
71 */
72 const willSettingRequireSync = useCallback((value, previousValue, requiresSync) => {
73 return requiresSync && value && value !== '0' && value !== previousValue;
74 }, []);
75
76 /**
77 * Return whether a feature requires a sync based on the current setting
78 * values.
79 *
80 * @returns {boolean}
81 */
82 const willFeatureRequireSync = useCallback(
83 (feature) => {
84 const { slug, settingsSchema } = feature;
85
86 return settingsSchema.some((s) => {
87 /**
88 * Settings that require a sync will only require a sync if the
89 * feature will be active.
90 */
91 if (settings?.[slug]?.active !== true) {
92 return false;
93 }
94
95 /**
96 * A setting requires a sync if it is flagged as requiring one,
97 * but only if it has a value. A sync is not required when a
98 * feature that requires a sync is disabled, for example.
99 */
100 const requiresSync = s.requires_sync;
101 const value = settings?.[slug]?.[s.key];
102 const previousValue = syncedSettings?.[slug]?.[s.key];
103
104 return willSettingRequireSync(value, previousValue, requiresSync);
105 });
106 },
107 [settings, syncedSettings, willSettingRequireSync],
108 );
109
110 /**
111 * The features that require a sync based on the current setting values.
112 */
113 const featuresRequiringSync = useMemo(() => {
114 return features.reduce((featuresRequiringSync, feature) => {
115 const settingsRequireSync = willFeatureRequireSync(feature);
116
117 if (settingsRequireSync) {
118 featuresRequiringSync.push(feature.slug);
119 }
120
121 return featuresRequiringSync;
122 }, []);
123 }, [features, willFeatureRequireSync]);
124
125 /**
126 * Whether a sync is required based on the current setting values.
127 */
128 const isSyncRequired = useMemo(() => {
129 return !!featuresRequiringSync.length;
130 }, [featuresRequiringSync]);
131
132 /**
133 * Handle resetting all settings.
134 *
135 * @returns {void}
136 */
137 const resetSettings = () => {
138 setSettings({ ...savedSettings });
139 };
140
141 /**
142 * Save settings.
143 *
144 * @returns {void}
145 */
146 const saveSettings = async () => {
147 try {
148 setIsBusy(true);
149
150 const response = await apiFetch({
151 body: JSON.stringify(settings),
152 headers: {
153 'Content-Type': 'application/json',
154 },
155 method: 'PUT',
156 url: apiUrl,
157 });
158
159 setSavedSettings(response.data);
160 } finally {
161 setIsBusy(false);
162 }
163 };
164
165 // eslint-disable-next-line react/jsx-no-constructed-context-values
166 const contextValue = {
167 epioLogoUrl,
168 features,
169 featuresRequiringSync,
170 getFeature,
171 isBusy,
172 isModified,
173 isSyncing,
174 setIsSyncing,
175 isSyncRequired,
176 resetSettings,
177 saveSettings,
178 savedSettings,
179 syncedSettings,
180 settings,
181 setSettings,
182 willSettingRequireSync,
183 };
184
185 return <Context.Provider value={contextValue}>{children}</Context.Provider>;
186 };
187
188 /**
189 * Use the API Search context.
190 *
191 * @returns {object} API Search Context.
192 */
193 export const useFeatureSettings = () => {
194 return useContext(Context);
195 };
196