Skip to content

Commit 51bdbaf

Browse files
feat: add useLinkProfile hook and update profile linking functionality
1 parent e91581a commit 51bdbaf

File tree

23 files changed

+538
-42
lines changed

23 files changed

+538
-42
lines changed

.changeset/hot-adults-doubt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
New `useLinkProfile()` hook to link profiles to inapp and ecosystem accounts

apps/playground-web/src/app/connect/in-app-wallet/ecosystem/page.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { CodeExample } from "@/components/code/code-example";
22
import { EcosystemConnectEmbed } from "../../../../components/in-app-wallet/ecosystem";
33
import ThirdwebProvider from "../../../../components/thirdweb-provider";
4+
import { Profiles } from "../page";
45

56
export default function Page() {
67
return (
78
<ThirdwebProvider>
8-
<section className="space-y-8">
9+
<section className="space-y-6">
910
<AnyAuth />
1011
</section>
12+
<section className="space-y-8">
13+
<Profiles />
14+
</section>
1115
</ThirdwebProvider>
1216
);
1317
}
@@ -17,7 +21,7 @@ function AnyAuth() {
1721
<>
1822
<div className="space-y-2">
1923
<h2 className="font-semibold text-2xl tracking-tight sm:text-3xl">
20-
Your own Ecosystem
24+
Build your own Ecosystem
2125
</h2>
2226
<p className="max-w-[600px]">
2327
Build a public or permissioned ecosystem by allowing third party apps

apps/playground-web/src/app/connect/in-app-wallet/page.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { CodeExample } from "@/components/code/code-example";
22
import { InAppConnectEmbed } from "../../../components/in-app-wallet/connect-button";
3+
import {
4+
LinkAccount,
5+
LinkedAccounts,
6+
} from "../../../components/in-app-wallet/profiles";
37
import ThirdwebProvider from "../../../components/thirdweb-provider";
48

59
export default function Page() {
@@ -8,6 +12,9 @@ export default function Page() {
812
<section className="space-y-8">
913
<AnyAuth />
1014
</section>
15+
<section className="mt-8 space-y-8">
16+
<Profiles />
17+
</section>
1118
</ThirdwebProvider>
1219
);
1320
}
@@ -67,3 +74,79 @@ function AnyAuth() {
6774
</>
6875
);
6976
}
77+
78+
export function Profiles() {
79+
return (
80+
<>
81+
<div className="space-y-2">
82+
<h2 className="font-semibold text-xl tracking-tight sm:text-2xl">
83+
View Linked Profiles
84+
</h2>
85+
<p className="max-w-[600px]">
86+
View all web2 and web3 linked profiles for a user along with specific
87+
details for each profile type, including name, email, profile picture
88+
and more.
89+
</p>
90+
</div>
91+
92+
<CodeExample
93+
preview={<LinkedAccounts />}
94+
code={`import { useProfiles } from "thirdweb/react";
95+
96+
function App() {
97+
const { data: profiles } = useProfiles({
98+
client,
99+
});
100+
101+
return (
102+
<div>
103+
{profiles?.map((profile) => (
104+
<div key={profile.type}>
105+
<ProfileCard profile={profile} />
106+
</div>
107+
))}
108+
</div>
109+
);
110+
};`}
111+
lang="tsx"
112+
/>
113+
114+
<div className="space-y-2">
115+
<h2 className="font-semibold text-xl tracking-tight sm:text-2xl">
116+
Link another profile
117+
</h2>
118+
<p className="max-w-[600px]">
119+
Link a web2 or web3 profile to the connected account.
120+
<br />
121+
You can do this with hooks like shown here or from the prebuilt
122+
connect UI.
123+
</p>
124+
</div>
125+
126+
<CodeExample
127+
preview={<LinkAccount />}
128+
code={`import { useLinkProfile } from "thirdweb/react";
129+
130+
function App() {
131+
const { mutate: linkProfile, isPending, error } = useLinkProfile();
132+
133+
return (
134+
<div>
135+
<button
136+
onClick={() => linkProfile({
137+
client: THIRDWEB_CLIENT,
138+
strategy: "wallet",
139+
wallet: createWallet("com.coinbase.wallet"),
140+
chain: baseSepolia,
141+
})}
142+
>
143+
Link Coinbase Wallet
144+
</button>
145+
</div>
146+
);
147+
};`}
148+
lang="tsx"
149+
/>
150+
</>
151+
);
152+
}

