PluginProbe
Extendify / 3.2.0
Extendify v3.2.0
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 / Agent / workflows / plugins / components / InstallPlugin.jsx

InstallPlugin.jsx in Extendify 3.2.0, at src/Agent/workflows/plugins/components/InstallPlugin.jsx

207 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { recordPluginActivity } from '@shared/api/DataApi';
2 import { activatePlugin, installPlugin } from '@shared/api/wp';
3 import { useEffect, useState } from '@wordpress/element';
4 import { __, sprintf } from '@wordpress/i18n';
5 import { external, Icon } from '@wordpress/icons';
6
7 export const InstallPlugin = ({ inputs, onConfirm, onCancel }) => {
8 const [status, setStatus] = useState('idle');
9 const handleConfirm = () => {
10 setStatus('installing');
11 };
12
13 useEffect(() => {
14 if (!inputs.alreadyInstalled) return;
15 const id = requestAnimationFrame(() => {
16 onCancel?.();
17 });
18 return () => cancelAnimationFrame(id);
19 }, [inputs, onCancel]);
20
21 useEffect(() => {
22 let cancelled = false;
23
24 const run = async () => {
25 if (status === 'installing') {
26 try {
27 await installPlugin(inputs.pluginSlug);
28 if (cancelled) return;
29 setStatus('activating');
30 } catch (error) {
31 if (cancelled) return;
32 if (error?.code === 'folder_exists') {
33 setStatus('activating');
34 return;
35 }
36 setStatus('error');
37 }
38 return;
39 }
40
41 if (status === 'activating') {
42 try {
43 await activatePlugin(inputs.pluginSlug);
44 } catch {
45 try {
46 await new Promise((r) => setTimeout(r, 500));
47 await activatePlugin(inputs.pluginSlug);
48 } catch {
49 if (!cancelled) setStatus('error');
50 return;
51 }
52 }
53 if (cancelled) return;
54
55 try {
56 await recordPluginActivity({
57 slug: inputs.pluginSlug,
58 source: 'ai-agent-recommendation',
59 });
60 await adminLoader();
61
62 if (!cancelled) onConfirm({ shouldRefreshPage: true });
63 } catch {
64 if (!cancelled) setStatus('error');
65 }
66 }
67 };
68 run();
69 return () => {
70 cancelled = true;
71 };
72 }, [status, onConfirm, inputs]);
73
74 if (status === 'error') {
75 return (
76 <Wrapper>
77 <Content>
78 <p className="m-0 p-0 text-sm text-gray-900">
79 {__(
80 'There was an error installing the plugin. You may try again',
81 'extendify-local',
82 )}
83 </p>
84 </Content>
85 <div className="flex justify-start gap-2 p-3">
86 <CancelButton onClick={onCancel} />
87 <ConfirmButton
88 onClick={handleConfirm}
89 text={__('Try Again', 'extendify-local')}
90 />
91 </div>
92 </Wrapper>
93 );
94 }
95
96 if (['installing', 'activating'].includes(status)) {
97 return (
98 <Wrapper>
99 <Content>
100 <p className="m-0 p-0 text-sm text-gray-900">
101 {status === 'installing'
102 ? __('Installing plugin...', 'extendify-local')
103 : __('Activating plugin...', 'extendify-local')}
104 </p>
105 </Content>
106 </Wrapper>
107 );
108 }
109
110 return (
111 <Wrapper>
112 <Content>
113 <div className="flex flex-col gap-3">
114 <p className="m-0 p-0 text-sm text-gray-900">
115 {__(
116 'The agent is requesting to install and activate a plugin.',
117 'extendify-local',
118 )}
119 </p>
120 <div className="flex-col gap-1">
121 {inputs.pluginName && (
122 <div className="flex-1 font-bold">{inputs.pluginName}</div>
123 )}
124 <a
125 className="text-xs flex flex-1 items-end text-gray-900 hover:text-gray-900 hover:decoration-solid"
126 href={`https://wordpress.org/plugins/${inputs.pluginSlug}`}
127 target="_blank"
128 rel="noopener noreferrer"
129 >
130 <span
131 dangerouslySetInnerHTML={{
132 __html: sprintf(
133 // translators: %1$s and %2$s are HTML tags. %1$s opens a span with screen-reader-only text, and %2$s closes that span.
134 __(
135 'Plugin Details %1$s(opens in a new tab)%2$s',
136 'extendify-local',
137 ),
138 '<span class="sr-only">',
139 '</span>',
140 ),
141 }}
142 />
143 <Icon className="ml-1 inline-block" icon={external} size={18} />
144 </a>
145 </div>
146 </div>
147 </Content>
148 <div className="flex justify-start gap-2 p-3">
149 <CancelButton onClick={onCancel} />
150 <ConfirmButton
151 onClick={handleConfirm}
152 text={__('Install Plugin', 'extendify-local')}
153 />
154 </div>
155 </Wrapper>
156 );
157 };
158
159 const CancelButton = ({ onClick }) => (
160 <button
161 type="button"
162 className="w-full rounded-sm border border-gray-500 bg-white p-2 text-sm text-gray-900"
163 onClick={onClick}
164 >
165 {__('Cancel', 'extendify-local')}
166 </button>
167 );
168
169 const ConfirmButton = ({ onClick, text }) => (
170 <button
171 type="button"
172 className="w-full rounded-sm border border-design-main bg-design-main p-2 text-sm text-white"
173 onClick={onClick}
174 >
175 {text}
176 </button>
177 );
178
179 const Wrapper = ({ children }) => (
180 <div className="mb-4 ms-12 me-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50">
181 {children}
182 </div>
183 );
184
185 const Content = ({ children }) => (
186 <div className="rounded-lg border-b border-gray-300 bg-white">
187 <div className="p-3">{children}</div>
188 </div>
189 );
190
191 const adminLoader = () => {
192 return new Promise((resolve) => {
193 const { adminUrl } = window.extSharedData;
194 const iframe = Object.assign(document.createElement('iframe'), {
195 title: 'Admin Loader',
196 src: adminUrl,
197 style: 'display: none;',
198 sandbox: 'allow-same-origin allow-scripts allow-forms',
199 onload: () => {
200 document.body.removeChild(iframe);
201 resolve();
202 },
203 });
204 document.body.appendChild(iframe);
205 });
206 };
207