Skip to content
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
74 changes: 23 additions & 51 deletions apps/playground-web/src/app/connect/in-app-wallet/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function AnyAuth() {
/>
</div>
<div className="space-y-2">
<h3 className="mb-3 font-medium text-lg">Custom Auth and UI</h3>
<h3 className="mb-3 font-medium text-lg">Custom UI</h3>
<p className="max-w-[600px]">
Customize the login UI and integrate with your existing user base. No
limits on customizations and auth methods.
Expand All @@ -85,63 +85,35 @@ function AnyAuth() {
<CustomLoginForm />
</div>
}
code={`import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
code={`import { useState } from "react";
import { useConnect } from "thirdweb/react";
import { inAppWallet } from "thirdweb/wallets";
import { inAppWallet, preAuthenticate } from "thirdweb/wallets/in-app";

export function CustomLoginForm() {
const [email, setEmail] = useState("");
export function CustomLoginUi() {
const { connect, isConnecting, error } = useConnect();

const { mutate: loginWithCustomAuth } = useMutation({
mutationFn: async (email: string) => {
const wallet = await connect(async () => {
const wallet = inAppWallet();
await wallet.connect({
strategy: "auth_endpoint",
client,
// your own custom auth payload here
payload: JSON.stringify({
userId: email,
email,
}),
});
return wallet;
const preLogin = async (email: string) => {
// send email verification code
await preAuthenticate({
client,
strategy: "email",
email,
});
};

const handleLogin = async (email: string, verificationCode: string) => {
// verify email with verificationCode and connect
connect(async () => {
const wallet = inAppWallet();
await wallet.connect({
client,
strategy: "email",
email,
verificationCode,
});
return wallet;
}
});

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
loginWithCustomAuth(email);
});
};

return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">
Email Address
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
required
/>
<button
type="submit"
disabled={isConnecting || !email}
>
{isConnecting ? "Submitting..." : "Submit"}
</button>
{error && <p>{error.message}</p>}
</div>
</form>
);
}
Comment on lines +92 to 117
Copy link
Contributor

Choose a reason for hiding this comment

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

The CustomLoginUi component is missing its UI implementation. While the authentication logic is correct, the component needs to return JSX that renders the email input, verification code input, and submit buttons that call preLogin() and handleLogin(). Consider adding a form similar to the one that was removed, but updated to handle the two-step email verification flow.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

`}
lang="tsx"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,46 @@
"use client";

import { THIRDWEB_CLIENT } from "@/lib/client";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
import { useActiveAccount, useConnect } from "thirdweb/react";
import { inAppWallet } from "thirdweb/wallets";
import { inAppWallet, preAuthenticate } from "thirdweb/wallets";
import { InAppConnectEmbed } from "./connect-button";

export function CustomLoginForm() {
const [email, setEmail] = useState("");
const [verificationCode, setVerificationCode] = useState("");
const [screen, setScreen] = useState<"login" | "verify">("login");
const { connect, isConnecting, error } = useConnect();
const account = useActiveAccount();

const { mutate: loginWithCustomAuthEndpoint } = useMutation({
mutationFn: async (email: string) => {
const wallet = await connect(async () => {
const wallet = inAppWallet();
await wallet.connect({
strategy: "auth_endpoint",
client: THIRDWEB_CLIENT,
payload: JSON.stringify({
userId: email,
email,
}),
});
return wallet;
const sendEmailVerificationCode = async (email: string) => {
setScreen("verify");
await preAuthenticate({
client: THIRDWEB_CLIENT,
strategy: "email",
email,
});
};

const loginWithEmail = async (email: string, verificationCode: string) => {
connect(async () => {
const wallet = inAppWallet();
await wallet.connect({
strategy: "email",
client: THIRDWEB_CLIENT,
email,
verificationCode,
});
return wallet;
},
});

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
loginWithCustomAuthEndpoint(email);
});
};

if (account) {
return <InAppConnectEmbed />;
}

return (
<form onSubmit={handleSubmit} className="mt-4">
if (screen === "login") {
return (
<div className="flex flex-col space-y-2">
<label htmlFor="email" className="font-medium text-sm">
Email Address
Expand All @@ -55,13 +56,42 @@ export function CustomLoginForm() {
/>
<button
type="submit"
onClick={() => sendEmailVerificationCode(email)}
className="rounded-lg bg-blue-500 px-4 py-2 text-white transition-colors enabled:hover:bg-blue-600"
disabled={isConnecting || !email}
>
{isConnecting ? "Submitting..." : "Submit"}
</button>
{error && <p className="max-w-[300px] text-red-500">{error.message}</p>}
</div>
</form>
);
);
}

if (screen === "verify") {
return (
<div className="flex flex-col space-y-2">
<label htmlFor="verification-code" className="font-medium text-sm">
Verification Code
</label>
<input
type="text"
id="verification-code"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
className="rounded-lg border px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Enter the code you received"
required
/>
<button
type="submit"
onClick={() => loginWithEmail(email, verificationCode)}
className="rounded-lg bg-blue-500 px-4 py-2 text-white transition-colors enabled:hover:bg-blue-600"
disabled={isConnecting || !verificationCode}
>
{isConnecting ? "Submitting..." : "Submit"}
</button>
{error && <p className="max-w-[300px] text-red-500">{error.message}</p>}
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ const preLogin = async (email: string) => {
});
};

const handleLogin = async (email: string, verificationCode: string) => {
const handleLogin = (email: string, verificationCode: string) => {
// verify email and connect
await connect(() => {
connect(async () => {
const wallet = inAppWallet();
await wallet.connect({
client,
Expand Down
109 changes: 46 additions & 63 deletions apps/portal/src/app/connect/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
OpenSourceCard,
Stack,
ConnectCard,
SDKCard,
Callout
} from "@doc";
import { TypeScriptIcon, ReactIcon, DotNetIcon, UnityIcon, UnrealEngineIcon } from "@/icons";
import { cn } from "@/lib/utils";
import Link from "next/link";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -36,24 +38,51 @@ Connect is the complete toolkit for connecting every user to your application. I
title="Playground"
href="https://playground.thirdweb.com"
iconUrl="/icons/connect/intros/demo.svg"
isExternal={true}
/>
<ConnectCard
title="Quickstart"
href="https://portal.thirdweb.com/react/v5/getting-started"
title="Templates"
href="https://thirdweb.com/templates"
iconUrl="/icons/connect/intros/quickstart.svg"
/>
{/* <ConnectCard
title="Why Connect?"
href=""
iconUrl="/icons/connect/intros/why-connect.svg"
/>
<ConnectCard
title="Video Tutorial"
href=""
iconUrl="/icons/connect/intros/video-tutorial.svg"
/> */}
</div>
### With Connect, you can:

### Quick starts

<Grid>
<SDKCard
title="TypeScript"
href="/typescript/v5/getting-started"
icon={TypeScriptIcon}
/>
<SDKCard
title="React"
href="/react/v5/getting-started"
icon={ReactIcon}
/>
<SDKCard
title="React Native"
href="/react-native/v5/getting-started"
icon={ReactIcon}
/>
<SDKCard
title="DotNet"
href="/dotnet/getting-started"
icon={DotNetIcon}
/>
<SDKCard
title="Unity"
href="/unity/v5/getting-started"
icon={UnityIcon}
/>
<SDKCard
title="Unreal Engine"
href="/unreal-engine/getting-started"
icon={UnrealEngineIcon}
/>
</Grid>

### With Connect, you can

- **Connect to 350+ different wallet providers** with support for every EVM network
- **Log in and authenticate your users** with customizable and secure [email, phone, passkeys and social login](https://portal.thirdweb.com/connect/in-app-wallet/overview) flows.
Expand Down Expand Up @@ -82,59 +111,13 @@ Connect is the complete toolkit for connecting every user to your application. I

Connect is supported on every EVM compatible chain. To view the full list, visit [thirdweb chainlist](https://thirdweb.com/chainlist).

### Templates
### Starter Kits & demos

<ExpandableGrid>
View all available starter kits, demos and templates on Github.

<GithubTemplateCard
title="Next Starter"
href="https://github.com/thirdweb-example/next-starter"
tag="Starter"
/>
<GithubTemplateCard
title="Vite Starter"
href="https://github.com/thirdweb-example/vite-starter"
title="View all templates"
href="https://github.com/thirdweb-example"
tag="Starter"
/>
<GithubTemplateCard
title="Node Starter"
href="https://github.com/thirdweb-example/node-starter"
tag="Starter"
/>
<GithubTemplateCard
title="Expo Starter"
href="https://github.com/thirdweb-example/expo-starter"
tag="Starter"
/>

<GithubTemplateCard
title="NFT Marketplace"
href="https://github.com/thirdweb-example/marketplace-v3"
tag="Builds"
/>

<GithubTemplateCard
title="Auth - NextJS"
href="https://github.com/thirdweb-example/thirdweb-auth-next"
tag="Example"
/>

<GithubTemplateCard
title="Auth - Express"
href="https://github.com/thirdweb-example/thirdweb-auth-express"
tag="Example"
/>

<GithubTemplateCard
title="Account Abstraction"
href="https://github.com/thirdweb-example/account-abstraction"
tag="Example"
/>

<GithubTemplateCard
title="Sign in with Farcaster"
href="https://github.com/thirdweb-example/thirdweb-siwf"
tag="Example"
/>

</ExpandableGrid>
Loading
Loading