apps/playground-web/src/app/connect/sign-in/components/CodeGen.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
import { Suspense, lazy } from "react";
2-
import { LoadingDots } from "../../../../components/ui/LoadingDots";
2+
import { CodeLoading } from "../../../../components/code/code.client";
33
import type { ConnectPlaygroundOptions } from "./types";
44

55
const CodeClient = lazy(
66
() => import("../../../../components/code/code.client"),
77
);
88

9-
function CodeLoading() {
10-
return (
11-
<div className="flex h-[300px] items-center justify-center xl:h-[calc(100vh-100px)]">
12-
<LoadingDots />
13-
</div>
14-
);
15-
}
16-
179
export function CodeGen(props: {
1810
connectOptions: ConnectPlaygroundOptions;
1911
}) {

apps/playground-web/src/app/navLinks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const navLinks: SidebarLink[] = [
4646
href: "/connect/in-app-wallet",
4747
},
4848
{
49-
name: "Your own Ecosystem",
49+
name: "Ecosystems",
5050
href: "/connect/in-app-wallet/ecosystem",
5151
},
5252
{

apps/playground-web/src/components/code/code.client.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { keepPreviousData, useQuery } from "@tanstack/react-query";
22
import type { BundledLanguage } from "shiki";
3+
import { LoadingDots } from "../ui/LoadingDots";
34
import { RenderCode } from "./RenderCode";
45
import { getCodeHtml } from "./getCodeHtml";
56

@@ -14,6 +15,14 @@ export type CodeProps = {
1415
scrollableClassName?: string;
1516
};
1617

18+
export function CodeLoading() {
19+
return (
20+
<div className="flex h-[300px] items-center justify-center xl:h-[calc(100vh-100px)]">
21+
<LoadingDots />
22+
</div>
23+
);
24+
}
25+
1726
export const CodeClient: React.FC<CodeProps> = ({
1827
code,
1928
lang,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"use client";
2+
import { baseSepolia } from "thirdweb/chains";
3+
import { useActiveAccount, useLinkProfile, useProfiles } from "thirdweb/react";
4+
import { type WalletId, createWallet } from "thirdweb/wallets";
5+
import { THIRDWEB_CLIENT } from "../../lib/client";
6+
import CodeClient, { CodeLoading } from "../code/code.client";
7+
import { Button } from "../ui/button";
8+
9+
export function LinkedAccounts() {
10+
const { data: profiles } = useProfiles({
11+
client: THIRDWEB_CLIENT,
12+
});
13+
14+
return (
15+
<div className="flex flex-col gap-4 p-6">
16+
{profiles ? (
17+
<CodeClient
18+
code={JSON.stringify(profiles, null, 2)}
19+
lang={"json"}
20+
loader={<CodeLoading />}
21+
/>
22+
) : (
23+
<p>Login to see linked profiles</p>
24+
)}
25+
</div>
26+
);
27+
}
28+
29+
export function LinkAccount() {
30+
const { mutate: linkProfile, isPending, error } = useLinkProfile();
31+
const account = useActiveAccount();
32+
const linkWallet = async (walletId: WalletId) => {
33+
linkProfile({
34+
client: THIRDWEB_CLIENT,
35+
strategy: "wallet",
36+
wallet: createWallet(walletId),
37+
chain: baseSepolia,
38+
});
39+
};
40+
41+
const linkPasskey = async () => {
42+
linkProfile({
43+
client: THIRDWEB_CLIENT,
44+
strategy: "passkey",
45+
type: "sign-up",
46+
});
47+
};
48+
49+
return (
50+
<div className="flex flex-col gap-4 p-6">
51+
{account ? (
52+
<>
53+
{isPending ? (
54+
<p>Linking...</p>
55+
) : (
56+
<>
57+
<Button
58+
variant="default"
59+
onClick={() => linkWallet("com.coinbase.wallet")}
60+
className="rounded-full p-6"
61+
disabled={isPending}
62+
>
63+
Link Coinbase Wallet
64+
</Button>
65+
<Button
66+
variant="default"
67+
onClick={() => linkWallet("io.metamask")}
68+
className="rounded-full p-6"
69+
disabled={isPending}
70+
>
71+
Link MetaMask
72+
</Button>
73+
<Button
74+
variant="default"
75+
onClick={linkPasskey}
76+
className="rounded-full p-6"
77+
disabled={isPending}
78+
>
79+
Link Passkey
80+
</Button>
81+
</>
82+
)}
83+
{error && <p className="text-red-500">Error: {error.message}</p>}
84+
</>
85+
) : (
86+
<p>Login to link another account.</p>
87+
)}
88+
</div>
89+
);
90+
}

apps/portal/src/app/react-native/v5/sidebar.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ export const sidebar: SideBar = {
121121
},
122122
],
123123
},
124+
{
125+
name: "Ecosystem Wallets",
126+
links: [
127+
{
128+
name: "React API",
129+
href: "/react/v5/ecosystem-wallet/get-started",
130+
icon: <ReactIcon />,
131+
},
132+
],
133+
},
124134
{
125135
name: "Account Abstraction",
126136
links: [
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {
2+
Tabs,
3+
TabsList,
4+
TabsTrigger,
5+
TabsContent,
6+
DocImage,
7+
createMetadata,
8+
} from "@doc";
9+
10+
export const metadata = createMetadata({
11+
title: "Connect users with Ecosystem Wallets",
12+
description:
13+
"use the prebuilt connect UI components to authenticate users and connect ecosystem wallets",
14+
image: {
15+
title: "Connect users with Ecosystem Wallets",
16+
icon: "wallets",
17+
},
18+
});
19+
20+
# Connect Users to your Ecosystem
21+
22+
## Using the Connect UI components
23+
24+
If you're building a [React website](/typescript/react/v5/ConnectButton), [React Native app](/react-native/v5/), or [Unity game](/unity/ConnectWallet) you can use the prebuilt connect UI components to authenticate users and connect their wallets accross your ecosystem.
25+
26+
```jsx
27+
import { ThirdwebProvider, ConnectButton } from "thirdweb/react";
28+
import { inAppWallet } from "thirdweb/wallets";
29+
30+
const client = createThirdwebClient({ clientId });
31+
const wallets = [ecosystemWallet("ecosystem.your-ecosystem-id")];
32+
33+
export default function App() {
34+
return (
35+
<ThirdwebProvider>
36+
<ConnectButton client={client} wallets={wallets} />
37+
</ThirdwebProvider>
38+
);
39+
}
40+
```
41+
42+
## Using your own UI
43+
44+
You can also build your own UI using the low-level hooks and functions. Remember to wrap your app in a `ThirdwebProvider` to ensure that the wallet is available to all components in your app.
45+
46+
```tsx
47+
import { ecosystemWallet } from "thirdweb/wallets";
48+
49+
const wallet = ecosystemWallet("ecosystem.your-ecosystem-id");
50+
51+
const LoginComponent = () => {
52+
const { connect, isLoading } = useConnect();
53+
54+
return <button onClick={() => connect(async () => {
55+
await wallet.connect({
56+
client,
57+
strategy: "discord", // or any supported auth strategy
58+
})
59+
return wallet;
60+
})}>Connect</button>;
61+
};
62+
```
63+
64+
## Passing a partner ID
65+
66+
For closed ecosystems, you can pass a valid `partnerId` to the `ecosystemWallet` provided by the ecosystem owner.
67+
68+
```tsx
69+
const wallet = ecosystemWallet("ecosystem.your-ecosystem-id", {
70+
partnerId: "your-partner-id",
71+
});
72+
```

0 commit comments

Comments
 (0)