Skip to content

Commit 76e38a2

Browse files
committed
update
1 parent f25d1d8 commit 76e38a2

File tree

13 files changed

+552
-108
lines changed

13 files changed

+552
-108
lines changed

.changeset/fluffy-pets-tie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Fix caching issues for headless component; improve code coverage

packages/thirdweb/src/react/web/ui/prebuilt/Chain/icon.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
22
import type { JSX } from "react";
33
import { getChainMetadata } from "../../../../../chains/utils.js";
44
import type { ThirdwebClient } from "../../../../../client/client.js";
5+
import { getFunctionId } from "../../../../../utils/function-id.js";
56
import { resolveScheme } from "../../../../../utils/ipfs.js";
67
import { useChainContext } from "./provider.js";
78

@@ -119,7 +120,18 @@ export function ChainIcon({
119120
}: ChainIconProps) {
120121
const { chain } = useChainContext();
121122
const iconQuery = useQuery({
122-
queryKey: ["_internal_chain_icon_", chain.id] as const,
123+
queryKey: [
124+
"_internal_chain_icon_",
125+
chain.id,
126+
{
127+
resolver:
128+
typeof iconResolver === "string"
129+
? iconResolver
130+
: typeof iconResolver === "function"
131+
? getFunctionId(iconResolver)
132+
: undefined,
133+
},
134+
] as const,
123135
queryFn: async () => {
124136
if (typeof iconResolver === "string") {
125137
return iconResolver;

packages/thirdweb/src/react/web/ui/prebuilt/Chain/name.test.tsx

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import { describe, expect, it } from "vitest";
22
import { render, screen, waitFor } from "~test/react-render.js";
33
import { ethereum } from "../../../../../chains/chain-definitions/ethereum.js";
44
import { defineChain } from "../../../../../chains/utils.js";
5-
import { ChainName } from "./name.js";
5+
import { ChainName, fetchChainName } from "./name.js";
66
import { ChainProvider } from "./provider.js";
77

88
describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
9-
it("should return the correct chain name, if the name exists in the chain object", () => {
9+
it("should return the correct chain name, if the name exists in the chain object", async () => {
1010
render(
1111
<ChainProvider chain={ethereum}>
1212
<ChainName />
1313
</ChainProvider>,
1414
);
15-
waitFor(() =>
15+
await waitFor(() =>
1616
expect(
1717
screen.getByText("Ethereum", {
1818
exact: true,
@@ -22,13 +22,13 @@ describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
2222
);
2323
});
2424

25-
it("should return the correct chain name, if the name is loaded from the server", () => {
25+
it("should return the correct chain name, if the name is loaded from the server", async () => {
2626
render(
2727
<ChainProvider chain={defineChain(1)}>
2828
<ChainName />
2929
</ChainProvider>,
3030
);
31-
waitFor(() =>
31+
await waitFor(() =>
3232
expect(
3333
screen.getByText("Ethereum Mainnet", {
3434
exact: true,
@@ -38,13 +38,13 @@ describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
3838
);
3939
});
4040

41-
it("should return the correct FORMATTED chain name", () => {
41+
it("should return the correct FORMATTED chain name", async () => {
4242
render(
4343
<ChainProvider chain={ethereum}>
4444
<ChainName formatFn={(str: string) => `${str}-formatted`} />
4545
</ChainProvider>,
4646
);
47-
waitFor(() =>
47+
await waitFor(() =>
4848
expect(
4949
screen.getByText("Ethereum-formatted", {
5050
exact: true,
@@ -54,14 +54,14 @@ describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
5454
);
5555
});
5656

57-
it("should fallback properly when fail to resolve chain name", () => {
57+
it("should fallback properly when fail to resolve chain name", async () => {
5858
render(
5959
<ChainProvider chain={defineChain(-1)}>
6060
<ChainName fallbackComponent={<span>oops</span>} />
6161
</ChainProvider>,
6262
);
6363

64-
waitFor(() =>
64+
await waitFor(() =>
6565
expect(
6666
screen.getByText("oops", {
6767
exact: true,
@@ -70,4 +70,31 @@ describe.runIf(process.env.TW_SECRET_KEY)("ChainName component", () => {
7070
).toBeInTheDocument(),
7171
);
7272
});
73+
74+
it("fetchChainName should respect nameResolver as a string", async () => {
75+
const res = await fetchChainName({
76+
chain: ethereum,
77+
nameResolver: "eth_mainnet",
78+
});
79+
expect(res).toBe("eth_mainnet");
80+
});
81+
82+
it("fetchChainName should respect nameResolver as a non-async function", async () => {
83+
const res = await fetchChainName({
84+
chain: ethereum,
85+
nameResolver: () => "eth_mainnet",
86+
});
87+
expect(res).toBe("eth_mainnet");
88+
});
89+
90+
it("fetchChainName should respect nameResolver as an async function", async () => {
91+
const res = await fetchChainName({
92+
chain: ethereum,
93+
nameResolver: async () => {
94+
await new Promise((resolve) => setTimeout(resolve, 2000));
95+
return "eth_mainnet";
96+
},
97+
});
98+
expect(res).toBe("eth_mainnet");
99+
});
73100
});

packages/thirdweb/src/react/web/ui/prebuilt/Chain/name.tsx

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
44
import type React from "react";
55
import type { JSX } from "react";
6+
import type { Chain } from "../../../../../chains/types.js";
67
import { getChainMetadata } from "../../../../../chains/utils.js";
8+
import { getFunctionId } from "../../../../../utils/function-id.js";
79
import { useChainContext } from "./provider.js";
810

911
/**
@@ -155,19 +157,19 @@ export function ChainName({
155157
}: ChainNameProps) {
156158
const { chain } = useChainContext();
157159
const nameQuery = useQuery({
158-
queryKey: ["_internal_chain_name_", chain.id] as const,
159-
queryFn: async () => {
160-
if (typeof nameResolver === "string") {
161-
return nameResolver;
162-
}
163-
if (typeof nameResolver === "function") {
164-
return nameResolver();
165-
}
166-
if (chain.name) {
167-
return chain.name;
168-
}
169-
return getChainMetadata(chain).then((data) => data.name);
170-
},
160+
queryKey: [
161+
"_internal_chain_name_",
162+
chain.id,
163+
{
164+
resolver:
165+
typeof nameResolver === "string"
166+
? nameResolver
167+
: typeof nameResolver === "function"
168+
? getFunctionId(nameResolver)
169+
: undefined,
170+
},
171+
] as const,
172+
queryFn: async () => fetchChainName({ chain, nameResolver }),
171173
...queryOptions,
172174
});
173175

@@ -183,3 +185,23 @@ export function ChainName({
183185

184186
return <span {...restProps}>{displayValue}</span>;
185187
}
188+
189+
/**
190+
* @internal Exported for tests only
191+
*/
192+
export async function fetchChainName(props: {
193+
chain: Chain;
194+
nameResolver?: string | (() => string) | (() => Promise<string>);
195+
}) {
196+
const { nameResolver, chain } = props;
197+
if (typeof nameResolver === "string") {
198+
return nameResolver;
199+
}
200+
if (typeof nameResolver === "function") {
201+
return nameResolver();
202+
}
203+
if (chain.name) {
204+
return chain.name;
205+
}
206+
return getChainMetadata(chain).then((data) => data.name);
207+
}

packages/thirdweb/src/react/web/ui/prebuilt/NFT/description.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
44
import type { JSX } from "react";
55
import type { ThirdwebContract } from "../../../../../contract/contract.js";
6+
import { getFunctionId } from "../../../../../utils/function-id.js";
67
import { useNFTContext } from "./provider.js";
78
import { getNFTInfo } from "./utils.js";
89

@@ -100,7 +101,7 @@ export function NFTDescription({
100101
typeof descriptionResolver === "string"
101102
? descriptionResolver
102103
: typeof descriptionResolver === "function"
103-
? descriptionResolver.toString()
104+
? getFunctionId(descriptionResolver)
104105
: undefined,
105106
},
106107
],

packages/thirdweb/src/react/web/ui/prebuilt/NFT/media.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
22
import type { JSX } from "react";
33
import type { ThirdwebContract } from "../../../../../contract/contract.js";
4+
import { getFunctionId } from "../../../../../utils/function-id.js";
45
import { MediaRenderer } from "../../MediaRenderer/MediaRenderer.js";
56
import type { MediaRendererProps } from "../../MediaRenderer/types.js";
67
import { useNFTContext } from "./provider.js";
@@ -138,7 +139,7 @@ export function NFTMedia({
138139
typeof mediaResolver === "object"
139140
? mediaResolver
140141
: typeof mediaResolver === "function"
141-
? mediaResolver.toString()
142+
? getFunctionId(mediaResolver)
142143
: undefined,
143144
},
144145
],

packages/thirdweb/src/react/web/ui/prebuilt/NFT/name.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
22
import type { JSX } from "react";
33
import type { ThirdwebContract } from "../../../../../contract/contract.js";
4+
import { getFunctionId } from "../../../../../utils/function-id.js";
45
import { useNFTContext } from "./provider.js";
56
import { getNFTInfo } from "./utils.js";
67

@@ -100,7 +101,7 @@ export function NFTName({
100101
typeof nameResolver === "string"
101102
? nameResolver
102103
: typeof nameResolver === "function"
103-
? nameResolver.toString()
104+
? getFunctionId(nameResolver)
104105
: undefined,
105106
},
106107
],

packages/thirdweb/src/react/web/ui/prebuilt/Token/icon.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { getChainMetadata } from "../../../../../chains/utils.js";
44
import { NATIVE_TOKEN_ADDRESS } from "../../../../../constants/addresses.js";
55
import { getContract } from "../../../../../contract/contract.js";
66
import { getContractMetadata } from "../../../../../extensions/common/read/getContractMetadata.js";
7+
import { getFunctionId } from "../../../../../utils/function-id.js";
78
import { resolveScheme } from "../../../../../utils/ipfs.js";
89
import { useTokenContext } from "./provider.js";
910

@@ -115,7 +116,19 @@ export function TokenIcon({
115116
}: TokenIconProps) {
116117
const { address, client, chain } = useTokenContext();
117118
const iconQuery = useQuery({
118-
queryKey: ["_internal_token_icon_", chain.id, address] as const,
119+
queryKey: [
120+
"_internal_token_icon_",
121+
chain.id,
122+
address,
123+
{
124+
resolver:
125+
typeof iconResolver === "string"
126+
? iconResolver
127+
: typeof iconResolver === "function"
128+
? getFunctionId(iconResolver)
129+
: undefined,
130+
},
131+
] as const,
119132
queryFn: async () => {
120133
if (typeof iconResolver === "string") {
121134
return iconResolver;

0 commit comments

Comments
 (0)