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 / Launch / state / user-selections.js

user-selections.js in Extendify 3.1.5, at src/Launch/state/user-selections.js

209 lines 5.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { safeParseJson } from '@shared/lib/parsing';
2 import apiFetch from '@wordpress/api-fetch';
3 import { create } from 'zustand';
4 import { createJSONStorage, devtools, persist } from 'zustand/middleware';
5
6 const initialState = {
7 siteType: {
8 slug: '0default',
9 name: 'Default',
10 },
11 siteStructure: undefined,
12 siteProfile: undefined,
13 siteStrings: undefined,
14 siteImages: undefined,
15 siteInformation: {
16 title: window.extSharedData.siteTitle || '',
17 },
18 businessInformation: {
19 description: undefined,
20 tones: [],
21 acceptTerms: false,
22 },
23 siteObjective: undefined,
24 CTALink: undefined,
25 siteQA: {
26 showHidden: false,
27 questions: [],
28 },
29 attempt: 1,
30 sitePlugins: [],
31 urlParameters: {
32 title: null,
33 description: null,
34 objective: null,
35 structure: null,
36 tone: null,
37 skip: null,
38 },
39 };
40
41 const setIfChanged = (get, set, key, newValue) => {
42 const current = get()[key];
43 if (current === newValue) return;
44 set({ [key]: newValue });
45 };
46
47 const incoming = safeParseJson(window.extSharedData.launchDataLegacy);
48 const state = (set, get) => ({
49 ...initialState,
50 // initialize the state with default values
51 ...(incoming?.state ?? {}),
52 setSiteStructure: (siteStructure) =>
53 setIfChanged(get, set, 'siteStructure', siteStructure),
54 setSiteInformation: (name, value) => {
55 const current = get().siteInformation?.[name];
56 if (current === value) return;
57
58 const siteInformation = { ...get().siteInformation, [name]: value };
59 set({ siteInformation });
60 },
61 setBusinessInformation: (name, value) => {
62 const current = get().businessInformation?.[name];
63 if (current === value) return;
64
65 const businessInformation = { ...get().businessInformation, [name]: value };
66 set({ businessInformation });
67 },
68 setSiteProfile: (data) => {
69 set({
70 siteProfile: undefined,
71 siteStrings: undefined,
72 siteImages: undefined,
73 });
74 if (!data) data = {};
75 const siteProfile = Object.assign(
76 {
77 aiSiteType: null,
78 aiSiteCategory: null,
79 aiDescription: null,
80 aiKeywords: [],
81 },
82 data,
83 );
84 set({ siteProfile });
85 },
86 setSiteStrings: (data) => {
87 if (!data) data = {};
88 const siteStrings = Object.assign(
89 { aiHeaders: [], aiBlogTitles: [] },
90 data,
91 );
92 set({ siteStrings });
93 },
94 setSiteImages: (data) => {
95 if (!data) data = {};
96 const siteImages = Object.assign({ siteImages: [] }, data);
97 set({ siteImages });
98 },
99 setSiteObjective: (siteObjective) =>
100 setIfChanged(get, set, 'siteObjective', siteObjective),
101 setCTALink: (CTALink) => setIfChanged(get, set, 'CTALink', CTALink),
102 has: (type, item) => {
103 if (!item?.id) return false;
104 return (get()?.[type] ?? [])?.some((t) => t.id === item.id);
105 },
106 add: (type, item) => {
107 if (get().has(type, item)) return;
108 set({ [type]: [...(get()?.[type] ?? []), item] });
109 },
110 addMany: (type, items, options = {}) => {
111 if (options.clearExisting) {
112 set({ [type]: items });
113 return;
114 }
115 set({ [type]: [...(get()?.[type] ?? []), ...items] });
116 },
117 remove: (type, item) =>
118 set({ [type]: get()?.[type]?.filter((t) => t.id !== item.id) }),
119 removeMany: (type, items) => {
120 set({
121 [type]: get()?.[type]?.filter((t) => !items.some((i) => i.id === t.id)),
122 });
123 },
124 removeAll: (type) => set({ [type]: [] }),
125 toggle: (type, item) => {
126 if (get().has(type, item)) {
127 get().remove(type, item);
128 return;
129 }
130 get().add(type, item);
131 },
132 resetState: () =>
133 set((state) => ({ ...initialState, attempt: state?.attempt + 1 })),
134 setVariation: (variation) => set({ variation }),
135 setSiteQuestions: (questions) => set({ siteQA: questions }),
136 setSiteQuestionAnswer: (
137 questionId,
138 answer,
139 { isExtraField = false, extraFieldKey = null } = {},
140 ) => {
141 set((state) => {
142 const { siteQA } = state;
143
144 const questions = siteQA?.questions.map((q) => {
145 if (q.id !== questionId) return q;
146
147 if (!isExtraField) {
148 return { ...q, answerUser: answer };
149 }
150
151 // isExtraField === true
152 const updatedExtraFields = q.extraFields?.map((ef) =>
153 ef.key === extraFieldKey ? { ...ef, answer } : ef,
154 );
155
156 return { ...q, extraFields: updatedExtraFields };
157 });
158
159 return {
160 siteQA: {
161 ...siteQA,
162 questions,
163 },
164 };
165 });
166 },
167 setShowHiddenQuestions: (showHidden) => {
168 const current = get().siteQA?.showHidden;
169 if (current === showHidden) return;
170
171 set({ siteQA: { ...get().siteQA, showHidden } });
172 },
173 setUrlParameters: (params) =>
174 set((state) => {
175 if (!params || Object.keys(params).length === 0) return state;
176 const prev = state.urlParameters;
177 const same = Object.entries(params).every(([k, v]) => v === prev[k]);
178
179 if (same) return state;
180 return { urlParameters: { ...prev, ...params } };
181 }),
182 });
183
184 const debounce = (func, delay) => {
185 let timeoutId;
186 return (...params) => {
187 clearTimeout(timeoutId);
188 timeoutId = setTimeout(() => func(...params), delay);
189 };
190 };
191
192 const path = '/extendify/v1/shared/user-selections-data';
193 const storage = {
194 getItem: async () => await apiFetch({ path }),
195 setItem: debounce(
196 async (_name, state) =>
197 await apiFetch({ path, method: 'POST', data: { state } }),
198 300,
199 ),
200 };
201
202 export const useUserSelectionStore = create(
203 persist(devtools(state, { name: 'Extendify User Selections' }), {
204 storage: createJSONStorage(() => storage),
205 skipHydration: true,
206 }),
207 state,
208 );
209