← 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 | ); |