Skip to content

Commit ce5e6dc

Browse files
authored
Merge branch 'main' into mintable-module
2 parents 2de303a + 0ad80db commit ce5e6dc

File tree

8 files changed

+175
-7
lines changed

8 files changed

+175
-7
lines changed

.changeset/real-cars-sell.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
Add SiteLink component for creating wallet-aware links between thirdweb-enabled sites. This component automatically adds wallet connection parameters to the target URL when a wallet is connected, enabling seamless wallet state sharing between sites.
6+
7+
Example:
8+
```tsx
9+
import { SiteLink } from "thirdweb/react";
10+
11+
function App() {
12+
return (
13+
<SiteLink
14+
href="https://thirdweb.com"
15+
client={thirdwebClient}
16+
ecosystem={{ id: "ecosystem.thirdweb" }}
17+
>
18+
Visit thirdweb.com with connected wallet
19+
</SiteLink>
20+
);
21+
}
22+
```

apps/portal/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
"dev": "next dev --turbopack",
99
"prebuild": "pnpm run create-index",
1010
"build": "next build",
11-
"postbuild": "pnpm run extract-search-data",
11+
"postbuild": "pnpm run extract-search-data && pnpm next-sitemap",
1212
"start": "next start",
1313
"lint": "biome check ./src && eslint ./src",
1414
"fix": "biome check ./src --fix && eslint ./src --fix",
1515
"create-index": "pnpm tsx scripts/createEmptySearchIndex.ts",
16-
"extract-search-data": "pnpm tsx scripts/extractSearchData.ts",
17-
"postextract-search-data": "next-sitemap"
16+
"extract-search-data": "pnpm tsx scripts/extractSearchData.ts"
1817
},
1918
"dependencies": {
2019
"@dirtycajunrice/klee": "^1.0.1",

apps/portal/src/components/Document/metadata.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function createMetadata(obj: {
3232
}): Metadata {
3333
return {
3434
title: obj.title,
35+
description: obj.description,
3536
metadataBase: new URL("https://portal.thirdweb.com"),
3637
twitter: {
3738
title: obj.title,
@@ -45,6 +46,7 @@ export function createMetadata(obj: {
4546
description: obj.description,
4647
locale: "en_US",
4748
type: "website",
49+
siteName: "thirdweb docs",
4850
images: obj.image
4951
? [
5052
{

packages/thirdweb/src/exports/react.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,5 +196,6 @@ export type {
196196
LensProfile,
197197
} from "../social/types.js";
198198

199-
// Site Embed
199+
// Site Embed and Linking
200200
export { SiteEmbed } from "../react/web/ui/SiteEmbed.js";
201+
export { SiteLink } from "../react/web/ui/SiteLink.js";

packages/thirdweb/src/react/web/ui/SiteEmbed.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import { useActiveWallet } from "../../core/hooks/wallets/useActiveWallet.js";
1717
*
1818
* @param {Object} props - The props to pass to the iframe
1919
* @param {String} props.src - The URL of the site to embed
20-
* @param {ThirdwebClient} props.client - The client to use for the embedded site
21-
* @param {Ecosystem} [props.ecosystem] - The ecosystem to use for the embedded site
20+
* @param {ThirdwebClient} props.client - The current site's thirdweb client
21+
* @param {Ecosystem} [props.ecosystem] - The ecosystem to use for the wallet connection in the embedded site
2222
*
2323
* @example
2424
* ```tsx
2525
* import { SiteEmbed } from "thirdweb/react";
2626
*
27-
* <SiteEmbed src="https://thirdweb.com" clientId="thirdweb-client-id" />
27+
* <SiteEmbed src="https://thirdweb.com" client={thirdwebClient} ecosystem={{ id: "ecosystem.thirdweb" }} />
2828
* ```
2929
*/
3030
export function SiteEmbed({
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { describe, expect, it } from "vitest";
2+
import { render, waitFor } from "../../../../test/src/react-render.js";
3+
import { TEST_CLIENT } from "../../../../test/src/test-clients.js";
4+
import { SiteLink } from "./SiteLink.js";
5+
6+
describe("SiteLink", () => {
7+
it("renders anchor with correct href", () => {
8+
const testUrl = "https://example.com/";
9+
const { container } = render(
10+
<SiteLink href={testUrl} client={TEST_CLIENT}>
11+
Test Link
12+
</SiteLink>,
13+
);
14+
15+
const anchor = container.querySelector("a");
16+
expect(anchor).toBeTruthy();
17+
expect(anchor?.href).toBe(testUrl);
18+
expect(anchor?.textContent).toBe("Test Link");
19+
});
20+
21+
it("throws error if clientId is not provided", () => {
22+
const testUrl = "https://example.com/";
23+
expect(() =>
24+
render(
25+
// biome-ignore lint/suspicious/noExplicitAny: testing invalid input
26+
<SiteLink href={testUrl} client={{} as any}>
27+
Test Link
28+
</SiteLink>,
29+
),
30+
).toThrow("The SiteLink client must have a clientId");
31+
});
32+
33+
it("adds wallet params to url when wallet is connected", async () => {
34+
const testUrl = "https://example.com/";
35+
const { container } = render(
36+
<SiteLink href={testUrl} client={TEST_CLIENT}>
37+
Test Link
38+
</SiteLink>,
39+
{
40+
setConnectedWallet: true,
41+
},
42+
);
43+
44+
const anchor = container.querySelector("a");
45+
expect(anchor).toBeTruthy();
46+
await waitFor(() => expect(anchor?.href).toContain("walletId="));
47+
});
48+
});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"use client";
2+
import { useQuery } from "@tanstack/react-query";
3+
import type { ThirdwebClient } from "../../../client/client.js";
4+
import { getLastAuthProvider } from "../../../react/core/utils/storage.js";
5+
import { webLocalStorage } from "../../../utils/storage/webStorage.js";
6+
import { isEcosystemWallet } from "../../../wallets/ecosystem/is-ecosystem-wallet.js";
7+
import { ClientScopedStorage } from "../../../wallets/in-app/core/authentication/client-scoped-storage.js";
8+
import type { Ecosystem } from "../../../wallets/in-app/core/wallet/types.js";
9+
import { useActiveWallet } from "../../core/hooks/wallets/useActiveWallet.js";
10+
11+
/**
12+
* Creates a link to another thirdweb-supported site with wallet connection parameters.
13+
*
14+
* @note The target site must support the connected wallet (ecosystem or in-app).
15+
*
16+
* @param {Object} props - The props to pass to the anchor tag
17+
* @param {String} props.href - The URL of the site to link to
18+
* @param {ThirdwebClient} props.client - The current site's thirdweb client
19+
* @param {Ecosystem} [props.ecosystem] - The ecosystem to use for the wallet connection in the target site
20+
* @param {React.ReactNode} props.children - The content to render inside the link
21+
*
22+
* @example
23+
* ```tsx
24+
* import { SiteLink } from "thirdweb/react";
25+
*
26+
* <SiteLink href="https://thirdweb.com" client={thirdwebClient} ecosystem={{ id: "ecosystem.thirdweb" }}>
27+
* Visit Site
28+
* </SiteLink>
29+
* ```
30+
*/
31+
export function SiteLink({
32+
href,
33+
client,
34+
ecosystem,
35+
children,
36+
...props
37+
}: {
38+
href: string;
39+
client: ThirdwebClient;
40+
ecosystem?: Ecosystem;
41+
children: React.ReactNode;
42+
} & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href">) {
43+
if (!client.clientId) {
44+
throw new Error("The SiteLink client must have a clientId");
45+
}
46+
47+
const activeWallet = useActiveWallet();
48+
const walletId = activeWallet?.id;
49+
50+
const {
51+
data: { authProvider, authCookie } = {},
52+
} = useQuery({
53+
queryKey: ["site-link", walletId, href, client.clientId, ecosystem],
54+
enabled:
55+
activeWallet && (isEcosystemWallet(activeWallet) || walletId === "inApp"),
56+
queryFn: async () => {
57+
const storage = new ClientScopedStorage({
58+
storage: webLocalStorage,
59+
clientId: client.clientId,
60+
ecosystem,
61+
});
62+
63+
const authProvider = await getLastAuthProvider(webLocalStorage);
64+
const authCookie = await storage.getAuthCookie();
65+
66+
return { authProvider, authCookie };
67+
},
68+
});
69+
70+
const url = new URL(href);
71+
if (walletId) {
72+
url.searchParams.set("walletId", walletId);
73+
}
74+
if (authProvider) {
75+
url.searchParams.set("authProvider", authProvider);
76+
}
77+
if (authCookie) {
78+
url.searchParams.set("authCookie", authCookie);
79+
}
80+
81+
return (
82+
<a href={encodeURI(url.toString())} {...props}>
83+
{children}
84+
</a>
85+
);
86+
}

turbo.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
"outputs": ["dist/**"],
77
"dependsOn": ["^build"]
88
},
9+
"postbuild": {
10+
"dependsOn": ["^postbuild"],
11+
"outputs": [
12+
"dist/**",
13+
".next/**",
14+
"public/robots.txt",
15+
"public/sitemap*.xml",
16+
"searchIndex.json"
17+
]
18+
},
919
"bench": {
1020
"cache": false,
1121
"dependsOn": []

0 commit comments

Comments
 (0)