diff --git a/account-kit/react/src/components/auth/card/add-passkey.tsx b/account-kit/react/src/components/auth/card/add-passkey.tsx index 7f6c171c1c..f458a860aa 100644 --- a/account-kit/react/src/components/auth/card/add-passkey.tsx +++ b/account-kit/react/src/components/auth/card/add-passkey.tsx @@ -1,5 +1,4 @@ import { useAddPasskey } from "../../../hooks/useAddPasskey.js"; -import { useUiConfig } from "../../../hooks/useUiConfig.js"; import { AddPasskeyIllustration } from "../../../icons/illustrations/add-passkey.js"; import { PasskeyShieldIllustration, @@ -25,7 +24,6 @@ const BENEFITS = [ // eslint-disable-next-line jsdoc/require-jsdoc export const AddPasskey = () => { - const { illustrationStyle } = useUiConfig(); const { setAuthStep } = useAuthContext(); const { addPasskey, isAddingPasskey } = useAddPasskey({ onSuccess: () => { @@ -36,11 +34,7 @@ export const AddPasskey = () => { return (
- +

{ls.addPasskey.title}

@@ -49,7 +43,7 @@ export const AddPasskey = () => { {BENEFITS.map(({ title, icon: Icon, description }) => (
- +

{title}

diff --git a/account-kit/react/src/components/auth/card/index.tsx b/account-kit/react/src/components/auth/card/index.tsx index c9a1896c70..79da19e65a 100644 --- a/account-kit/react/src/components/auth/card/index.tsx +++ b/account-kit/react/src/components/auth/card/index.tsx @@ -11,6 +11,7 @@ import { Navigation } from "../../navigation.js"; import { Notification } from "../../notification.js"; import { useAuthContext } from "../context.js"; import { Step } from "./steps.js"; +import { useSigner } from "../../../hooks/useSigner.js"; export type AuthCardProps = { className?: string; @@ -41,35 +42,44 @@ export const AuthCardContent = ({ const { status, isAuthenticating } = useSignerStatus(); const { authStep, setAuthStep } = useAuthContext(); + const signer = useSigner(); const error = useAuthError(); const contentRef = useRef(null); const { height } = useElementHeight(contentRef); - const { auth } = useUiConfig(); - const hideError = auth?.hideError; - const onAuthSuccess = auth?.onAuthSuccess; + const didGoBack = useRef(false); + + const { + auth: { hideError, onAuthSuccess }, + } = useUiConfig(); - // TODO: Finalize the steps that allow going back const canGoBack = useMemo(() => { - return ["email_verify"].includes(authStep.type); + return ["email_verify", "passkey_verify"].includes(authStep.type); }, [authStep]); const onBack = useCallback(() => { switch (authStep.type) { case "email_verify": + case "passkey_verify": + signer?.disconnect(); // Terminate any inflight authentication + didGoBack.current = true; setAuthStep({ type: "initial" }); break; default: console.warn("Unhandled back action for auth step", authStep); } - }, [authStep, setAuthStep]); + }, [authStep, setAuthStep, signer]); useLayoutEffect(() => { if (authStep.type === "complete") { + didGoBack.current = false; closeAuthModal(); onAuthSuccess?.(); - } else if (isAuthenticating && authStep.type === "initial") { + } else if (authStep.type !== "initial") { + didGoBack.current = false; + } else if (!didGoBack.current && isAuthenticating) { + // Auth step must be initial const urlParams = new URLSearchParams(window.location.search); setAuthStep({ diff --git a/account-kit/react/src/components/auth/card/loading/email.tsx b/account-kit/react/src/components/auth/card/loading/email.tsx index 570de8714c..5f88a4f66f 100644 --- a/account-kit/react/src/components/auth/card/loading/email.tsx +++ b/account-kit/react/src/components/auth/card/loading/email.tsx @@ -7,7 +7,6 @@ import { useAuthContext, type AuthStep } from "../../context.js"; import { Spinner } from "../../../../icons/spinner.js"; import { ls } from "../../../../strings.js"; import { EmailIllustration } from "../../../../icons/illustrations/email.js"; -import { useUiConfig } from "../../../../hooks/useUiConfig.js"; interface LoadingEmailProps { context: Extract; @@ -18,7 +17,6 @@ export const LoadingEmail = ({ context }: LoadingEmailProps) => { // yup, re-sent and resent. I'm not fixing it const [emailResent, setEmailResent] = useState(false); - const { illustrationStyle } = useUiConfig(); const { setAuthStep } = useAuthContext(); const { authenticate } = useAuthenticate({ onSuccess: () => { @@ -38,12 +36,7 @@ export const LoadingEmail = ({ context }: LoadingEmailProps) => { return (
- +

{ls.loadingEmail.title}

diff --git a/account-kit/react/src/components/auth/card/loading/passkey.tsx b/account-kit/react/src/components/auth/card/loading/passkey.tsx index aaad170736..2239a70224 100644 --- a/account-kit/react/src/components/auth/card/loading/passkey.tsx +++ b/account-kit/react/src/components/auth/card/loading/passkey.tsx @@ -2,16 +2,13 @@ import { ls } from "../../../../strings.js"; import { LoadingPasskey } from "../../../../icons/passkey.js"; import { Button } from "../../../button.js"; import { PoweredBy } from "../../../poweredby.js"; -import { useUiConfig } from "../../../../hooks/useUiConfig.js"; // eslint-disable-next-line jsdoc/require-jsdoc export const LoadingPasskeyAuth = () => { - const { illustrationStyle } = useUiConfig(); - return (
- +

{ls.loadingPasskey.title}

diff --git a/account-kit/react/src/components/auth/card/main.tsx b/account-kit/react/src/components/auth/card/main.tsx index d5fffa5603..2ab9968ae5 100644 --- a/account-kit/react/src/components/auth/card/main.tsx +++ b/account-kit/react/src/components/auth/card/main.tsx @@ -7,11 +7,9 @@ import { useUiConfig } from "../../../hooks/useUiConfig.js"; // eslint-disable-next-line jsdoc/require-jsdoc export const MainAuthContent = () => { - const { auth } = useUiConfig(); - - const header = auth?.header ?? null; - const sections = auth?.sections; - const showSignInText = auth?.showSignInText ?? false; + const { + auth: { header, sections, showSignInText }, + } = useUiConfig(); return ( <> diff --git a/account-kit/react/src/components/auth/card/passkey-added.tsx b/account-kit/react/src/components/auth/card/passkey-added.tsx index f31eef003a..70c629b33a 100644 --- a/account-kit/react/src/components/auth/card/passkey-added.tsx +++ b/account-kit/react/src/components/auth/card/passkey-added.tsx @@ -1,19 +1,12 @@ -import { useUiConfig } from "../../../hooks/useUiConfig.js"; import { AddedPasskeyIllustration } from "../../../icons/illustrations/added-passkey.js"; import { PoweredBy } from "../../poweredby.js"; // eslint-disable-next-line jsdoc/require-jsdoc export function PasskeyAdded() { - const { illustrationStyle } = useUiConfig(); - return (
- +

Passkey added!

diff --git a/account-kit/react/src/createConfig.ts b/account-kit/react/src/createConfig.ts index 0c8e4003d2..da9f22380d 100644 --- a/account-kit/react/src/createConfig.ts +++ b/account-kit/react/src/createConfig.ts @@ -14,6 +14,26 @@ export type AlchemyAccountsConfigWithUI = AlchemyAccountsConfig & { * an additional argument, the configuration object for the Auth Components UI * (the modal and AuthCard). * + * @example + * ```ts + * import { sepolia } from "@account-kit/infra" + * import { createConfig } from "@account-kit/react" + * + * const uiConfig = { + * illustrationStyle: "linear", + * auth: { + * sections: [[{ type: "email" }], [{ type: "passkey" }]], + * addPasskeyOnSignup: true, + * }, + * } + * + * const config = createConfig({ + * rpcUrl: "/api/rpc", + * chain: sepolia, + * ssr: true, + * }, uiConfig) + * ``` + * * @param {CreateConfigProps} props for creating an alchemy account config * @param {AlchemyAccountsUIConfig} ui the configuration to use for the Auth Components UI * @returns an alchemy account config object containing the core and client store, as well as the UI config diff --git a/account-kit/react/src/hooks/useUiConfig.ts b/account-kit/react/src/hooks/useUiConfig.ts index a34a320dc9..30e68e3dde 100644 --- a/account-kit/react/src/hooks/useUiConfig.ts +++ b/account-kit/react/src/hooks/useUiConfig.ts @@ -2,6 +2,7 @@ import { useAlchemyAccountContext } from "../context.js"; import { MissingUiConfigError } from "../errors.js"; +import type { AlchemyAccountsUIConfig } from "../types.js"; export const useUiConfig = () => { const { ui } = useAlchemyAccountContext(); @@ -9,7 +10,20 @@ export const useUiConfig = () => { throw new MissingUiConfigError("useUiConfig"); } + // We set some defaults here return { ...ui.config, - }; + illustrationStyle: ui.config.illustrationStyle ?? "flat", + auth: { + ...ui.config.auth, + addPasskeyOnSignup: ui.config.auth?.addPasskeyOnSignup ?? false, + header: ui.config.auth?.header ?? null, + hideError: ui.config.auth?.hideError ?? false, + sections: ui.config.auth?.sections ?? [ + [{ type: "email" }], + [{ type: "passkey" }], + ], + showSignInText: ui.config.auth?.showSignInText ?? true, + }, + } satisfies AlchemyAccountsUIConfig; }; diff --git a/account-kit/react/src/icons/illustrations/add-passkey.tsx b/account-kit/react/src/icons/illustrations/add-passkey.tsx index 92e4287d8d..1c3a3e0ca5 100644 --- a/account-kit/react/src/icons/illustrations/add-passkey.tsx +++ b/account-kit/react/src/icons/illustrations/add-passkey.tsx @@ -1,15 +1,16 @@ import { type SVGProps } from "react"; -import type { IllustrationProps } from "./types.js"; +import { useUiConfig } from "../../hooks/useUiConfig.js"; // eslint-disable-next-line jsdoc/require-jsdoc export const AddPasskeyIllustration = ({ className, - illustrationStyle: style, ...props -}: IllustrationProps) => { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> { +}: JSX.IntrinsicAttributes & SVGProps) => { + const { illustrationStyle } = useUiConfig(); + return ( <> - {style === "outline" && ( + {illustrationStyle === "outline" && ( <> )} - {style === "linear" && ( + {illustrationStyle === "linear" && ( <> )} - {style === "filled" && ( + {illustrationStyle === "filled" && ( <> )} - {style === "flat" && ( + {illustrationStyle === "flat" && ( <> & { - illustrationStyle: AuthIllustrationStyle; - }; diff --git a/account-kit/react/src/icons/passkey.tsx b/account-kit/react/src/icons/passkey.tsx index 9321c3e267..d072813835 100644 --- a/account-kit/react/src/icons/passkey.tsx +++ b/account-kit/react/src/icons/passkey.tsx @@ -1,14 +1,13 @@ import type { SVGProps } from "react"; import { Spinner } from "./spinner.js"; import { PasskeyIllustration } from "./illustrations/passkey.js"; -import type { IllustrationProps } from "./illustrations/types.js"; // eslint-disable-next-line jsdoc/require-jsdoc -export function LoadingPasskey(props: IllustrationProps) { +export function LoadingPasskey() { return (

- +
); }