Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add UI config defaults, better back navigation to auth UI #827

Merged
merged 4 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions account-kit/react/src/components/auth/card/add-passkey.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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: () => {
Expand All @@ -36,11 +34,7 @@ export const AddPasskey = () => {
return (
<div className="flex flex-col gap-5 items-center">
<div className="flex flex-col items-center justify-center h-12 w-12">
<AddPasskeyIllustration
illustrationStyle={illustrationStyle ?? "flat"}
height="48"
width="48"
/>
<AddPasskeyIllustration height="48" width="48" />
</div>

<h3 className="font-semibold text-lg">{ls.addPasskey.title}</h3>
Expand All @@ -49,7 +43,7 @@ export const AddPasskey = () => {
{BENEFITS.map(({ title, icon: Icon, description }) => (
<div key={title} className="flex gap-2">
<div className="h-5 w-5 flex items-center justify-center">
<Icon illustrationStyle={illustrationStyle ?? "flat"} />
<Icon />
</div>
<div className="flex flex-col">
<p className="font-semibold text-sm">{title}</p>
Expand Down
24 changes: 17 additions & 7 deletions account-kit/react/src/components/auth/card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,35 +42,44 @@ export const AuthCardContent = ({
const { status, isAuthenticating } = useSignerStatus();
const { authStep, setAuthStep } = useAuthContext();

const signer = useSigner();
const error = useAuthError();

const contentRef = useRef<HTMLDivElement>(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(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also clear the QP (or at least the bundle QP)

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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AuthStep, { type: "email_verify" }>;
Expand All @@ -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: () => {
Expand All @@ -38,12 +36,7 @@ export const LoadingEmail = ({ context }: LoadingEmailProps) => {
return (
<div className="flex flex-col gap-5 items-center">
<div className="flex flex-col items-center justify-center h-12 w-12">
<EmailIllustration
illustrationStyle={illustrationStyle ?? "flat"}
height="48"
width="48"
className="animate-pulse"
/>
<EmailIllustration height="48" width="48" className="animate-pulse" />
</div>

<h3 className="font-semibold text-lg">{ls.loadingEmail.title}</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className="flex flex-col gap-5 items-center">
<div className="flex flex-col items-center justify-center">
<LoadingPasskey illustrationStyle={illustrationStyle ?? "flat"} />
<LoadingPasskey />
</div>

<h3 className="font-semibold text-lg">{ls.loadingPasskey.title}</h3>
Expand Down
8 changes: 3 additions & 5 deletions account-kit/react/src/components/auth/card/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex flex-col gap-5 items-center">
<div className="flex flex-col items-center justify-center h-12 w-12">
<AddedPasskeyIllustration
illustrationStyle={illustrationStyle ?? "flat"}
height="48"
width="48"
/>
<AddedPasskeyIllustration height="48" width="48" />
</div>
<h3 className="font-semibold text-lg">Passkey added!</h3>
<p className="text-fg-secondary text-sm text-center">
Expand Down
20 changes: 20 additions & 0 deletions account-kit/react/src/createConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion account-kit/react/src/hooks/useUiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@

import { useAlchemyAccountContext } from "../context.js";
import { MissingUiConfigError } from "../errors.js";
import type { AlchemyAccountsUIConfig } from "../types.js";

export const useUiConfig = () => {
const { ui } = useAlchemyAccountContext();
if (ui == null) {
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;
};
15 changes: 8 additions & 7 deletions account-kit/react/src/icons/illustrations/add-passkey.tsx
Original file line number Diff line number Diff line change
@@ -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<SVGSVGElement>) => {
const { illustrationStyle } = useUiConfig();

return (
<>
{style === "outline" && (
{illustrationStyle === "outline" && (
<>
<AddPasskeyOutlineLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -23,7 +24,7 @@ export const AddPasskeyIllustration = ({
/>
</>
)}
{style === "linear" && (
{illustrationStyle === "linear" && (
<>
<AddPasskeyLinearLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -37,7 +38,7 @@ export const AddPasskeyIllustration = ({
/>
</>
)}
{style === "filled" && (
{illustrationStyle === "filled" && (
<>
<AddPasskeyFilledLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -51,7 +52,7 @@ export const AddPasskeyIllustration = ({
/>
</>
)}
{style === "flat" && (
{illustrationStyle === "flat" && (
<>
<AddPasskeyFlatLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand Down
15 changes: 8 additions & 7 deletions account-kit/react/src/icons/illustrations/added-passkey.tsx
Original file line number Diff line number Diff line change
@@ -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 AddedPasskeyIllustration = ({
className,
illustrationStyle: style,
...props
}: IllustrationProps) => {
}: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => {
const { illustrationStyle } = useUiConfig();

return (
<>
{style === "outline" && (
{illustrationStyle === "outline" && (
<>
<AddedPasskeyOutlineLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -23,7 +24,7 @@ export const AddedPasskeyIllustration = ({
/>
</>
)}
{style === "linear" && (
{illustrationStyle === "linear" && (
<>
<AddedPasskeyLinearLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -37,7 +38,7 @@ export const AddedPasskeyIllustration = ({
/>
</>
)}
{style === "filled" && (
{illustrationStyle === "filled" && (
<>
<AddedPasskeyFilledLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -51,7 +52,7 @@ export const AddedPasskeyIllustration = ({
/>
</>
)}
{style === "flat" && (
{illustrationStyle === "flat" && (
<>
<AddedPasskeyFlatLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand Down
15 changes: 8 additions & 7 deletions account-kit/react/src/icons/illustrations/email.tsx
Original file line number Diff line number Diff line change
@@ -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 EmailIllustration = ({
className,
illustrationStyle: style,
...props
}: IllustrationProps) => {
}: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => {
const { illustrationStyle } = useUiConfig();

return (
<>
{style === "outline" && (
{illustrationStyle === "outline" && (
<>
<EmailOutlineLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -23,7 +24,7 @@ export const EmailIllustration = ({
/>
</>
)}
{style === "linear" && (
{illustrationStyle === "linear" && (
<>
<EmailLinearLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -37,7 +38,7 @@ export const EmailIllustration = ({
/>
</>
)}
{style === "filled" && (
{illustrationStyle === "filled" && (
<>
<EmailFilledLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand All @@ -51,7 +52,7 @@ export const EmailIllustration = ({
/>
</>
)}
{style === "flat" && (
{illustrationStyle === "flat" && (
<>
<EmailFlatLight
className={`dark:hidden text-fg-accent-brand ${className ?? ""}`}
Expand Down
Loading
Loading