PluginProbe
Extendify / 3.2.3
Extendify v3.2.3
3.2.3 3.2.2 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 All 129 releases
← All changes | src/Shared/components/ProductAccountActivation/ProductAccountActivation.jsx +87 -67 3.1.5 → 3.2.3 View file →
@@ -1,13 +1,15 @@
1 1 import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
2 +import { prewarmRecaptcha } from '@shared/api/pluginsActivation';
2 3 import { useEffect, useState } from '@wordpress/element';
3 4 import { isEmail } from '@wordpress/url';
5 +import { accountContext } from './accountContext';
4 6 import { createAccount } from './createAccount';
5 -import { Loading } from './Loading';
6 7 import { partitionPlugins } from './partitionPlugins';
7 8 import { SetupComplete } from './SetupComplete';
8 9 import { SetupPlugins } from './SetupPlugins';
9 10 import {
11 + ACCOUNT_STATUS,
10 12 ACTIVATION_STATUS,
11 13 usePluginsActivation,
12 14 } from './usePluginsActivation';
13 15
@@ -12,10 +14,9 @@
12 14 } from './usePluginsActivation';
13 15
14 16 export const ProductAccountActivation = () => {
15 17 const [isOpen, setIsOpen] = useState(true);
16 - const [isLoading, setIsLoading] = useState(false);
17 - const [isFinished, setIsFinished] = useState(false);
18 + const [submitted, setSubmitted] = useState(null);
18 19 const { offered, ineligible } = partitionPlugins(
19 20 window.extSharedData?.showProductActivation,
20 21 );
21 22 const [plugins, setPlugins] = useState(offered);
@@ -33,8 +34,16 @@
33 34 style.textContent = '.grecaptcha-badge { visibility: hidden; }';
34 35 document.head.appendChild(style);
35 36 }, []);
36 37
38 + useEffect(() => {
39 + // Site keys come from PHP, so the prewarm needs no SWR wait.
40 + for (const plugin of offered) {
41 + const siteKey = plugin.scriptData?.recaptchaSiteKey;
42 + if (siteKey) prewarmRecaptcha(siteKey).catch(() => {});
43 + }
44 + }, []);
45 +
37 46 const handleClose = () => {
38 47 activatePlugins({
39 48 status: ACTIVATION_STATUS.skipped,
40 49 });
@@ -40,91 +49,102 @@
40 49 });
41 50 setIsOpen(false);
42 51 };
43 52
53 + const trackStatus = (slug, request) => {
54 + const record = (status) =>
55 + setSubmitted((current) =>
56 + current.map((plugin) =>
57 + plugin.slug === slug ? { ...plugin, status } : plugin,
58 + ),
59 + );
60 +
61 + request.then(
62 + () => record(ACCOUNT_STATUS.success),
63 + () => record(ACCOUNT_STATUS.error),
64 + );
65 +
66 + return request;
67 + };
68 +
44 69 const handleCreateAccounts = async () => {
45 70 if (!isEmail(email)) return;
46 71
47 - setIsLoading(true);
72 + const selectedPlugins = plugins.filter((plugin) => plugin.selected);
48 73
49 - const selectedPlugins = plugins?.filter((plugin) => plugin.selected) ?? [];
74 + setSubmitted(
75 + selectedPlugins.map((plugin) => ({
76 + ...plugin,
77 + status: ACCOUNT_STATUS.pending,
78 + })),
79 + );
50 80
51 - const results = await Promise.allSettled(
81 + const settling = Promise.allSettled(
52 82 selectedPlugins.map((plugin) =>
53 - createAccount(plugin, {
54 - email,
55 - marketingConsent,
56 - termsAgreed,
57 - scriptData: { ...scriptData?.[plugin.slug], ...plugin.scriptData },
58 - }),
83 + trackStatus(
84 + plugin.slug,
85 + createAccount(plugin, {
86 + email,
87 + marketingConsent,
88 + termsAgreed,
89 + scriptData: { ...scriptData?.[plugin.slug], ...plugin.scriptData },
90 + }),
91 + ),
59 92 ),
60 93 );
61 94
62 - const context = Object.fromEntries(
63 - selectedPlugins.map((plugin, index) => {
64 - const result = results[index];
65 - const { requestTimeInMs, captchaTimeInMs, retries, errors } =
66 - result.status === 'fulfilled' ? result.value : result.reason;
67 - const entry = {
68 - status: result.status === 'fulfilled' ? 'success' : 'error',
69 - requestTimeInMs,
70 - captchaTimeInMs,
71 - endpoint: `extendify/v1/${plugin.slug}/create-account`,
72 - extendifyVersion: window.extSharedData?.version,
73 - retries,
74 - ...(errors.length > 0 && { errors }),
75 - };
76 - return [plugin.slug, entry];
77 - }),
78 - );
95 + // A record stuck on pending is a user who left before the answer.
96 + await activatePlugins({
97 + status: ACTIVATION_STATUS.completed,
98 + context: accountContext(selectedPlugins),
99 + });
79 100
101 + const results = await settling;
102 +
80 103 await activatePlugins({
81 104 status: ACTIVATION_STATUS.completed,
82 - context,
105 + context: accountContext(selectedPlugins, results),
83 106 });
84 -
85 - setIsFinished(true);
86 - setIsLoading(false);
87 107 };
88 108
89 109 return (
90 110 plugins.length > 0 && (
91 - <Dialog
92 - open={isOpen}
93 - onClose={() => {}}
94 - className="relative z-high extendify-shared"
95 - >
96 - <DialogBackdrop
97 - transition
98 - className="fixed inset-0 bg-black/30 transition-opacity data-closed:opacity-0"
99 - />
100 -
101 - <div className="z-10 fixed inset-0 flex w-screen items-center justify-center p-4 [body:has(#extendify-agent-chat)_&]:ml-96 [body:has(#extendify-agent-chat)_&]:w-[calc(100%-24rem)]">
102 - <DialogPanel
111 + <Dialog open={isOpen} onClose={() => {}} className="extendify-shared">
112 + {/* Utilities on the scope-class element itself never match the prefixed CSS. */}
113 + <div className="relative z-high">
114 + <DialogBackdrop
103 115 transition
104 - className="relative w-full max-w-208 bg-white rounded-lg shadow-xl transition-all data-closed:opacity-0 data-closed:scale-95"
105 - >
106 - {!isFinished && !isLoading && (
107 - <SetupPlugins
108 - plugins={plugins}
109 - setPlugins={setPlugins}
110 - handleCreateAccounts={handleCreateAccounts}
111 - email={email}
112 - setEmail={setEmail}
113 - handleClose={handleClose}
114 - marketingConsent={marketingConsent}
115 - setMarketingConsent={setMarketingConsent}
116 - termsAgreed={termsAgreed}
117 - setTermsAgreed={setTermsAgreed}
118 - />
119 - )}
116 + className="fixed inset-0 bg-black/30 transition-opacity data-closed:opacity-0"
117 + />
120 118
121 - {!isFinished && isLoading && <Loading />}
122 -
123 - {isFinished && (
124 - <SetupComplete handleClose={() => setIsOpen(false)} />
125 - )}
126 - </DialogPanel>
119 + <div className="z-10 fixed top-0 left-0 right-0 bottom-(--extendify-notification-bar-height,0px) flex items-center justify-center p-4 [body:has(#extendify-agent-chat)_&]:ml-96 [body:has(#extendify-agent-chat)_&]:w-[calc(100%-24rem)]">
120 + <DialogPanel
121 + transition
122 + className="relative w-full max-w-208 max-h-full overflow-hidden flex flex-col bg-white rounded-lg shadow-xl transition-all data-closed:opacity-0 data-closed:scale-95"
123 + >
124 + <div className="overflow-y-auto">
125 + {submitted ? (
126 + <SetupComplete
127 + plugins={submitted}
128 + handleClose={() => setIsOpen(false)}
129 + />
130 + ) : (
131 + <SetupPlugins
132 + plugins={plugins}
133 + setPlugins={setPlugins}
134 + handleCreateAccounts={handleCreateAccounts}
135 + email={email}
136 + setEmail={setEmail}
137 + handleClose={handleClose}
138 + marketingConsent={marketingConsent}
139 + setMarketingConsent={setMarketingConsent}
140 + termsAgreed={termsAgreed}
141 + setTermsAgreed={setTermsAgreed}
142 + />
143 + )}
144 + </div>
145 + </DialogPanel>
146 + </div>
127 147 </div>
128 148 </Dialog>
129 149 )
130 150 );