diff --git a/apps/dashboard/src/@/components/ui/code.tsx b/apps/dashboard/src/@/components/ui/code.tsx new file mode 100644 index 00000000000..876b87b4896 --- /dev/null +++ b/apps/dashboard/src/@/components/ui/code.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { useClipboard } from "hooks/useClipboard"; +import { CheckIcon, CopyIcon } from "lucide-react"; +import { cn } from "../../lib/utils"; +import { Button } from "./button"; + +export function PlainTextCodeBlock(props: { + code: string; + copyButtonClassName?: string; +}) { + const { hasCopied, onCopy } = useClipboard(props.code); + + return ( +
+ + {props.code} + + +
+ ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx index 7e2cbc44306..0c2f3ccaa7d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/ContractOverviewPage.tsx @@ -1,5 +1,3 @@ -"use client"; - import { PublishedBy } from "components/contract-components/shared/published-by"; import type { ThirdwebContract } from "thirdweb"; import { AnalyticsOverview } from "./components/Analytics"; @@ -21,6 +19,7 @@ interface ContractOverviewPageProps { isPermissionsEnumerable: boolean; chainSlug: string; isAnalyticsSupported: boolean; + functionSelectors: string[]; } const TRACKING_CATEGORY = "contract_overview"; @@ -35,6 +34,7 @@ export const ContractOverviewPage: React.FC = ({ isPermissionsEnumerable, chainSlug, isAnalyticsSupported, + functionSelectors, }) => { return (
@@ -45,6 +45,7 @@ export const ContractOverviewPage: React.FC = ({ isErc20={isErc20} contract={contract} chainSlug={chainSlug} + functionSelectors={functionSelectors} /> {isAnalyticsSupported && ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx index 270194a4774..aa47e4783b2 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/Analytics.tsx @@ -1,3 +1,5 @@ +"use client"; + import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart"; import { Button } from "@/components/ui/button"; import { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx index 72714f7f346..9b283c1e78f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/BuildYourApp.tsx @@ -1,3 +1,5 @@ +"use client"; + import { Flex, GridItem, @@ -7,7 +9,9 @@ import { } from "@chakra-ui/react"; import { ChakraNextImage as Image } from "components/Image"; import { PRODUCTS } from "components/product-pages/common/nav/data"; -import { Card, Text, TrackedLink, type TrackedLinkProps } from "tw-components"; +import { Card } from "tw-components/card"; +import { TrackedLink, type TrackedLinkProps } from "tw-components/link"; +import { Text } from "tw-components/text"; const RENDERED_PRODUCTS = ["sdk", "storage", "ui-components", "auth"]; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx index e6eb237a3bd..52326429c0e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/ContractChecklist.tsx @@ -1,7 +1,8 @@ +"use client"; + import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only"; import { useIsMinter } from "@3rdweb-sdk/react/hooks/useContractRoles"; import { StepsCard } from "components/dashboard/StepsCard"; -import { useContractFunctionSelectors } from "contract-ui/hooks/useContractFunctionSelectors"; import Link from "next/link"; import { useMemo } from "react"; import type { ThirdwebContract } from "thirdweb"; @@ -18,6 +19,7 @@ interface ContractChecklistProps { isErc1155: boolean; isErc20: boolean; chainSlug: string; + functionSelectors: string[]; } type Step = { @@ -27,13 +29,10 @@ type Step = { }; export const ContractChecklist: React.FC = (props) => { - const functionSelectorQuery = useContractFunctionSelectors(props.contract); return ( // if no permissions, simply return null (do not fail open) - {!!functionSelectorQuery.data?.length && ( - - )} + ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx index 2e28e150fa5..6a710432ed6 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/LatestEvents.tsx @@ -1,3 +1,5 @@ +"use client"; + import { type InternalTransaction, useActivity, diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx index 4c06de88a14..3d608267ea0 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/MarketplaceDetails.tsx @@ -1,3 +1,5 @@ +"use client"; + import { WalletAddress } from "@/components/blocks/wallet-address"; import { Badge } from "@/components/ui/badge"; import { SkeletonContainer } from "@/components/ui/skeleton"; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx index 59f7bd41336..ebf4dcefb6e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/NFTDetails.tsx @@ -1,9 +1,11 @@ +"use client"; + import { Flex, useBreakpointValue } from "@chakra-ui/react"; import type { ThirdwebContract } from "thirdweb"; import * as ERC721 from "thirdweb/extensions/erc721"; import * as ERC1155 from "thirdweb/extensions/erc1155"; import { useReadContract } from "thirdweb/react"; -import { TrackedLink, type TrackedLinkProps } from "tw-components"; +import { TrackedLink, type TrackedLinkProps } from "tw-components/link"; import { NFTCards } from "../../_components/NFTCards"; interface NFTDetailsProps { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx index 5c14353c650..8ee0f7fcd7f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx @@ -1,3 +1,5 @@ +"use client"; + import { ToolTipLabel } from "@/components/ui/tooltip"; import { Box, Flex, List, SimpleGrid, Tag } from "@chakra-ui/react"; import { getAllRoleMembers } from "contract-ui/hooks/permissions"; @@ -8,14 +10,11 @@ import { useMemo } from "react"; import { toast } from "sonner"; import { type ThirdwebContract, ZERO_ADDRESS } from "thirdweb"; import { useReadContract } from "thirdweb/react"; -import { - Button, - Card, - Heading, - Text, - TrackedLink, - type TrackedLinkProps, -} from "tw-components"; +import { Button } from "tw-components/button"; +import { Card } from "tw-components/card"; +import { Heading } from "tw-components/heading"; +import { TrackedLink, type TrackedLinkProps } from "tw-components/link"; +import { Text } from "tw-components/text"; import { shortenIfAddress } from "utils/usedapp-external"; interface PermissionsTableProps { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx index dfe1dfa6698..51846f31621 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/page.tsx @@ -31,6 +31,7 @@ export default async function Page(props: { } chainSlug={chainMetadata.slug} isAnalyticsSupported={contractPageMetadata.isAnalyticsSupported} + functionSelectors={contractPageMetadata.functionSelectors} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx b/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx index 602335a234e..7c2b20404f2 100644 --- a/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx @@ -1,4 +1,3 @@ -import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; import { Breadcrumb, BreadcrumbItem, @@ -7,12 +6,16 @@ import { BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; -import { ContractCard } from "components/explore/contract-card"; +import { + ContractCard, + ContractCardSkeleton, +} from "components/explore/contract-card"; import { DeployUpsellCard } from "components/explore/upsells/deploy-your-own"; import { ALL_CATEGORIES, getCategory } from "data/explore"; import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; +import { Suspense } from "react"; type ExploreCategoryPageProps = { params: { @@ -101,35 +104,36 @@ export default async function ExploreCategoryPage( } return ( - } key={publisher + contractId + overrides?.title} - publisher={publisher} - contractId={contractId} - titleOverride={overrides?.title} - descriptionOverride={overrides?.description} - tracking={{ - source: category.id, - itemIndex: `${idx}`, - }} - isBeta={category.isBeta} - modules={ - modules?.length - ? modules.map((m) => ({ - publisher: m.split("/")[0] || "", - moduleId: m.split("/")[1] || "", - })) - : undefined - } - /> + > + ({ + publisher: m.split("/")[0] || "", + moduleId: m.split("/")[1] || "", + })) + : undefined + } + /> + ); })}
- {/* TODO: remove this once we update the deploy upsell card */} - - - +
); @@ -140,3 +144,6 @@ export async function generateStaticParams() { params: { category }, })); } + +// TODO - figure out why this page is not building if we let it be static +export const dynamic = "force-dynamic"; diff --git a/apps/dashboard/src/app/(dashboard)/explore/page.tsx b/apps/dashboard/src/app/(dashboard)/explore/page.tsx index 41506da5c6b..c373595f296 100644 --- a/apps/dashboard/src/app/(dashboard)/explore/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/explore/page.tsx @@ -1,4 +1,3 @@ -import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; import { Breadcrumb, BreadcrumbItem, @@ -53,11 +52,11 @@ export default async function ExplorePage() {
- {/* TODO: remove this once we update the deploy upsell card */} - - - +
); } + +// TODO - figure out why this page is not building if we let it be static +export const dynamic = "force-dynamic"; diff --git a/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts b/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts index 040128e1763..c2a0971495c 100644 --- a/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts +++ b/apps/dashboard/src/components/contract-components/fetchDeployMetadata.ts @@ -1,7 +1,7 @@ import { getThirdwebClient } from "@/constants/thirdweb.server"; import { fetchDeployMetadata as sdkFetchDeployMetadata } from "thirdweb/contract"; import { removeUndefinedFromObjectDeep } from "../../utils/object"; -import { toContractIdIpfsHash } from "./hooks"; +import type { ContractId } from "./types"; // metadata PRE publish, only has the compiler output info (from CLI) @@ -15,3 +15,10 @@ export async function fetchDeployMetadata(contractId: string) { }), ); } + +function toContractIdIpfsHash(contractId: ContractId) { + if (contractId?.startsWith("ipfs://")) { + return contractId; + } + return `ipfs://${contractId}`; +} diff --git a/apps/dashboard/src/components/contract-components/hooks.ts b/apps/dashboard/src/components/contract-components/hooks.ts index 9d3721856ee..8943cf933e6 100644 --- a/apps/dashboard/src/components/contract-components/hooks.ts +++ b/apps/dashboard/src/components/contract-components/hooks.ts @@ -131,13 +131,6 @@ export function useFunctionParamsFromABI(abi?: Abi, functionName?: string) { }, [abi, functionName]); } -export function toContractIdIpfsHash(contractId: ContractId) { - if (contractId?.startsWith("ipfs://")) { - return contractId; - } - return `ipfs://${contractId}`; -} - export type PublishedContractDetails = Awaited< ReturnType >[number]; diff --git a/apps/dashboard/src/components/contract-components/shared/published-by.tsx b/apps/dashboard/src/components/contract-components/shared/published-by.tsx index e14f3cb7ba3..50802e0fb01 100644 --- a/apps/dashboard/src/components/contract-components/shared/published-by.tsx +++ b/apps/dashboard/src/components/contract-components/shared/published-by.tsx @@ -1,139 +1,147 @@ -import { useQuery } from "@tanstack/react-query"; -import { - useEns, - usePublishedContractsFromDeploy, -} from "components/contract-components/hooks"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; import { ContractCard } from "components/explore/contract-card"; import { THIRDWEB_DEPLOYER_ADDRESS } from "constants/addresses"; -import { useMemo } from "react"; import type { ThirdwebContract } from "thirdweb"; import { polygon } from "thirdweb/chains"; import { getBytecode, getContract } from "thirdweb/contract"; import { getPublishedUriFromCompilerUri } from "thirdweb/extensions/thirdweb"; import { getInstalledModules } from "thirdweb/modules"; -import { useActiveAccount, useReadContract } from "thirdweb/react"; import { download } from "thirdweb/storage"; import { extractIPFSUri } from "thirdweb/utils"; +import { getAuthTokenWalletAddress } from "../../../app/api/lib/getAuthToken"; +import { isEnsName, resolveEns } from "../../../lib/ens"; +import { fetchPublishedContractsFromDeploy } from "../fetchPublishedContractsFromDeploy"; interface PublishedByProps { contract: ThirdwebContract; } -export const PublishedBy: React.FC = ({ contract }) => { - const publishedContractsFromDeploy = - usePublishedContractsFromDeploy(contract); - - const address = useActiveAccount()?.address; - - const publishedContractToShow = useMemo(() => { - const reversedPublishedContractsFromDeploy = [ - ...(publishedContractsFromDeploy.data || []), - ].reverse(); - - return ( - reversedPublishedContractsFromDeploy.find( - (publishedContract) => publishedContract.publisher === address, - ) || - reversedPublishedContractsFromDeploy.find( - (publishedContract) => - publishedContract.publisher === THIRDWEB_DEPLOYER_ADDRESS, - ) || - reversedPublishedContractsFromDeploy[ - reversedPublishedContractsFromDeploy.length - 1 - ] || - undefined - ); - }, [publishedContractsFromDeploy.data, address]); - - const publisherEnsQuery = useEns(publishedContractToShow?.publisher); - const publisherAddress = - publisherEnsQuery.data?.ensName || publisherEnsQuery.data?.address; - - const installedModules = useReadContract(getInstalledModules, { +type ModuleMetadataPickedKeys = { + publisher: string; + moduleId: string; + name: string; + version: string; +}; + +export const PublishedBy: React.FC = async ({ contract }) => { + const client = getThirdwebClient(); + const publishedContractsFromDeploy = await fetchPublishedContractsFromDeploy({ contract, - queryOptions: { - enabled: publishedContractToShow?.routerType === "modular" && !!contract, - }, + client, }); - // this handles all the logic for modules in the published contract card - const installedModulesQuery = useQuery({ - queryKey: [ - "published-by-modules", - contract, - installedModules.data?.map((m) => m.implementation), - ], - queryFn: async () => { - if (!installedModules.data?.length) { - return []; + const address = getAuthTokenWalletAddress(); + + const reversedPublishedContractsFromDeploy = [ + ...(publishedContractsFromDeploy || []), + ].reverse(); + + const publishedContractToShow = + reversedPublishedContractsFromDeploy.find( + (publishedContract) => publishedContract.publisher === address, + ) || + reversedPublishedContractsFromDeploy.find( + (publishedContract) => + publishedContract.publisher === THIRDWEB_DEPLOYER_ADDRESS, + ) || + reversedPublishedContractsFromDeploy[ + reversedPublishedContractsFromDeploy.length - 1 + ] || + undefined; + + if (!publishedContractToShow || !publishedContractToShow.publisher) { + return null; + } + + // get publisher address/ens + let publisherAddressOrEns = publishedContractToShow.publisher; + if (!isEnsName(publishedContractToShow.publisher)) { + try { + const res = await resolveEns(publishedContractToShow.publisher); + if (res.ensName) { + publisherAddressOrEns = res.ensName; } + } catch { + // no op + } + } - const moduleContracts = installedModules.data.map((module) => { - return getContract({ ...contract, address: module.implementation }); + // Get Modules metadata for modular contract + let modules: ModuleMetadataPickedKeys[] = []; + if (publishedContractToShow?.routerType === "modular") { + try { + const installedModules = await getInstalledModules({ + contract, }); - const metadataUris = await Promise.allSettled( - moduleContracts.map(async (c) => { - const byteCode = await getBytecode(c); - const ipfsUri = extractIPFSUri(byteCode); - - if (!ipfsUri) { - throw new Error("No IPFS URI found in bytecode"); - } - let uris = await getPublishedUriFromCompilerUri({ - contract: { - chain: polygon, - client: contract.client, - address: "0xf5b896Ddb5146D5dA77efF4efBb3Eae36E300808", - }, - compilerMetadataUri: ipfsUri, - }).catch((e) => { - console.error("Error fetching published URI", e); - return []; - }); - - uris = uris.filter((uri) => uri.length > 0); - if (uris.length === 0) { - throw new Error("No published URI found"); - } - - const results = await Promise.allSettled( - uris.map(async (uri) => { - const content = await download({ - uri, - client: contract.client, - }); - return JSON.parse(await content.text()); - }), - ); - - return results - .filter((r) => r.status === "fulfilled") - .map((r) => r.value); - }), - ); - - const filtered = metadataUris - .filter((m) => m.status === "fulfilled") - .map((m) => m.value); - - return filtered.map((m) => m[0]); - }, - enabled: !!installedModules.data?.length, - }); - if (!publishedContractToShow || !publisherAddress) { - return null; + if (installedModules.length !== 0) { + const moduleContracts = installedModules.map((module) => { + return getContract({ ...contract, address: module.implementation }); + }); + + const metadataUris = await Promise.allSettled( + moduleContracts.map(async (c) => { + const byteCode = await getBytecode(c); + const ipfsUri = extractIPFSUri(byteCode); + + if (!ipfsUri) { + throw new Error("No IPFS URI found in bytecode"); + } + let uris = await getPublishedUriFromCompilerUri({ + contract: { + chain: polygon, + client: contract.client, + address: "0xf5b896Ddb5146D5dA77efF4efBb3Eae36E300808", + }, + compilerMetadataUri: ipfsUri, + }).catch((e) => { + console.error("Error fetching published URI", e); + return []; + }); + + uris = uris.filter((uri) => uri.length > 0); + if (uris.length === 0) { + throw new Error("No published URI found"); + } + + const results = await Promise.allSettled( + uris.map(async (uri) => { + const content = await download({ + uri, + client: contract.client, + }); + return JSON.parse( + await content.text(), + ) as ModuleMetadataPickedKeys; + }), + ); + + return results + .filter((r) => r.status === "fulfilled") + .map((r) => r.value); + }), + ); + + const filtered = metadataUris + .filter((m) => m.status === "fulfilled") + .map((m) => m.value); + + modules = filtered.map((m) => m[0]).filter((m) => m !== undefined); + } + } catch { + // no op + } } return ( ({ + modules={modules.map((m) => ({ publisher: m.publisher, moduleId: m.name, version: m.version, diff --git a/apps/dashboard/src/components/explore/contract-card/index.tsx b/apps/dashboard/src/components/explore/contract-card/index.tsx index 185d6534d52..e34684a7fa6 100644 --- a/apps/dashboard/src/components/explore/contract-card/index.tsx +++ b/apps/dashboard/src/components/explore/contract-card/index.tsx @@ -1,17 +1,13 @@ -"use client"; - import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Skeleton, SkeletonContainer } from "@/components/ui/skeleton"; +import { Skeleton } from "@/components/ui/skeleton"; import { TrackedLinkTW } from "@/components/ui/tracked-link"; -import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler"; import { cn } from "@/lib/utils"; -import { useQuery } from "@tanstack/react-query"; import { moduleToBase64 } from "app/(dashboard)/published-contract/utils/module-base-64"; import { RocketIcon, ShieldCheckIcon } from "lucide-react"; import Link from "next/link"; -import invariant from "tiny-invariant"; import { fetchPublishedContractVersion } from "../../contract-components/fetch-contracts-with-versions"; import { ContractPublisher, replaceDeployerAddress } from "../publisher"; @@ -80,7 +76,7 @@ function getContractUrl( return replaceDeployerAddress(pathName); } -export const ContractCard: React.FC = ({ +export async function ContractCard({ publisher, contractId, titleOverride, @@ -89,25 +85,28 @@ export const ContractCard: React.FC = ({ tracking, modules = [], isBeta, -}) => { - const client = useThirdwebClient(); - const publishedContractResult = usePublishedContract( - `${publisher}/${contractId}/${version}`, - ); +}: ContractCardProps) { + const client = getThirdwebClient(); + const publishedContractResult = await fetchPublishedContractVersion( + publisher, + contractId, + version, + ).catch(() => null); - const showSkeleton = publishedContractResult.isPending; + if (!publishedContractResult) { + return null; + } const auditLink = resolveSchemeWithErrorHandler({ - uri: publishedContractResult.data?.audit, + uri: publishedContractResult.audit, client, }); return (
= ({ )} {/* Version */} - { - return ( -

- v{v} -

- ); - }} - /> + + {publishedContractResult.version && ( +

+ v{publishedContractResult.version} +

+ )} {/* Tags */} @@ -170,34 +164,20 @@ export const ContractCard: React.FC = ({
- + {( titleOverride || - publishedContractResult.data?.displayName || - publishedContractResult.data?.name - } - render={(v) => { - return ( -

- {v.replace("[Beta]", "")} -

- ); - }} - /> + publishedContractResult.displayName || + publishedContractResult.name + ).replace("[Beta]", "")} + + + {/* Desc */} +

+ {descriptionOverride || publishedContractResult.description} +

- {publishedContractResult.data ? ( -

- {descriptionOverride || publishedContractResult.data?.description} -

- ) : ( -
- -
- -
- )} {modules.length ? (
{modules.slice(0, 2).map((m) => ( @@ -216,10 +196,9 @@ export const ContractCard: React.FC = ({ !modules?.length && "mt-auto", )} > - + {publishedContractResult.publisher && ( + + )}
); -}; - -// data fetching -type PublishedContractId = - | `${string}/${string}` - | `${string}/${string}/${string}`; +} -function usePublishedContract(publishedContractId: PublishedContractId) { - const [publisher, contractId, version] = publishedContractId.split("/"); - return useQuery({ - queryKey: ["published-contract", { publishedContractId }], - queryFn: () => { - invariant(publisher, "publisher is required"); - invariant(contractId, "contractId is required"); - return fetchPublishedContractVersion(publisher, contractId, version); - }, - enabled: !!publisher || !!contractId, - }); +export function ContractCardSkeleton() { + return ; } diff --git a/apps/dashboard/src/components/explore/contract-row/index.tsx b/apps/dashboard/src/components/explore/contract-row/index.tsx index 959e927a0ff..876248c01ff 100644 --- a/apps/dashboard/src/components/explore/contract-row/index.tsx +++ b/apps/dashboard/src/components/explore/contract-row/index.tsx @@ -1,7 +1,8 @@ import type { ExploreCategory } from "data/explore"; import { ArrowRightIcon } from "lucide-react"; import Link from "next/link"; -import { ContractCard } from "../contract-card"; +import { Suspense } from "react"; +import { ContractCard, ContractCardSkeleton } from "../contract-card"; interface ContractRowProps { category: ExploreCategory; @@ -69,26 +70,30 @@ export function ContractRow({ category }: ContractRowProps) { return null; } return ( - ({ - publisher: m.split("/")[0] || "", - moduleId: m.split("/")[1] || "", - })) - : undefined - } - /> + fallback={} + > + ({ + publisher: m.split("/")[0] || "", + moduleId: m.split("/")[1] || "", + })) + : undefined + } + /> + ); })} diff --git a/apps/dashboard/src/components/explore/publisher/index.tsx b/apps/dashboard/src/components/explore/publisher/index.tsx index 53f968d2bc5..a41fe479604 100644 --- a/apps/dashboard/src/components/explore/publisher/index.tsx +++ b/apps/dashboard/src/components/explore/publisher/index.tsx @@ -1,41 +1,40 @@ -import { SkeletonContainer } from "@/components/ui/skeleton"; -import { useEns } from "components/contract-components/hooks"; import { PublisherAvatar } from "components/contract-components/publisher/masked-avatar"; import Link from "next/link"; -import type { RequiredParam } from "utils/types"; import { shortenIfAddress } from "utils/usedapp-external"; +import { isEnsName, resolveEns } from "../../../lib/ens"; interface ContractPublisherProps { - addressOrEns: RequiredParam; - showSkeleton?: boolean; + addressOrEns: string; } -export const ContractPublisher: React.FC = ({ +export const ContractPublisher: React.FC = async ({ addressOrEns, - showSkeleton, }) => { - const ensQuery = useEns(addressOrEns || undefined); + let ensOrAddressToShow = addressOrEns; + + if (!isEnsName(addressOrEns)) { + try { + const res = await resolveEns(addressOrEns); + if (res.ensName) { + ensOrAddressToShow = res.ensName; + } + } catch { + // ignore + } + } return ( -

{treatAddress(v)}

} - /> +

{treatAddress(ensOrAddressToShow)}

); }; diff --git a/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx b/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx index 664cce03423..57e82491941 100644 --- a/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx +++ b/apps/dashboard/src/components/explore/upsells/deploy-your-own.tsx @@ -1,8 +1,6 @@ -"use client"; - +import { PlainTextCodeBlock } from "@/components/ui/code"; import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { ExternalLinkIcon } from "lucide-react"; -import { CodeBlock } from "tw-components"; export const DeployUpsellCard: React.FC = () => { return ( @@ -29,11 +27,7 @@ export const DeployUpsellCard: React.FC = () => {
- +
@@ -53,7 +47,7 @@ export const DeployUpsellCard: React.FC = () => {
- + ); }; diff --git a/apps/dashboard/src/components/explore/upsells/publish-submit.tsx b/apps/dashboard/src/components/explore/upsells/publish-submit.tsx index e245ca3eae2..73507ae6cc1 100644 --- a/apps/dashboard/src/components/explore/upsells/publish-submit.tsx +++ b/apps/dashboard/src/components/explore/upsells/publish-submit.tsx @@ -1,5 +1,3 @@ -"use client"; - import { Button } from "@/components/ui/button"; import { TrackedLinkTW } from "@/components/ui/tracked-link"; import Image from "next/image";