PluginProbe
Extendify / trunk
Extendify vtrunk
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 → trunk 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 );