"use client"; import { type UseQueryOptions, useQuery } from "@tanstack/react-query"; import type React from "react"; import type { JSX } from "react"; import type { Chain } from "../../../../../chains/types.js"; import { getChainMetadata } from "../../../../../chains/utils.js"; import { getFunctionId } from "../../../../../utils/function-id.js"; import { useChainContext } from "./provider.js"; /** * Props for the ChainName component * @component * @chain */ export interface ChainNameProps extends Omit, "children"> { /** * This prop can be a string or a (async) function that resolves to a string, representing the name of the chain * This is particularly useful if you already have a way to fetch the chain name. */ nameResolver?: string | (() => string) | (() => Promise); /** * A function to format the name's display value * Particularly useful to avoid overflowing-UI issues * * ```tsx * doSomething()} /> * ``` */ formatFn?: (str: string) => string; /** * This component will be shown while the name of the chain is being fetched * If not passed, the component will return `null`. * * You can/should pass a loading sign or spinner to this prop. * @example * ```tsx * } /> * ``` */ loadingComponent?: JSX.Element; /** * This component will be shown if the name fails to be retreived * If not passed, the component will return `null`. * * You can/should pass a descriptive text/component to this prop, indicating that the * name was not fetched successfully * @example * ```tsx * Failed to load} * /> * ``` */ fallbackComponent?: JSX.Element; /** * Optional `useQuery` params */ queryOptions?: Omit, "queryFn" | "queryKey">; } /** * This component fetches then shows the name of a chain. * It inherits all the attributes of a HTML component, hence you can style it just like how you would style a normal * * * @example * ### Basic usage * ```tsx * import { ChainProvider, ChainName } from "thirdweb/react"; * import { ethereum } from "thirdweb/chains"; * * * * * ``` * Result: * ```html * Ethereum Mainnet * ``` * * ### Custom name resolver * By default ChainName will call the thirdweb API to retrieve the chain name. * However if you have a different way to fetch the name, you can pass the function to the `nameResolver` prop. * Note: nameResolver should either be a string or a function (async) that returns a string. * ```tsx * async function fetchNameMethod() { * // your own fetching logic * return "the chain name"; * } * * * ``` * * Alternatively you can also pass in a string directly: * ```tsx * * ``` * * * ### Format the name (capitalize, truncate, etc.) * The ChainName component accepts a `formatFn` which takes in a string and outputs a string * The function is used to modify the name of the chain * * ```tsx * const concatStr = (str: string):string => str + "Network" * * * * * ``` * * Result: * ```html * Ethereum Mainnet Network * ``` * * ### Show a loading sign when the name is being fetched * ```tsx * import { ChainProvider, ChainName } from "thirdweb/react"; * * * } /> * * ``` * * ### Fallback to something when the name fails to resolve * ```tsx * * * * ``` * * ### Custom query options for useQuery * This component uses `@tanstack-query`'s useQuery internally. * You can use the `queryOptions` prop for more fine-grained control * ```tsx * * ``` * * @component * @chain * @beta */ export function ChainName({ nameResolver, formatFn, loadingComponent, fallbackComponent, queryOptions, ...restProps }: ChainNameProps) { const { chain } = useChainContext(); const nameQuery = useQuery({ queryFn: async () => fetchChainName({ chain, nameResolver }), queryKey: getQueryKeys({ chainId: chain.id, nameResolver }), ...queryOptions, }); if (nameQuery.isLoading) { return loadingComponent || null; } if (!nameQuery.data) { return fallbackComponent || null; } const displayValue = formatFn ? formatFn(nameQuery.data) : nameQuery.data; return {displayValue}; } /** * @internal Exported for tests only */ export async function fetchChainName(props: { chain: Chain; nameResolver?: string | (() => string) | (() => Promise); }) { const { nameResolver, chain } = props; if (typeof nameResolver === "string") { return nameResolver; } if (typeof nameResolver === "function") { return nameResolver(); } if (chain.name) { return chain.name; } return getChainMetadata(chain).then((data) => data.name); } /** * @internal Exported for tests */ export function getQueryKeys(props: { chainId: number; nameResolver?: string | (() => string) | (() => Promise); }) { if (typeof props.nameResolver === "function") { return [ "_internal_chain_name_", props.chainId, { resolver: getFunctionId(props.nameResolver) }, ] as const; } return ["_internal_chain_name_", props.chainId] as const; }