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.",