PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
3.2.1 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 All 127 releases
extendify / src / AutoLaunch / state / launch-data.js

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

196 lines 5.2 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 { launchStrings } from '@auto-launch/strings';
15 import { siteImageUrls } from '@shared/lib/site-images';
16 import { safeLocalStorage } from '@shared/state/safe-local-storage';
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: [launchStrings().statusBooting],
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 setData: (key, value) => {
68 if (!isValidKey(key)) return;
69 if (get()[key] === value) return; // avoid unnecessary updates
70 set({ [key]: value });
71 },
72 addStatusMessage: (message) => {
73 const currentMessages = get().statusMessages;
74 // remove any previous duplicates
75 const prev = currentMessages.filter((msg) => msg !== message);
76 set({ statusMessages: [...prev, message] });
77 },
78 setErrorMessage: (message) => {
79 set((state) => ({
80 errorMessage: message,
81 errorCount: state.errorCount + 1,
82 }));
83 },
84 needToStall: () => get().errorCount > 6,
85 resetErrorCount: () => {
86 set({ errorCount: 0 });
87 },
88 reset: ({ exclude }) => {
89 const newState = { ...initialState, attempt: get().attempt + 1 };
90 if (exclude && Array.isArray(exclude)) {
91 exclude.forEach((key) => {
92 if (!isValidKey(key)) return;
93 newState[key] = get()[key];
94 });
95 }
96 clearSiteImages().catch(() => null);
97 set(newState);
98 },
99 });
100
101 // Checks that a key being set is actually something we expect
102 const isValidKey = (key) => Object.keys(initialState).includes(key);
103
104 const keySchemas = {
105 urlParams: urlParamsShape,
106 siteProfile: getProfileShape,
107 launchDecisions: getLaunchDecisionsShape,
108 ...Object.fromEntries(
109 [
110 getLogoShape,
111 getPluginsShape,
112 getStyleShape,
113 getStringsShape,
114 getImagesShape,
115 getHomeShape,
116 getPagesShape,
117 getDesignBuildShape,
118 ].flatMap((s) => Object.entries(s.shape)),
119 ),
120 };
121
122 export const useLaunchDataStore = create(
123 persist(devtools(state, { name: 'Extendify Launch Data' }), {
124 name: `extendify-launch-data-${window.extSharedData.siteId}`,
125 storage: createJSONStorage(() => safeLocalStorage),
126 merge: (p, current) => {
127 // Make sure the persisted state is valid and not corrupted.
128 const persisted = p && typeof p === 'object' ? p : {};
129
130 // This gives us some recovery on page reload
131 const validated = Object.fromEntries(
132 Object.entries(persisted)
133 .filter(([key]) => key in keySchemas)
134 .map(([key, value]) => {
135 const result = keySchemas[key].safeParse(value);
136 return [key, result.success ? result.data : undefined];
137 }),
138 );
139 // Merge in the url params
140 const { title, description, go, ...urlParamsMapped } =
141 overrideWithUrlParams(urlParams);
142
143 return {
144 ...current,
145 ...persisted,
146 ...validated,
147 // If there's a URL param here it should override these values
148 title: title || persisted.title,
149 description: description || persisted.description,
150 descriptionRaw: description || persisted.descriptionRaw,
151 descriptionBackup:
152 persisted.descriptionBackup ||
153 description ||
154 (window.extLaunchData?.showLaunchTitle ? undefined : title),
155 go: go || persisted.go,
156 urlParams: {
157 title,
158 description,
159 go,
160 ...urlParamsMapped,
161 },
162 };
163 },
164 onRehydrateStorage: () => (state) => {
165 if (!state) return;
166 queueMicrotask(() => {
167 useLaunchDataStore.setState(state);
168 });
169 },
170 partialize: (state) => {
171 const {
172 statusMessages,
173 errorMessage,
174 errorCount,
175 description,
176 descriptionRaw,
177 title,
178 showExtendifyCodeScreen,
179 ...rest
180 } = state;
181 return Object.fromEntries(
182 Object.entries(rest).filter(([key, v]) => {
183 // An empty set means the fetch failed; keeping it skips the retry.
184 if (key === 'siteImages') return siteImageUrls(v).length > 0;
185 return Array.isArray(v) ? v.length > 0 : Boolean(v);
186 }),
187 );
188 },
189 }),
190 state,
191 );
192
193 export const clearPersistedLaunchData = () => {
194 useLaunchDataStore.persist.clearStorage();
195 };
196