diff --git a/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx b/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx index 18d62fa577..2bcae57487 100644 --- a/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx +++ b/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx @@ -1,7 +1,11 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useAuthenticate } from "../../../../hooks/useAuthenticate.js"; import { ls } from "../../../../strings.js"; -import { useAuthContext, type AuthStep } from "../../context.js"; +import { + AuthStepStatus, + useAuthContext, + type AuthStep, +} from "../../context.js"; import { Button } from "../../../button.js"; type EmailNotReceivedDisclaimerProps = { @@ -18,6 +22,14 @@ export const EmailNotReceivedDisclaimer = ({ }, }); + const isOTPVerifying = useMemo(() => { + return ( + authStep.type === "otp_verify" && + (authStep.status === AuthStepStatus.verifying || + authStep.status === AuthStepStatus.success) + ); + }, [authStep]); + useEffect(() => { if (emailResent) { // set the text back to "Resend" after 2 seconds @@ -29,13 +41,20 @@ export const EmailNotReceivedDisclaimer = ({ return (
- + {ls.loadingEmail.emailNotReceived}

- {ls.loadingOtp.title} + {titleText}

{ls.loadingOtp.body} @@ -66,13 +76,31 @@ export const LoadingOtp = () => { {authStep.email}

); }; + +function getUserErrorMessage(error: Error | undefined): string { + if (!error) { + return ""; + } + // Errors from Alchemy have a JSON message. + try { + const message = JSON.parse(error.message).error; + if (message === "invalid OTP code") { + return ls.error.otp.invalid; + } + return message; + } catch (e) { + // Ignore + } + return error.message; +} diff --git a/account-kit/react/src/components/auth/context.ts b/account-kit/react/src/components/auth/context.ts index 61e27847f1..e3b14107ee 100644 --- a/account-kit/react/src/components/auth/context.ts +++ b/account-kit/react/src/components/auth/context.ts @@ -4,9 +4,20 @@ import type { Connector } from "@wagmi/core"; import { createContext, useContext } from "react"; import type { AuthType } from "./types"; +export enum AuthStepStatus { + success = "success", + error = "error", + verifying = "verifying", +} + export type AuthStep = | { type: "email_verify"; email: string } - | { type: "otp_verify"; email: string; error?: Error } + | { + type: "otp_verify"; + email: string; + error?: Error; + status?: AuthStepStatus | null; + } | { type: "passkey_verify"; error?: Error } | { type: "passkey_create"; error?: Error } | { type: "passkey_create_success" } diff --git a/account-kit/react/src/components/otp-input/otp-input.tsx b/account-kit/react/src/components/otp-input/otp-input.tsx index c8e49bb6ae..d1e14b4d5d 100644 --- a/account-kit/react/src/components/otp-input/otp-input.tsx +++ b/account-kit/react/src/components/otp-input/otp-input.tsx @@ -12,6 +12,7 @@ type OTPInputProps = { disabled?: boolean; handleReset: () => void; className?: string; + isVerified?: boolean; }; export const isOTPCodeType = (arg: string[]): arg is OTPCodeType => { @@ -30,6 +31,7 @@ export const OTPInput: React.FC = ({ setErrorText, handleReset, className, + isVerified, }) => { const [autoComplete, setAutoComplete] = useState(""); const [activeElement, setActiveElement] = useState(0); @@ -140,9 +142,17 @@ export const OTPInput: React.FC = ({
{initialOTPValue.map((_, i) => ( (refs.current[i] = el)} tabIndex={i + 1} type="text" diff --git a/account-kit/react/src/strings.ts b/account-kit/react/src/strings.ts index a5680833bf..b37469d59d 100644 --- a/account-kit/react/src/strings.ts +++ b/account-kit/react/src/strings.ts @@ -35,6 +35,8 @@ const STRINGS = { body: "We sent a verification code to", notReceived: "Didn't receive code?", resend: "Resend", + verifying: "Verifying...", + verified: "Verified!", }, completingEmail: { body: "Completing login. Please wait a few seconds for this to screen to update.",