PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / src / AutoLaunch / state / launch-data.js

launch-data.js in Extendify 3.1.5, at src/AutoLaunch/state/launch-data.js

199 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {
2 getDesignBuildShape,
3 getHomeShape,
4 getImagesShape,
5 getLaunchDecisionsShape,
6 getLogoShape,
7 getPagesShape,
8 getPluginsShape,
9 getProfileShape,
10 getStringsShape,
11 getStyleShape,
12 } from '@auto-launch/fetchers/shape';
13 import { clearSiteImages } from '@auto-launch/functions/wp';
14 import { siteImageUrls } from '@shared/lib/site-images';
15 import { safeLocalStorage } from '@shared/state/safe-local-storage';
16 import { __ } from '@wordpress/i18n';
17 import { create } from 'zustand';
18 import { createJSONStorage, devtools, persist } from 'zustand/middleware';
19 import { overrideWithUrlParams, urlParams, urlParamsShape } from './url-params';
20
21 const shapeToKeyValue = (shape) => {
22 return Object.fromEntries(
23 Object.keys(shape.shape).map((key) => [key, undefined]),
24 );
25 };
26
27 const initialState = {
28 go: false,
29 showExtendifyCodeScreen: false,
30 // translators: this is for a action log UI. Keep it short
31 statusMessages: [__('Booting things up', 'extendify-local')],
32 errorMessage: null,
33 errorCount: 0,
34 title: null,
35 description: null,
36 descriptionBackup: undefined,
37 descriptionRaw: null,
38 urlParams: {},
39 siteProfile: {
40 ...shapeToKeyValue(getProfileShape),
41 },
42 launchDecisions: {
43 ...shapeToKeyValue(getLaunchDecisionsShape),
44 },
45 ...shapeToKeyValue(getLogoShape),
46 ...shapeToKeyValue(getPluginsShape),
47 ...shapeToKeyValue(getStyleShape),
48 ...shapeToKeyValue(getStringsShape),
49 ...shapeToKeyValue(getImagesShape),
50 ...shapeToKeyValue(getHomeShape),
51 ...shapeToKeyValue(getPagesShape),
52 ...shapeToKeyValue(getDesignBuildShape),
53 designBuild: undefined,
54 attempt: 1,
55 };
56
57 const state = (set, get) => ({
58 ...initialState,
59 urlParams: {
60 ...initialState.urlParams,
61 ...urlParams,
62 },
63 title: undefined,
64 description: undefined,
65 descriptionBackup: undefined,
66 descriptionRaw: undefined,
67 pulse: false,
68 setPulse: (value) => set({ pulse: value }),
69 setData: (key, value) => {
70 if (!isValidKey(key)) return;
71 if (get()[key] === value) return; // avoid unnecessary updates
72 set({ [key]: value });
73 },
74 addStatusMessage: (message) => {
75 const currentMessages = get().statusMessages;
76 // remove any previous duplicates
77 const prev = currentMessages.filter((msg) => msg !== message);
78 set({ statusMessages: [...prev, message] });
79 },
80 setErrorMessage: (message) => {
81 set((state) => ({
82 errorMessage: message,
83 errorCount: state.errorCount + 1,
84 }));
85 },
86 needToStall: () => get().errorCount > 6,
87 resetErrorCount: () => {
88 set({ errorCount: 0 });
89 },
90 reset: ({ exclude }) => {
91 const newState = { ...initialState, attempt: get().attempt + 1 };
92 if (exclude && Array.isArray(exclude)) {
93 exclude.forEach((key) => {
94 if (!isValidKey(key)) return;
95 newState[key] = get()[key];
96 });
97 }
98 clearSiteImages().catch(() => null);
99 set(newState);
100 },
101 });
102
103 // Checks that a key being set is actually something we expect
104 const isValidKey = (key) => Object.keys(initialState).includes(key);
105
106 const keySchemas = {
107 urlParams: urlParamsShape,
108 siteProfile: getProfileShape,
109 launchDecisions: getLaunchDecisionsShape,
110 ...Object.fromEntries(
111 [
112 getLogoShape,
113 getPluginsShape,
114 getStyleShape,
115 getStringsShape,
116 getImagesShape,
117 getHomeShape,
118 getPagesShape,
119 getDesignBuildShape,
120 ].flatMap((s) => Object.entries(s.shape)),
121 ),
122 };
123
124 export const useLaunchDataStore = create(
125 persist(devtools(state, { name: 'Extendify Launch Data' }), {
126 name: `extendify-launch-data-${window.extSharedData.siteId}`,
127 storage: createJSONStorage(() => safeLocalStorage),
128 merge: (p, current) => {
129 // Make sure the persisted state is valid and not corrupted.
130 const persisted = p && typeof p === 'object' ? p : {};
131
132 // This gives us some recovery on page reload
133 const validated = Object.fromEntries(
134 Object.entries(persisted)
135 .filter(([key]) => key in keySchemas)
136 .map(([key, value]) => {
137 const result = keySchemas[key].safeParse(value);
138 return [key, result.success ? result.data : undefined];
139 }),
140 );
141 // Merge in the url params
142 const { title, description, go, ...urlParamsMapped } =
143 overrideWithUrlParams(urlParams);
144
145 return {
146 ...current,
147 ...persisted,
148 ...validated,
149 // If there's a URL param here it should override these values
150 title: title || persisted.title,
151 description: description || persisted.description,
152 descriptionRaw: description || persisted.descriptionRaw,
153 descriptionBackup:
154 persisted.descriptionBackup ||
155 description ||
156 (window.extLaunchData?.showLaunchTitle ? undefined : title),
157 go: go || persisted.go,
158 urlParams: {
159 title,
160 description,
161 go,
162 ...urlParamsMapped,
163 },
164 };
165 },
166 onRehydrateStorage: () => (state) => {
167 if (!state) return;
168 queueMicrotask(() => {
169 useLaunchDataStore.setState(state);
170 });
171 },
172 partialize: (state) => {
173 const {
174 statusMessages,
175 errorMessage,
176 errorCount,
177 pulse,
178 description,
179 descriptionRaw,
180 title,
181 showExtendifyCodeScreen,
182 ...rest
183 } = state;
184 return Object.fromEntries(
185 Object.entries(rest).filter(([key, v]) => {
186 // An empty set means the fetch failed; keeping it skips the retry.
187 if (key === 'siteImages') return siteImageUrls(v).length > 0;
188 return Array.isArray(v) ? v.length > 0 : Boolean(v);
189 }),
190 );
191 },
192 }),
193 state,
194 );
195
196 export const clearPersistedLaunchData = () => {
197 useLaunchDataStore.persist.clearStorage();
198 };
199