@abstraxn/signer-react 2.2.1 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js +31 -88
- package/dist/src/components/AbstraxnProvider/AbstraxnProviderInner.js.map +1 -1
- package/dist/src/components/AbstraxnProvider/useWalletInitialization.js +6 -3
- package/dist/src/components/AbstraxnProvider/useWalletInitialization.js.map +1 -1
- package/dist/src/components/AbstraxnProvider/utils.js +1 -12
- package/dist/src/components/AbstraxnProvider/utils.js.map +1 -1
- package/dist/src/components/OnboardingUI/OnboardingUIReact.js +13 -32
- package/dist/src/components/OnboardingUI/OnboardingUIReact.js.map +1 -1
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +1 -0
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +31 -45
- package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -1
- package/dist/src/components/OnboardingUI/components/EmailForm.js +10 -1
- package/dist/src/components/OnboardingUI/components/EmailForm.js.map +1 -1
- package/dist/src/components/OnboardingUI/components/MfaForm.js +10 -3
- package/dist/src/components/OnboardingUI/components/MfaForm.js.map +1 -1
- package/dist/src/components/OnboardingUI/components/OtpForm.js +11 -4
- package/dist/src/components/OnboardingUI/components/OtpForm.js.map +1 -1
- package/dist/src/components/OnboardingUI/hooks/useAuthMethods.js +6 -3
- package/dist/src/components/OnboardingUI/hooks/useAuthMethods.js.map +1 -1
- package/dist/src/components/OnboardingUI/hooks/useOnboarding.js +2 -2
- package/dist/src/components/OnboardingUI/hooks/useOnboarding.js.map +1 -1
- package/dist/src/components/WalletModal/components/ManageWalletModal.js +105 -126
- package/dist/src/components/WalletModal/components/ManageWalletModal.js.map +1 -1
- package/dist/src/hooks.d.ts +59 -1
- package/dist/src/hooks.js +200 -0
- package/dist/src/hooks.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -11,11 +11,15 @@ import { FiUsers, FiLink } from "react-icons/fi";
|
|
|
11
11
|
import { TfiEmail } from "react-icons/tfi";
|
|
12
12
|
import { BiShieldQuarter } from "react-icons/bi";
|
|
13
13
|
import { useAbstraxnWallet } from "../../../AbstraxnProvider";
|
|
14
|
+
import { useEnableMfa } from "../../../hooks";
|
|
14
15
|
import "./ManageWalletModal.css";
|
|
15
16
|
import { QRCodeSVG } from "qrcode.react";
|
|
16
17
|
function normalizeLinkedProvider(authProvider) {
|
|
17
18
|
return authProvider === "otp" ? "email" : authProvider;
|
|
18
19
|
}
|
|
20
|
+
function normalizeErrorMessage(message) {
|
|
21
|
+
return message.replace(/^network error:\s*/i, "");
|
|
22
|
+
}
|
|
19
23
|
function AuthMethodIcon({ provider }) {
|
|
20
24
|
const p = provider.toLowerCase();
|
|
21
25
|
if (p === "google")
|
|
@@ -32,7 +36,7 @@ function AuthMethodIcon({ provider }) {
|
|
|
32
36
|
}
|
|
33
37
|
export function ManageWalletModal({ ...props }) {
|
|
34
38
|
const { isOpen, onClose, isExternalWalletConnected, onExportPrivateKey, theme = "dark", userEmail, organizationName, } = props;
|
|
35
|
-
const { getSupportedAuthMethods, getLinkedAuthMethods, linkAuthMethod, linkEmail, loginWithOTP, requestOtpForEmailLink, unlinkAuthMethod, whoami, getMfaStatus,
|
|
39
|
+
const { getSupportedAuthMethods, getLinkedAuthMethods, linkAuthMethod, linkEmail, loginWithOTP, requestOtpForEmailLink, unlinkAuthMethod, whoami, getMfaStatus, verifyMfa, disableMfaWithSignedPayload, } = useAbstraxnWallet();
|
|
36
40
|
const [showLinkedProfilesList, setShowLinkedProfilesList] = useState(false);
|
|
37
41
|
const [supportedMethods, setSupportedMethods] = useState([]);
|
|
38
42
|
const [linkedMethods, setLinkedMethods] = useState([]);
|
|
@@ -50,21 +54,13 @@ export function ManageWalletModal({ ...props }) {
|
|
|
50
54
|
const [emailLinkResending, setEmailLinkResending] = useState(false);
|
|
51
55
|
const [emailLinkResendCooldown, setEmailLinkResendCooldown] = useState(0);
|
|
52
56
|
const emailLinkOtpInputsRef = useRef([]);
|
|
53
|
-
// MFA state
|
|
54
57
|
const [mfaStatus, setMfaStatus] = useState(null);
|
|
55
58
|
const [mfaLoading, setMfaLoading] = useState(false);
|
|
56
|
-
const [mfaError, setMfaError] = useState(null);
|
|
57
|
-
// MFA setup flow state (QR + secret + TOTP + backup codes)
|
|
58
59
|
const [showMfaSetupModal, setShowMfaSetupModal] = useState(false);
|
|
59
|
-
const [
|
|
60
|
-
const [mfaQrCode, setMfaQrCode] = useState(null);
|
|
61
|
-
const [mfaSecret, setMfaSecret] = useState(null);
|
|
60
|
+
const [showMfaBackupModal, setShowMfaBackupModal] = useState(false);
|
|
62
61
|
const [mfaSetupCode, setMfaSetupCode] = useState("");
|
|
63
62
|
const [mfaRecoveryCode, setMfaRecoveryCode] = useState("");
|
|
64
|
-
const
|
|
65
|
-
const [mfaSetupError, setMfaSetupError] = useState(null);
|
|
66
|
-
const [mfaBackupCodes, setMfaBackupCodes] = useState([]);
|
|
67
|
-
const [showMfaBackupModal, setShowMfaBackupModal] = useState(false);
|
|
63
|
+
const { canEnableMfaFlow, setupState, isLoading: mfaSetupLoading, error: mfaSetupError, start, goToSetup, goToVerify, goToRecovery, verifyCode, verifyRecoveryCode, reset, } = useEnableMfa();
|
|
68
64
|
const [mfaSecretCopied, setMfaSecretCopied] = useState(false);
|
|
69
65
|
const [mfaBackupCodesCopied, setMfaBackupCodesCopied] = useState(false);
|
|
70
66
|
const mfaSetupVerifyButtonRef = useRef(null);
|
|
@@ -78,6 +74,10 @@ export function ManageWalletModal({ ...props }) {
|
|
|
78
74
|
const [mfaDisableLoading, setMfaDisableLoading] = useState(false);
|
|
79
75
|
const [mfaDisableError, setMfaDisableError] = useState(null);
|
|
80
76
|
const mfaDisableVerifyButtonRef = useRef(null);
|
|
77
|
+
const hasFetchedMfaForOpenRef = useRef(false);
|
|
78
|
+
const hasFetchedLinkedProfilesForOpenRef = useRef(false);
|
|
79
|
+
const linkedProfilesFetchPromiseRef = useRef(null);
|
|
80
|
+
const mfaStatusRequestInFlightRef = useRef(false);
|
|
81
81
|
const showFeedback = useCallback((message, type = "success") => {
|
|
82
82
|
if (type === "error") {
|
|
83
83
|
setLinkUnlinkError(message);
|
|
@@ -88,35 +88,48 @@ export function ManageWalletModal({ ...props }) {
|
|
|
88
88
|
const fetchLinkedProfilesData = useCallback(async () => {
|
|
89
89
|
if (!getSupportedAuthMethods || !getLinkedAuthMethods || isExternalWalletConnected)
|
|
90
90
|
return;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const [supportedRes, linkedRes] = await Promise.all([
|
|
95
|
-
getSupportedAuthMethods(),
|
|
96
|
-
getLinkedAuthMethods(),
|
|
97
|
-
]);
|
|
98
|
-
const loginProvider = whoami?.loginProvider?.toLowerCase();
|
|
99
|
-
const supported = supportedRes.supportedMethods ?? [];
|
|
100
|
-
const filteredSupported = loginProvider != null && loginProvider !== ""
|
|
101
|
-
? supported.filter((method) => method.provider.toLowerCase() !== loginProvider)
|
|
102
|
-
: supported;
|
|
103
|
-
setSupportedMethods(filteredSupported);
|
|
104
|
-
setLinkedMethods(linkedRes.linkedMethods ?? []);
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
const msg = err instanceof Error ? err.message : "Failed to load auth methods";
|
|
108
|
-
setLinkedError(msg);
|
|
109
|
-
setSupportedMethods([]);
|
|
110
|
-
setLinkedMethods([]);
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
setLinkedLoading(false);
|
|
91
|
+
if (linkedProfilesFetchPromiseRef.current) {
|
|
92
|
+
await linkedProfilesFetchPromiseRef.current;
|
|
93
|
+
return;
|
|
114
94
|
}
|
|
95
|
+
const fetchPromise = (async () => {
|
|
96
|
+
setLinkedLoading(true);
|
|
97
|
+
setLinkedError(null);
|
|
98
|
+
try {
|
|
99
|
+
const [supportedRes, linkedRes] = await Promise.all([
|
|
100
|
+
getSupportedAuthMethods(),
|
|
101
|
+
getLinkedAuthMethods(),
|
|
102
|
+
]);
|
|
103
|
+
const loginProvider = whoami?.loginProvider?.toLowerCase();
|
|
104
|
+
const supported = supportedRes.supportedMethods ?? [];
|
|
105
|
+
const filteredSupported = loginProvider != null && loginProvider !== ""
|
|
106
|
+
? supported.filter((method) => method.provider.toLowerCase() !== loginProvider)
|
|
107
|
+
: supported;
|
|
108
|
+
setSupportedMethods(filteredSupported);
|
|
109
|
+
setLinkedMethods(linkedRes.linkedMethods ?? []);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
const msg = err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to load auth methods";
|
|
113
|
+
setLinkedError(msg);
|
|
114
|
+
setSupportedMethods([]);
|
|
115
|
+
setLinkedMethods([]);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
setLinkedLoading(false);
|
|
119
|
+
}
|
|
120
|
+
})();
|
|
121
|
+
linkedProfilesFetchPromiseRef.current = fetchPromise;
|
|
122
|
+
await fetchPromise.finally(() => {
|
|
123
|
+
linkedProfilesFetchPromiseRef.current = null;
|
|
124
|
+
});
|
|
115
125
|
}, [getSupportedAuthMethods, getLinkedAuthMethods, isExternalWalletConnected, whoami]);
|
|
116
126
|
const handleOpenLinkedProfiles = useCallback(() => {
|
|
117
127
|
setShowLinkedProfilesList(true);
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
const hasLinkedProfilesData = supportedMethods.length > 0 || linkedMethods.length > 0;
|
|
129
|
+
if (!hasLinkedProfilesData && !linkedLoading) {
|
|
130
|
+
fetchLinkedProfilesData();
|
|
131
|
+
}
|
|
132
|
+
}, [fetchLinkedProfilesData, linkedLoading, linkedMethods.length, supportedMethods.length]);
|
|
120
133
|
const isProviderLinked = useCallback((provider) => {
|
|
121
134
|
return linkedMethods.some((m) => normalizeLinkedProvider(m.authProvider) === provider.toLowerCase());
|
|
122
135
|
}, [linkedMethods]);
|
|
@@ -141,7 +154,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
141
154
|
await fetchLinkedProfilesData();
|
|
142
155
|
}
|
|
143
156
|
catch (err) {
|
|
144
|
-
const msg = err instanceof Error ? err.message : "Failed to connect";
|
|
157
|
+
const msg = err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to connect";
|
|
145
158
|
setLinkUnlinkError(msg);
|
|
146
159
|
}
|
|
147
160
|
finally {
|
|
@@ -165,7 +178,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
165
178
|
showFeedback("Check your email for the code", "success");
|
|
166
179
|
}
|
|
167
180
|
catch (err) {
|
|
168
|
-
const msg = err instanceof Error ? err.message : "Failed to send OTP";
|
|
181
|
+
const msg = err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to send OTP";
|
|
169
182
|
setEmailLinkError(msg);
|
|
170
183
|
showFeedback(msg, "error");
|
|
171
184
|
}
|
|
@@ -189,7 +202,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
189
202
|
showFeedback("Code resent", "success");
|
|
190
203
|
}
|
|
191
204
|
catch (err) {
|
|
192
|
-
const msg = err instanceof Error ? err.message : "Failed to resend";
|
|
205
|
+
const msg = err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to resend";
|
|
193
206
|
setEmailLinkError(msg);
|
|
194
207
|
showFeedback(msg, "error");
|
|
195
208
|
}
|
|
@@ -214,7 +227,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
214
227
|
await fetchLinkedProfilesData();
|
|
215
228
|
}
|
|
216
229
|
catch (err) {
|
|
217
|
-
const msg = err instanceof Error ? err.message : "Failed to link email";
|
|
230
|
+
const msg = err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to link email";
|
|
218
231
|
setEmailLinkError(msg);
|
|
219
232
|
showFeedback(msg, "error");
|
|
220
233
|
}
|
|
@@ -277,33 +290,34 @@ export function ManageWalletModal({ ...props }) {
|
|
|
277
290
|
handleLinkEmailSubmit();
|
|
278
291
|
}
|
|
279
292
|
}, [emailLinkOtpCode, actionProvider, emailLinkResending, emailLinkError, handleLinkEmailSubmit]);
|
|
293
|
+
const mfaSetupStep = setupState?.step === "complete" || !setupState ? "setup" : setupState.step;
|
|
294
|
+
const mfaQrCode = setupState?.step === "setup" ? setupState.qrCode : null;
|
|
295
|
+
const mfaSecret = setupState?.step === "setup" ? setupState.secret : null;
|
|
296
|
+
const mfaBackupCodes = setupState?.step === "complete" ? setupState.backupCodes : [];
|
|
280
297
|
const refreshMfaStatus = useCallback(async () => {
|
|
281
|
-
if (!getMfaStatus)
|
|
298
|
+
if (!getMfaStatus || mfaStatusRequestInFlightRef.current)
|
|
282
299
|
return;
|
|
300
|
+
mfaStatusRequestInFlightRef.current = true;
|
|
283
301
|
setMfaLoading(true);
|
|
284
|
-
setMfaError(null);
|
|
285
302
|
try {
|
|
286
303
|
const status = await getMfaStatus();
|
|
287
304
|
setMfaStatus(status);
|
|
288
305
|
}
|
|
289
|
-
catch
|
|
290
|
-
|
|
291
|
-
setMfaError(msg);
|
|
306
|
+
catch {
|
|
307
|
+
setMfaStatus(null);
|
|
292
308
|
}
|
|
293
309
|
finally {
|
|
294
310
|
setMfaLoading(false);
|
|
311
|
+
mfaStatusRequestInFlightRef.current = false;
|
|
295
312
|
}
|
|
296
313
|
}, [getMfaStatus]);
|
|
297
314
|
const resetMfaSetupState = useCallback(() => {
|
|
298
|
-
setMfaSetupStep("setup");
|
|
299
|
-
setMfaQrCode(null);
|
|
300
|
-
setMfaSecret(null);
|
|
301
315
|
setMfaSetupCode("");
|
|
302
316
|
setMfaRecoveryCode("");
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}, []);
|
|
317
|
+
setShowMfaSetupModal(false);
|
|
318
|
+
setShowMfaBackupModal(false);
|
|
319
|
+
reset();
|
|
320
|
+
}, [reset]);
|
|
307
321
|
const resetMfaDisableState = useCallback(() => {
|
|
308
322
|
setShowMfaDisableAuthModal(false);
|
|
309
323
|
setShowMfaDisableConfirmModal(false);
|
|
@@ -325,7 +339,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
325
339
|
setMfaDisableCode("");
|
|
326
340
|
}
|
|
327
341
|
catch (err) {
|
|
328
|
-
setMfaDisableError(err instanceof Error ? err.message : "Invalid authentication code");
|
|
342
|
+
setMfaDisableError(err instanceof Error ? normalizeErrorMessage(err.message) : "Invalid authentication code");
|
|
329
343
|
}
|
|
330
344
|
finally {
|
|
331
345
|
setMfaDisableLoading(false);
|
|
@@ -348,7 +362,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
348
362
|
setMfaDisableRecoveryCode("");
|
|
349
363
|
}
|
|
350
364
|
catch (err) {
|
|
351
|
-
setMfaDisableError(err instanceof Error ? err.message : "Invalid backup code");
|
|
365
|
+
setMfaDisableError(err instanceof Error ? normalizeErrorMessage(err.message) : "Invalid backup code");
|
|
352
366
|
}
|
|
353
367
|
finally {
|
|
354
368
|
setMfaDisableLoading(false);
|
|
@@ -377,7 +391,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
377
391
|
showFeedback("2FA disabled successfully", "success");
|
|
378
392
|
}
|
|
379
393
|
catch (err) {
|
|
380
|
-
const msg = err instanceof Error ? err.message : "Failed to disable 2FA";
|
|
394
|
+
const msg = err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to disable 2FA";
|
|
381
395
|
setMfaDisableError(msg);
|
|
382
396
|
}
|
|
383
397
|
finally {
|
|
@@ -385,74 +399,27 @@ export function ManageWalletModal({ ...props }) {
|
|
|
385
399
|
}
|
|
386
400
|
}, [disableMfaWithSignedPayload, refreshMfaStatus, resetMfaDisableState, showFeedback]);
|
|
387
401
|
const startMfaSetup = useCallback(async () => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
setShowMfaSetupModal(true);
|
|
392
|
-
setMfaSetupLoading(true);
|
|
393
|
-
try {
|
|
394
|
-
const result = await enableMfa();
|
|
395
|
-
setMfaQrCode(result.qrCode);
|
|
396
|
-
setMfaSecret(result.secret);
|
|
402
|
+
const result = await start();
|
|
403
|
+
if (result) {
|
|
404
|
+
setShowMfaSetupModal(true);
|
|
397
405
|
}
|
|
398
|
-
|
|
399
|
-
const msg = err instanceof Error ? err.message : "Failed to enable MFA";
|
|
400
|
-
setMfaSetupError(msg);
|
|
401
|
-
setShowMfaSetupModal(false);
|
|
402
|
-
}
|
|
403
|
-
finally {
|
|
404
|
-
setMfaSetupLoading(false);
|
|
405
|
-
}
|
|
406
|
-
}, [enableMfa, resetMfaSetupState]);
|
|
406
|
+
}, [start]);
|
|
407
407
|
const handleVerifyMfaSetup = useCallback(async () => {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const trimmed = mfaSetupCode.trim();
|
|
411
|
-
if (trimmed.length !== 6) {
|
|
412
|
-
setMfaSetupError("Please enter a 6-digit code");
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
setMfaSetupLoading(true);
|
|
416
|
-
setMfaSetupError(null);
|
|
417
|
-
try {
|
|
418
|
-
const result = await verifySetupMfa(trimmed);
|
|
419
|
-
setMfaBackupCodes(result.backupCodes || []);
|
|
408
|
+
const result = await verifyCode(mfaSetupCode);
|
|
409
|
+
if (result) {
|
|
420
410
|
setShowMfaSetupModal(false);
|
|
421
411
|
setShowMfaBackupModal(true);
|
|
422
|
-
|
|
412
|
+
setMfaSetupCode("");
|
|
423
413
|
showFeedback("MFA enabled successfully.", "success");
|
|
414
|
+
await refreshMfaStatus();
|
|
424
415
|
}
|
|
425
|
-
|
|
426
|
-
const msg = err instanceof Error ? err.message : "Invalid TOTP code";
|
|
427
|
-
setMfaSetupError(msg);
|
|
428
|
-
}
|
|
429
|
-
finally {
|
|
430
|
-
setMfaSetupLoading(false);
|
|
431
|
-
}
|
|
432
|
-
}, [verifySetupMfa, mfaSetupCode, refreshMfaStatus, showFeedback]);
|
|
416
|
+
}, [verifyCode, mfaSetupCode, showFeedback, refreshMfaStatus]);
|
|
433
417
|
const handleVerifyRecoveryCode = useCallback(async () => {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const trimmed = mfaRecoveryCode.trim().toUpperCase();
|
|
437
|
-
if (!/^[A-Z0-9]{8}$/.test(trimmed)) {
|
|
438
|
-
setMfaSetupError("Please enter a valid 8-character recovery code");
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
setMfaSetupLoading(true);
|
|
442
|
-
setMfaSetupError(null);
|
|
443
|
-
try {
|
|
444
|
-
await verifyMfa(trimmed);
|
|
445
|
-
setMfaSetupStep("verify");
|
|
418
|
+
const ok = await verifyRecoveryCode(mfaRecoveryCode);
|
|
419
|
+
if (ok) {
|
|
446
420
|
setMfaRecoveryCode("");
|
|
447
421
|
}
|
|
448
|
-
|
|
449
|
-
const msg = err instanceof Error ? err.message : "Invalid recovery code";
|
|
450
|
-
setMfaSetupError(msg);
|
|
451
|
-
}
|
|
452
|
-
finally {
|
|
453
|
-
setMfaSetupLoading(false);
|
|
454
|
-
}
|
|
455
|
-
}, [verifyMfa, mfaRecoveryCode]);
|
|
422
|
+
}, [verifyRecoveryCode, mfaRecoveryCode]);
|
|
456
423
|
const handleCopyMfaSecret = useCallback(() => {
|
|
457
424
|
if (!mfaSecret)
|
|
458
425
|
return;
|
|
@@ -518,7 +485,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
518
485
|
await fetchLinkedProfilesData();
|
|
519
486
|
}
|
|
520
487
|
catch (err) {
|
|
521
|
-
showFeedback(err instanceof Error ? err.message : "Failed to disconnect", "error");
|
|
488
|
+
showFeedback(err instanceof Error ? normalizeErrorMessage(err.message) : "Failed to disconnect", "error");
|
|
522
489
|
}
|
|
523
490
|
finally {
|
|
524
491
|
setActionProvider(null);
|
|
@@ -539,8 +506,23 @@ export function ManageWalletModal({ ...props }) {
|
|
|
539
506
|
return () => clearInterval(id);
|
|
540
507
|
}, [emailLinkStep, emailLinkResendCooldown]);
|
|
541
508
|
useEffect(() => {
|
|
542
|
-
if (!isOpen || isExternalWalletConnected)
|
|
509
|
+
if (!isOpen || isExternalWalletConnected) {
|
|
510
|
+
hasFetchedLinkedProfilesForOpenRef.current = false;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (hasFetchedLinkedProfilesForOpenRef.current)
|
|
514
|
+
return;
|
|
515
|
+
hasFetchedLinkedProfilesForOpenRef.current = true;
|
|
516
|
+
fetchLinkedProfilesData();
|
|
517
|
+
}, [isOpen, isExternalWalletConnected, fetchLinkedProfilesData]);
|
|
518
|
+
useEffect(() => {
|
|
519
|
+
if (!isOpen || isExternalWalletConnected) {
|
|
520
|
+
hasFetchedMfaForOpenRef.current = false;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (hasFetchedMfaForOpenRef.current)
|
|
543
524
|
return;
|
|
525
|
+
hasFetchedMfaForOpenRef.current = true;
|
|
544
526
|
refreshMfaStatus();
|
|
545
527
|
}, [isOpen, isExternalWalletConnected, refreshMfaStatus]);
|
|
546
528
|
useEffect(() => {
|
|
@@ -556,10 +538,9 @@ export function ManageWalletModal({ ...props }) {
|
|
|
556
538
|
resetMfaSetupState();
|
|
557
539
|
}, children: _jsxs("div", { className: `wallet-modal-content wallet-modal-theme-${theme} wallet-modal-manage mfa-redesign-container`, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "wallet-modal-receive-header", children: [_jsx("button", { className: "wallet-modal-back", onClick: () => {
|
|
558
540
|
if (mfaSetupStep === "verify" || mfaSetupStep === "recovery") {
|
|
559
|
-
|
|
541
|
+
goToSetup();
|
|
560
542
|
setMfaSetupCode("");
|
|
561
543
|
setMfaRecoveryCode("");
|
|
562
|
-
setMfaSetupError(null);
|
|
563
544
|
return;
|
|
564
545
|
}
|
|
565
546
|
setShowMfaSetupModal(false);
|
|
@@ -569,10 +550,9 @@ export function ManageWalletModal({ ...props }) {
|
|
|
569
550
|
const label = userEmail ? `${issuer}:${userEmail}` : issuer;
|
|
570
551
|
return `otpauth://totp/${encodeURIComponent(label)}?secret=${mfaSecret}&issuer=${encodeURIComponent(issuer)}`;
|
|
571
552
|
})(), size: 160, level: "L", includeMargin: false }) })) : (_jsx("div", { className: "mfa-qr-placeholder", children: mfaSetupLoading ? "Generating QR..." : "QR Code" })), mfaSecret && (_jsxs("div", { className: "mfa-secret-display", children: [_jsx(IoKeyOutline, { size: 16, className: "mfa-secret-key-icon" }), _jsx("code", { className: "mfa-secret-code", children: mfaSecret }), _jsx("button", { type: "button", className: `mfa-secret-copy ${mfaSecretCopied ? "copied" : ""}`, onClick: handleCopyMfaSecret, children: mfaSecretCopied ? _jsx(IoCheckmarkOutline, { size: 18 }) : _jsx(IoCopyOutline, { size: 18 }) })] }))] }), _jsx("button", { type: "button", className: "mfa-verify-button", onClick: () => {
|
|
572
|
-
|
|
553
|
+
goToVerify();
|
|
573
554
|
setMfaSetupCode("");
|
|
574
555
|
setMfaRecoveryCode("");
|
|
575
|
-
setMfaSetupError(null);
|
|
576
556
|
}, disabled: mfaSetupLoading || !mfaSecret, children: "Continue" })] })) : mfaSetupStep === "verify" ? (_jsxs("div", { className: "mfa-verification-section", children: [_jsx("div", { className: "mfa-divider", children: _jsx("span", { children: "Enter 6-digit verification code" }) }), mfaSetupError && (_jsx("p", { className: "mfa-setup-error", style: { marginBottom: 12 }, children: mfaSetupError })), _jsx("div", { className: "mfa-otp-grid", children: [0, 1, 2, 3, 4, 5].map((index) => (_jsx("div", { className: "mfa-otp-slot", children: _jsx("input", { type: "text", inputMode: "numeric", maxLength: 1, value: mfaSetupCode[index] || "", onChange: (e) => {
|
|
577
557
|
const val = e.target.value.replace(/\D/g, "");
|
|
578
558
|
if (val) {
|
|
@@ -608,8 +588,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
608
588
|
}
|
|
609
589
|
}
|
|
610
590
|
}, className: "mfa-otp-input" }) }, index))) }), _jsx("p", { className: "mfa-refresh-text", children: "Code refreshes every 30 seconds" }), _jsx("button", { ref: mfaSetupVerifyButtonRef, type: "button", className: "mfa-verify-button", onClick: handleVerifyMfaSetup, disabled: mfaSetupLoading || mfaSetupCode.length !== 6, children: mfaSetupLoading ? "Verifying..." : "Verify & continue" }), _jsxs("p", { className: "mfa-trouble-text", children: ["Having trouble?", " ", _jsx("button", { type: "button", className: "mfa-link-button", onClick: () => {
|
|
611
|
-
|
|
612
|
-
setMfaSetupError(null);
|
|
591
|
+
goToRecovery();
|
|
613
592
|
setMfaRecoveryCode("");
|
|
614
593
|
}, children: "Use a recovery code" })] })] })) : (_jsxs("div", { className: "mfa-recovery-section", children: [_jsx("div", { className: "mfa-divider", children: _jsx("span", { children: "Enter 8 charater backup code" }) }), mfaSetupError && (_jsx("p", { className: "mfa-setup-error", style: { marginBottom: 12 }, children: mfaSetupError })), _jsx("input", { type: "text", inputMode: "text", maxLength: 8, value: mfaRecoveryCode, onChange: (e) => {
|
|
615
594
|
const val = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 8);
|
|
@@ -619,13 +598,13 @@ export function ManageWalletModal({ ...props }) {
|
|
|
619
598
|
if (showMfaBackupModal) {
|
|
620
599
|
return (_jsx("div", { className: `wallet-modal-overlay wallet-modal-theme-${theme}`, onClick: () => {
|
|
621
600
|
setShowMfaBackupModal(false);
|
|
622
|
-
|
|
601
|
+
reset();
|
|
623
602
|
}, children: _jsxs("div", { className: `wallet-modal-content wallet-modal-theme-${theme} wallet-modal-manage mfa-redesign-container`, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "wallet-modal-receive-header", children: [_jsx("button", { className: "wallet-modal-back", onClick: () => {
|
|
624
603
|
setShowMfaBackupModal(false);
|
|
625
|
-
|
|
604
|
+
reset();
|
|
626
605
|
}, "aria-label": "Back", children: _jsx(IoChevronBack, { size: 20 }) }), _jsx("h2", { className: "wallet-modal-receive-title", children: "Backup Codes" }), _jsx("button", { className: "wallet-modal-close", onClick: onClose, "aria-label": "Close", children: _jsx(IoClose, { size: 24 }) })] }), _jsxs("div", { className: "mfa-redesign-content", children: [_jsxs("div", { className: "mfa-info-box", children: [_jsx("div", { className: "mfa-info-icon", children: _jsx(IoKeyOutline, { size: 20 }) }), _jsx("p", { className: "mfa-info-text", children: "Backup codes can be used to access your account if you lose access to your primary authentication method. Store them securely as these codes will not be shown again." })] }), _jsx("div", { className: "wallet-modal-mfa-backup-heading", children: _jsx("span", { className: "wallet-modal-mfa-backup-heading-label", children: "Recovery Codes" }) }), _jsx("div", { className: "wallet-modal-mfa-backup-list", children: mfaBackupCodes.map((code, index) => (_jsx("div", { className: "wallet-modal-mfa-backup-code-wrapper", children: _jsx("code", { className: "wallet-modal-mfa-backup-code", children: code }) }, index))) }), _jsxs("div", { className: "wallet-modal-mfa-backup-actions", children: [_jsxs("button", { type: "button", className: `wallet-modal-mfa-backup-action-btn ${mfaBackupCodesCopied ? 'copied' : ''}`, onClick: handleCopyBackupCodes, disabled: !mfaBackupCodes.length, children: [mfaBackupCodesCopied ? _jsx(IoCheckmarkOutline, { size: 18 }) : _jsx(IoCopyOutline, { size: 18 }), mfaBackupCodesCopied ? "Copied" : "Copy All"] }), _jsxs("button", { type: "button", className: "wallet-modal-mfa-backup-action-btn", onClick: handleDownloadBackupCodes, disabled: !mfaBackupCodes.length, children: [_jsx(IoCloudDownloadOutline, { size: 18 }), "Download"] })] }), _jsx("button", { type: "button", className: "mfa-verify-button", onClick: () => {
|
|
627
606
|
setShowMfaBackupModal(false);
|
|
628
|
-
|
|
607
|
+
reset();
|
|
629
608
|
}, children: "I've saved these codes" })] })] }) }));
|
|
630
609
|
}
|
|
631
610
|
if (showMfaDisableAuthModal) {
|
|
@@ -720,7 +699,7 @@ export function ManageWalletModal({ ...props }) {
|
|
|
720
699
|
return (_jsx("div", { className: `wallet-modal-overlay wallet-modal-theme-${theme}`, onClick: onClose, children: _jsxs("div", { className: `wallet-modal-content wallet-modal-theme-${theme} wallet-modal-manage`, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "wallet-modal-receive-header", children: [_jsx("button", { className: "wallet-modal-back", onClick: onClose, "aria-label": "Back", children: _jsx(IoChevronBack, { size: 20 }) }), _jsx("h2", { className: "wallet-modal-receive-title", children: "Manage Wallet" }), _jsx("button", { className: "wallet-modal-close", onClick: onClose, "aria-label": "Close", children: _jsx(IoClose, { size: 24 }) })] }), _jsx("div", { className: "wallet-modal-manage-content", children: _jsxs("div", { className: "wallet-modal-manage-list", children: [!isExternalWalletConnected && (_jsxs("button", { className: "wallet-modal-manage-list-item", onClick: handleOpenLinkedProfiles, type: "button", children: [_jsx("div", { className: "wallet-modal-manage-list-icon", "aria-hidden": "true", children: _jsx(FiUsers, { size: 20 }) }), _jsx("span", { className: "wallet-modal-manage-list-label", children: "Auth Methods" }), _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "wallet-modal-manage-list-arrow", "aria-hidden": "true", children: _jsx("path", { d: "M6 12L10 8L6 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })] })), !isExternalWalletConnected && (_jsxs("button", { className: "wallet-modal-manage-list-item", onClick: () => {
|
|
721
700
|
if (mfaLoading)
|
|
722
701
|
return;
|
|
723
|
-
if (!
|
|
702
|
+
if (!canEnableMfaFlow) {
|
|
724
703
|
return;
|
|
725
704
|
}
|
|
726
705
|
if (mfaStatus?.enabled) {
|