From 939e7ab820ec5c298460870cfe9edc0df1cf6c46 Mon Sep 17 00:00:00 2001 From: MananTank Date: Mon, 28 Oct 2024 17:36:30 +0000 Subject: [PATCH] Render contract pages on client side for localhost chain (#5185) Fixes: DASH-348 --- .../hooks/useDashboardContractMetadata.tsx | 2 +- .../ContractDirectListingsPage.client.tsx | 27 +++++++ .../(marketplace)/direct-listings/page.tsx | 6 ++ .../ContractEnglishAuctionsPage.client.tsx | 27 +++++++ .../(marketplace)/english-auctions/page.tsx | 6 ++ .../_components/page-skeletons.tsx | 17 +++++ .../redirect-contract-overview.client.tsx | 24 ++++++ .../_hooks/useContractPageMetadata.ts | 18 +++++ .../_hooks/useResolveContractABI.ts | 13 ++++ .../{ => _layout}/ConfigureCustomChain.tsx | 4 +- .../_layout}/contract-metadata.tsx | 41 ++++++++--- .../_layout/contract-page-layout.client.tsx | 49 +++++++++++++ .../_layout/contract-page-layout.tsx | 62 ++++++++++++++++ .../_layout}/metadata-header.tsx | 0 .../primary-dashboard-button.tsx | 0 .../_utils/getContractPageMetadata.ts | 9 ++- .../AccountSigners.client.tsx | 27 +++++++ .../components/account-signers.tsx | 21 ++++-- .../account-permissions/page.tsx | 18 ++--- .../account/AccountPage.client.tsx | 34 +++++++++ .../[contractAddress]/account/page.tsx | 8 ++ .../accounts/AccountsPage.client.tsx | 27 +++++++ .../[contractAddress]/accounts/page.tsx | 9 ++- .../ContractAnalyticsPage.client.tsx | 27 +++++++ .../[contractAddress]/analytics/page.tsx | 6 ++ .../ClaimConditions.client.tsx | 38 ++++++++++ .../claim-conditions/page.tsx | 8 +- .../code/contract-code-page.client.tsx | 26 +++++++ .../code/contract-code-page.tsx | 33 +++++++++ .../[contractAddress]/code/page.tsx | 38 +++++----- .../embed/EmbedSetup.client.tsx | 32 ++++++++ .../[contractAddress]/embed/page.tsx | 11 ++- .../explorer/ContractExplorerPage.client.tsx | 31 ++++++++ .../explorer/ContractExplorerPage.tsx | 25 +++++-- .../[contractAddress]/explorer/page.tsx | 31 ++++---- .../[chain_id]/[contractAddress]/layout.tsx | 59 ++++++++------- .../ContractEditModulesPage.client.tsx | 27 +++++++ .../[contractAddress]/modules/page.tsx | 11 ++- .../nfts/ContractNFTPage.client.tsx | 35 +++++++++ .../nfts/[tokenId]/TokenIdPage.client.tsx | 37 ++++++++++ .../[contractAddress]/nfts/[tokenId]/page.tsx | 18 +++-- .../[contractAddress]/nfts/page.tsx | 12 ++- .../overview/ContractOverviewPage.tsx | 10 +-- .../overview/components/published-by-ui.tsx} | 55 ++++++++------ .../components/published-by.server.tsx | 32 ++++++++ .../contract-overview-page.client.tsx | 73 +++++++++++++++++++ .../[chain_id]/[contractAddress]/page.tsx | 18 +++++ .../ContractPermissionsPage.client.tsx | 32 ++++++++ .../[contractAddress]/permissions/page.tsx | 11 +++ .../ContractProposalsPage.client.tsx | 27 +++++++ .../[contractAddress]/proposals/page.tsx | 11 ++- .../settings/ContractSettingsPage.client.tsx | 27 +++++++ .../settings/ContractSettingsPage.tsx | 31 +++++++- .../[contractAddress]/settings/page.tsx | 25 ++----- .../split/ContractSplitPage.client.tsx | 27 +++++++ .../[contractAddress]/split/page.tsx | 10 ++- .../tokens/ContractTokensPage.client.tsx | 35 +++++++++ .../[contractAddress]/tokens/page.tsx | 6 ++ 58 files changed, 1217 insertions(+), 167 deletions(-) create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/page-skeletons.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/redirect-contract-overview.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useContractPageMetadata.ts create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useResolveContractABI.ts rename apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/{ => _layout}/ConfigureCustomChain.tsx (92%) rename apps/dashboard/src/{components/custom-contract/contract-header => app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout}/contract-metadata.tsx (66%) create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx rename apps/dashboard/src/{components/custom-contract/contract-header => app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout}/metadata-header.tsx (100%) rename apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/{_components => _layout}/primary-dashboard-button.tsx (100%) create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/AccountSigners.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/EmbedSetup.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/TokenIdPage.client.tsx rename apps/dashboard/src/{components/contract-components/shared/published-by.tsx => app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by-ui.tsx} (79%) create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by.server.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/contract-overview-page.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx index f9fa400b622..6f405b7e3d1 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx @@ -18,7 +18,7 @@ export function useDashboardContractMetadata(contract: ThirdwebContract) { }); } -type DashboardContractMetadata = { +export type DashboardContractMetadata = { name: string; symbol: string; image: string; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx new file mode 100644 index 00000000000..1ce045fefd6 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../../_components/page-skeletons"; +import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../../_hooks/useContractPageMetadata"; +import { ContractDirectListingsPage } from "./ContractDirectListingsPage"; + +export function ContractDirectListingsPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isDirectListingSupported) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx index 28697de7e13..7bcb9c010aa 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../../_utils/getContractFromParams"; import { getContractPageMetadata } from "../../_utils/getContractPageMetadata"; import { ContractDirectListingsPage } from "./ContractDirectListingsPage"; +import { ContractDirectListingsPageClient } from "./ContractDirectListingsPage.client"; export default async function Page(props: { params: { @@ -15,6 +17,10 @@ export default async function Page(props: { notFound(); } + if (info.chainMetadata.chainId === localhost.id) { + return ; + } + const { isDirectListingSupported } = await getContractPageMetadata( info.contract, ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx new file mode 100644 index 00000000000..8cb68a38778 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../../_components/page-skeletons"; +import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../../_hooks/useContractPageMetadata"; +import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage"; + +export function ContractEnglishAuctionsPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isEnglishAuctionSupported) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/page.tsx index ad4204b261c..4d79e43d4a7 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../../_utils/getContractFromParams"; import { getContractPageMetadata } from "../../_utils/getContractPageMetadata"; import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage"; +import { ContractEnglishAuctionsPageClient } from "./ContractEnglishAuctionsPage.client"; export default async function Page(props: { params: { @@ -15,6 +17,10 @@ export default async function Page(props: { notFound(); } + if (info.chainMetadata.chainId === localhost.id) { + return ; + } + const { isEnglishAuctionSupported } = await getContractPageMetadata( info.contract, ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/page-skeletons.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/page-skeletons.tsx new file mode 100644 index 00000000000..3bcf23b1e8f --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/page-skeletons.tsx @@ -0,0 +1,17 @@ +import { Spinner } from "@/components/ui/Spinner/Spinner"; + +export function LoadingPage() { + return ( +
+ +
+ ); +} + +export function ErrorPage() { + return ( +
+

Failed to load contract

+
+ ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/redirect-contract-overview.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/redirect-contract-overview.client.tsx new file mode 100644 index 00000000000..a4758c55726 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/redirect-contract-overview.client.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { useDashboardRouter } from "@/lib/DashboardRouter"; +import { useEffect, useRef } from "react"; +import type { ThirdwebContract } from "thirdweb"; +import { LoadingPage } from "./page-skeletons"; + +export function RedirectToContractOverview(props: { + contract: ThirdwebContract; +}) { + const router = useDashboardRouter(); + const redirected = useRef(false); + + // eslint-disable-next-line no-restricted-syntax + useEffect(() => { + if (redirected.current) { + return; + } + redirected.current = true; + router.replace(`/${props.contract.chain.id}/${props.contract.address}`); + }, [router, props.contract]); + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useContractPageMetadata.ts b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useContractPageMetadata.ts new file mode 100644 index 00000000000..ec3c0ff53c3 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useContractPageMetadata.ts @@ -0,0 +1,18 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import type { ThirdwebContract } from "thirdweb"; +import { getContractPageMetadataSetup } from "../_utils/getContractPageMetadata"; + +export function useContractPageMetadata(contract: ThirdwebContract) { + return useQuery({ + queryKey: ["getContractPageMetadataSetup", contract], + queryFn: () => { + return getContractPageMetadataSetup(contract, () => + Promise.resolve(false), + ); + }, + retry: false, + refetchOnWindowFocus: false, + }); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useResolveContractABI.ts b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useResolveContractABI.ts new file mode 100644 index 00000000000..c202aab8590 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_hooks/useResolveContractABI.ts @@ -0,0 +1,13 @@ +import { useQuery } from "@tanstack/react-query"; +import { type ThirdwebContract, resolveContractAbi } from "thirdweb/contract"; + +export function useResolveContractABI(contract: ThirdwebContract) { + return useQuery({ + queryKey: ["resolveContractAbi", contract], + queryFn: () => { + return resolveContractAbi(contract); + }, + retry: false, + refetchOnWindowFocus: false, + }); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/ConfigureCustomChain.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/ConfigureCustomChain.tsx similarity index 92% rename from apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/ConfigureCustomChain.tsx rename to apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/ConfigureCustomChain.tsx index 7a994076b18..d8b35654eba 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/ConfigureCustomChain.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/ConfigureCustomChain.tsx @@ -4,8 +4,8 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { CheckIcon, CircleAlertIcon, RotateCcwIcon } from "lucide-react"; import { useState } from "react"; -import { ConfigureNetworks } from "../../../../../components/configure-networks/ConfigureNetworks"; -import { addChainOverrides } from "../../../../../stores/chainStores"; +import { addChainOverrides } from "stores/chainStores"; +import { ConfigureNetworks } from "../../../../../../components/configure-networks/ConfigureNetworks"; export function ConfigureCustomChain(props: { chainSlug: string; diff --git a/apps/dashboard/src/components/custom-contract/contract-header/contract-metadata.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-metadata.tsx similarity index 66% rename from apps/dashboard/src/components/custom-contract/contract-header/contract-metadata.tsx rename to apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-metadata.tsx index 5004883be63..316bb1a6e22 100644 --- a/apps/dashboard/src/components/custom-contract/contract-header/contract-metadata.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-metadata.tsx @@ -1,5 +1,8 @@ import { getThirdwebClient } from "@/constants/thirdweb.server"; -import { fetchDashboardContractMetadata } from "@3rdweb-sdk/react/hooks/useDashboardContractMetadata"; +import { + type DashboardContractMetadata, + fetchDashboardContractMetadata, +} from "@3rdweb-sdk/react/hooks/useDashboardContractMetadata"; import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy"; import type { ThirdwebContract } from "thirdweb"; import type { ChainMetadata } from "thirdweb/chains"; @@ -8,12 +11,34 @@ import { MetadataHeader } from "./metadata-header"; interface ContractMetadataProps { contract: ThirdwebContract; chain: ChainMetadata; + contractMetadata: DashboardContractMetadata | undefined; + externalLinks: + | { + name: string; + url: string; + }[] + | undefined; } -export async function ContractMetadata({ +export function ContractMetadata({ contract, chain, + contractMetadata, + externalLinks, }: ContractMetadataProps) { + return ( + + ); +} + +export async function getContractMetadataHeaderData( + contract: ThirdwebContract, +) { const settledResults = await Promise.allSettled([ fetchDashboardContractMetadata(contract), fetchPublishedContractsFromDeploy({ @@ -34,12 +59,8 @@ export async function ContractMetadata({ const latestPublished = publishedContractsFromDeploy?.slice(-1)[0]; - return ( - - ); + return { + contractMetadata, + externalLinks: latestPublished?.externalLinks, + }; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.client.tsx new file mode 100644 index 00000000000..23f5d0226b7 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.client.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import type { ThirdwebContract } from "thirdweb"; +import type { ChainMetadata } from "thirdweb/chains"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { getContractPageSidebarLinks } from "../_utils/getContractPageSidebarLinks"; +import { getContractMetadataHeaderData } from "./contract-metadata"; +import { ContractPageLayout } from "./contract-page-layout"; + +export function ContractPageLayoutClient(props: { + chainMetadata: ChainMetadata; + contract: ThirdwebContract; + children: React.ReactNode; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + const headerMetadataQuery = useQuery({ + queryKey: ["getContractMetadataHeaderData", props.contract], + queryFn: async () => { + return await getContractMetadataHeaderData(props.contract); + }, + retry: false, + refetchOnWindowFocus: false, + }); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + const sidebarLinks = getContractPageSidebarLinks({ + chainSlug: props.chainMetadata.slug, + contractAddress: props.contract.address, + metadata: metadataQuery.data, + }); + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx new file mode 100644 index 00000000000..10565dcf70c --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx @@ -0,0 +1,62 @@ +import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; +import type { SidebarLink } from "@/components/blocks/Sidebar"; +import { SidebarLayout } from "@/components/blocks/SidebarLayout"; +import type { DashboardContractMetadata } from "@3rdweb-sdk/react/hooks/useDashboardContractMetadata"; +import { DeprecatedAlert } from "components/shared/DeprecatedAlert"; +import type { ThirdwebContract } from "thirdweb"; +import type { ChainMetadata } from "thirdweb/chains"; +import { ContractMetadata } from "./contract-metadata"; +import { PrimaryDashboardButton } from "./primary-dashboard-button"; + +export function ContractPageLayout(props: { + chainMetadata: ChainMetadata; + contract: ThirdwebContract; + children: React.ReactNode; + sidebarLinks: SidebarLink[]; + dashboardContractMetadata: DashboardContractMetadata | undefined; + externalLinks: + | { + name: string; + url: string; + }[] + | undefined; +}) { + const { + chainMetadata, + contract, + sidebarLinks, + dashboardContractMetadata, + externalLinks, + } = props; + + return ( + + +
+
+
+ + +
+ +
+
+
+
{props.children}
+ + + ); +} diff --git a/apps/dashboard/src/components/custom-contract/contract-header/metadata-header.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx similarity index 100% rename from apps/dashboard/src/components/custom-contract/contract-header/metadata-header.tsx rename to apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/metadata-header.tsx diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/primary-dashboard-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx similarity index 100% rename from apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/primary-dashboard-button.tsx rename to apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/primary-dashboard-button.tsx diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageMetadata.ts b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageMetadata.ts index c9372bfe673..bc3e3d660a8 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageMetadata.ts +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageMetadata.ts @@ -46,8 +46,13 @@ export type ContractPageMetadata = { functionSelectors: string[]; }; -export async function getContractPageMetadata( +export async function getContractPageMetadata(contract: ThirdwebContract) { + return getContractPageMetadataSetup(contract, isAnalyticsSupportedForChain); +} + +export async function getContractPageMetadataSetup( contract: ThirdwebContract, + isAnalyticsSupportedFn: (chainId: number) => Promise, ): Promise { const [ functionSelectorsResult, @@ -55,7 +60,7 @@ export async function getContractPageMetadata( contractTypeResult, ] = await Promise.allSettled([ resolveFunctionSelectors(contract), - isAnalyticsSupportedForChain(contract.chain.id), + isAnalyticsSupportedFn(contract.chain.id), getContractType({ contract }), ]); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/AccountSigners.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/AccountSigners.client.tsx new file mode 100644 index 00000000000..724618b8520 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/AccountSigners.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { AccountSigners } from "./components/account-signers"; + +export function AccountSignersClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isAccountPermissionsSupported) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/components/account-signers.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/components/account-signers.tsx index 20abe6fa933..7a399afe917 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/components/account-signers.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/components/account-signers.tsx @@ -31,14 +31,19 @@ export const AccountSigners: React.FC = ({ contract }) => { ); const data = transformedAdmins.concat(transformedSigners || []); return ( -
- {data.map((item) => ( - - ))} +
+

+ Account Signers +

+
+ {data.map((item) => ( + + ))} +
); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/page.tsx index c10fe11f36f..65f71d8aa36 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account-permissions/page.tsx @@ -1,6 +1,8 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; +import { AccountSignersClient } from "./AccountSigners.client"; import { AccountSigners } from "./components/account-signers"; export default async function Page(props: { @@ -15,7 +17,12 @@ export default async function Page(props: { notFound(); } - const { contract } = info; + const { contract, chainMetadata } = info; + + if (chainMetadata.chainId === localhost.id) { + return ; + } + const { isAccountPermissionsSupported } = await getContractPageMetadata(contract); @@ -23,12 +30,5 @@ export default async function Page(props: { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - return ( -
-

- Account Signers -

- -
- ); + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.client.tsx new file mode 100644 index 00000000000..dffc95f9c04 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.client.tsx @@ -0,0 +1,34 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import type { ChainMetadata } from "thirdweb/chains"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { AccountPage } from "./AccountPage"; + +export function AccountPageClient(props: { + contract: ThirdwebContract; + chainMetadata: ChainMetadata; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isAccount) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/page.tsx index a465679cb47..ae2b201cf20 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { AccountPage } from "./AccountPage"; +import { AccountPageClient } from "./AccountPage.client"; export default async function Page(props: { params: { @@ -16,6 +18,12 @@ export default async function Page(props: { } const { contract, chainMetadata } = info; + if (contract.chain.id === localhost.id) { + return ( + + ); + } + const { isAccount } = await getContractPageMetadata(contract); if (!isAccount) { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx new file mode 100644 index 00000000000..005b3f1d561 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { AccountsPage } from "./AccountsPage"; + +export function AccountsPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isAccountFactory) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx index 82d86959a13..998a7899413 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { AccountsPage } from "./AccountsPage"; +import { AccountsPageClient } from "./AccountsPage.client"; export default async function Page(props: { params: { @@ -15,7 +17,12 @@ export default async function Page(props: { notFound(); } - const { contract } = info; + const { contract, chainMetadata } = info; + + if (chainMetadata.chainId === localhost.id) { + return ; + } + const { isAccountFactory } = await getContractPageMetadata(contract); if (!isAccountFactory) { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.client.tsx new file mode 100644 index 00000000000..ed8010bd465 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractAnalyticsPage } from "./ContractAnalyticsPage"; + +export function ContractAnalyticsPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isAnalyticsSupported) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/page.tsx index d259d059f7d..85904d7360a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractAnalyticsPage } from "./ContractAnalyticsPage"; +import { ContractAnalyticsPageClient } from "./ContractAnalyticsPage.client"; export default async function Page(props: { params: { @@ -15,6 +17,10 @@ export default async function Page(props: { notFound(); } + if (info.chainMetadata.chainId === localhost.id) { + return ; + } + const { isAnalyticsSupported } = await getContractPageMetadata(info.contract); if (!isAnalyticsSupported) { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx new file mode 100644 index 00000000000..4c084f8759d --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx @@ -0,0 +1,38 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ClaimConditions } from "../_components/claim-conditions/claim-conditions"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; + +export function ClaimConditionsClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + const { + isERC20ClaimConditionsSupported, + isERC721ClaimConditionsSupported, + supportedERCs, + } = metadataQuery.data; + + if (!isERC20ClaimConditionsSupported && !isERC721ClaimConditionsSupported) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx index 12af1ceef38..f8193c0f13a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { ClaimConditions } from "../_components/claim-conditions/claim-conditions"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; +import { ClaimConditionsClient } from "./ClaimConditions.client"; export default async function Page(props: { params: { @@ -14,8 +16,12 @@ export default async function Page(props: { if (!info) { notFound(); } + const { contract, chainMetadata } = info; + + if (chainMetadata.chainId === localhost.id) { + return ; + } - const { contract } = info; const { isERC20ClaimConditionsSupported, isERC721ClaimConditionsSupported, diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.client.tsx new file mode 100644 index 00000000000..b0829bb0b15 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.client.tsx @@ -0,0 +1,26 @@ +"use client"; + +import type { ChainMetadata } from "thirdweb/chains"; +import type { ThirdwebContract } from "thirdweb/contract"; +import { LoadingPage } from "../_components/page-skeletons"; +import { useResolveContractABI } from "../_hooks/useResolveContractABI"; +import { ContractCodePage } from "./contract-code-page"; + +export function ContractCodePageClient(props: { + contract: ThirdwebContract; + chainMetadata: ChainMetadata; +}) { + const abiQuery = useResolveContractABI(props.contract); + + if (abiQuery.isPending) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.tsx new file mode 100644 index 00000000000..21306a29473 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/contract-code-page.tsx @@ -0,0 +1,33 @@ +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import type { Abi } from "abitype"; +import { CircleAlertIcon } from "lucide-react"; +import type { ChainMetadata } from "thirdweb/chains"; +import type { ThirdwebContract } from "thirdweb/contract"; +import { CodeOverview } from "../../../../../../contract-ui/tabs/code/components/code-overview"; + +export function ContractCodePage(props: { + abi: Abi | undefined; + contract: ThirdwebContract; + chainMetadata: ChainMetadata; +}) { + if (props.abi) { + return ( + + ); + } + + return ( + + + Failed to resolve contract ABI + + Please verify that contract address is correct and deployed on " + {props.chainMetadata.name}" chain. + + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/page.tsx index 3fcd4246acf..4b3bda1cc51 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/code/page.tsx @@ -1,9 +1,9 @@ -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { CircleAlertIcon } from "lucide-react"; import { notFound } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { resolveContractAbi } from "thirdweb/contract"; -import { CodeOverview } from "../../../../../../contract-ui/tabs/code/components/code-overview"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; +import { ContractCodePage } from "./contract-code-page"; +import { ContractCodePageClient } from "./contract-code-page.client"; export default async function Page(props: { params: { @@ -19,26 +19,22 @@ export default async function Page(props: { const { contract, chainMetadata } = info; - try { - const abi = await resolveContractAbi(contract); - + if (contract.chain.id === localhost.id) { return ( - ); - } catch { - return ( - - - Failed to resolve contract ABI - - Please verify that contract address is correct and deployed on " - {chainMetadata.name}" chain. - - - ); } + + const abi = await resolveContractAbi(contract).then(() => undefined); + + return ( + + ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/EmbedSetup.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/EmbedSetup.client.tsx new file mode 100644 index 00000000000..52794cbe07d --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/EmbedSetup.client.tsx @@ -0,0 +1,32 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { EmbedSetup } from "./embed-setup"; + +export function EmbedSetupClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (metadataQuery.data.embedType === null) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/page.tsx index ffbe9fa5e9d..13bcbf22b20 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/page.tsx @@ -1,6 +1,8 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; +import { EmbedSetupClient } from "./EmbedSetup.client"; import { EmbedSetup } from "./embed-setup"; export default async function Page(props: { @@ -15,11 +17,16 @@ export default async function Page(props: { notFound(); } - const { embedType } = await getContractPageMetadata(info.contract); + const { contract } = info; + if (contract.chain.id === localhost.id) { + return ; + } + + const { embedType } = await getContractPageMetadata(contract); if (embedType === null) { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx new file mode 100644 index 00000000000..2c6285b3766 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx @@ -0,0 +1,31 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import type { ChainMetadata } from "thirdweb/chains"; +import { LoadingPage } from "../_components/page-skeletons"; +import { useResolveContractABI } from "../_hooks/useResolveContractABI"; +import { ContractExplorerPage } from "./ContractExplorerPage"; + +interface ContractExplorePageProps { + contract: ThirdwebContract; + chainMetadata: ChainMetadata; +} + +export const ContractExplorerPageClient: React.FC = ({ + contract, + chainMetadata, +}) => { + const abiQuery = useResolveContractABI(contract); + + if (abiQuery.isPending) { + return ; + } + + return ( + + ); +}; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx index 57a0ba58800..2ec7f4538a6 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx @@ -1,21 +1,36 @@ -"use client"; - +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Flex } from "@chakra-ui/react"; import type { Abi } from "abitype"; import { getContractFunctionsFromAbi } from "components/contract-components/getContractFunctionsFromAbi"; import { ContractFunctionsOverview } from "components/contract-functions/contract-functions"; +import { CircleAlertIcon } from "lucide-react"; import type { ThirdwebContract } from "thirdweb"; - -// TODO - figure out why we have to add "use client" here and it does not work without it +import type { ChainMetadata } from "thirdweb/chains"; interface ContractExplorePageProps { contract: ThirdwebContract; - abi: Abi; + abi: Abi | undefined; + chainMetadata: ChainMetadata; } + export const ContractExplorerPage: React.FC = ({ contract, abi, + chainMetadata, }) => { + if (!abi) { + return ( + + + Failed to resolve contract ABI + + Please verify that contract address is correct and deployed on " + {chainMetadata.name}" chain. + + + ); + } + const functions = getContractFunctionsFromAbi(abi); return ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx index 940ec0a77d0..e6bdbe9aeda 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx @@ -1,9 +1,9 @@ -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { CircleAlertIcon } from "lucide-react"; import { notFound } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { resolveContractAbi } from "thirdweb/contract"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { ContractExplorerPage } from "./ContractExplorerPage"; +import { ContractExplorerPageClient } from "./ContractExplorerPage.client"; export default async function Page(props: { params: { @@ -19,19 +19,22 @@ export default async function Page(props: { const { contract, chainMetadata } = info; - try { - const abi = await resolveContractAbi(contract); - return ; - } catch { + if (contract.chain.id === localhost.id) { return ( - - - Failed to resolve contract ABI - - Please verify that contract address is correct and deployed on " - {chainMetadata.name}" chain. - - + ); } + + const abi = await resolveContractAbi(contract).catch(() => undefined); + + return ( + + ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx index 737f0b79c05..f0e14bef54d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx @@ -1,15 +1,14 @@ -import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; -import { SidebarLayout } from "@/components/blocks/SidebarLayout"; -import { ContractMetadata } from "components/custom-contract/contract-header/contract-metadata"; -import { DeprecatedAlert } from "components/shared/DeprecatedAlert"; import type { Metadata } from "next"; import { notFound } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractMetadata } from "thirdweb/extensions/common"; import { isAddress, isContractDeployed } from "thirdweb/utils"; import { resolveFunctionSelectors } from "../../../../../lib/selectors"; import { shortenIfAddress } from "../../../../../utils/usedapp-external"; -import { ConfigureCustomChain } from "./ConfigureCustomChain"; -import { PrimaryDashboardButton } from "./_components/primary-dashboard-button"; +import { ConfigureCustomChain } from "./_layout/ConfigureCustomChain"; +import { getContractMetadataHeaderData } from "./_layout/contract-metadata"; +import { ContractPageLayout } from "./_layout/contract-page-layout"; +import { ContractPageLayoutClient } from "./_layout/contract-page-layout.client"; import { supportedERCs } from "./_utils/detectedFeatures/supportedERCs"; import { getContractPageParamsInfo } from "./_utils/getContractFromParams"; import { getContractPageMetadata } from "./_utils/getContractPageMetadata"; @@ -38,44 +37,44 @@ export default async function Layout(props: { notFound(); } + if (contract.chain.id === localhost.id) { + return ( + + {props.children} + + ); + } + // check if the contract exists const isValidContract = await isContractDeployed(contract).catch(() => false); if (!isValidContract) { // TODO - replace 404 with a better page to upsale deploy or other thirdweb products notFound(); } - const contractPageMetadata = await getContractPageMetadata(contract); + const sidebarLinks = getContractPageSidebarLinks({ chainSlug: chainMetadata.slug, contractAddress: contract.address, metadata: contractPageMetadata, }); + const { contractMetadata, externalLinks } = + await getContractMetadataHeaderData(contract); + return ( - - -
-
-
- - -
- -
-
-
-
{props.children}
- - + + {props.children} + ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.client.tsx new file mode 100644 index 00000000000..3b0c7991e02 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractEditModulesPage } from "./ContractEditModulesPage"; + +export function ContractEditModulesPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isModularCore) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx index bd81c0cc96c..1c8be74a98c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractEditModulesPage } from "./ContractEditModulesPage"; +import { ContractEditModulesPageClient } from "./ContractEditModulesPage.client"; export default async function Page(props: { params: { @@ -15,11 +17,16 @@ export default async function Page(props: { notFound(); } - const { isModularCore } = await getContractPageMetadata(info.contract); + const { contract } = info; + if (contract.chain.id === localhost.id) { + return ; + } + + const { isModularCore } = await getContractPageMetadata(contract); if (!isModularCore) { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx new file mode 100644 index 00000000000..54b6a92541c --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx @@ -0,0 +1,35 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractNFTPage } from "./ContractNFTPage"; + +export function ContractNFTPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + const { supportedERCs, functionSelectors } = metadataQuery.data; + + if (!supportedERCs.isERC721 && !supportedERCs.isERC1155) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/TokenIdPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/TokenIdPage.client.tsx new file mode 100644 index 00000000000..2f7ad12695a --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/TokenIdPage.client.tsx @@ -0,0 +1,37 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../../_components/page-skeletons"; +import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../../_hooks/useContractPageMetadata"; +import { TokenIdPage } from "./token-id"; + +export function TokenIdPageClient(props: { + contract: ThirdwebContract; + tokenId: string; +}) { + const { contract } = props; + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + const { supportedERCs } = metadataQuery.data; + + if (!supportedERCs.isERC721 && !supportedERCs.isERC1155) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/page.tsx index d41e4239b56..a490a5d3f0c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/page.tsx @@ -1,6 +1,8 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../../_utils/getContractFromParams"; import { getContractPageMetadata } from "../../_utils/getContractPageMetadata"; +import { TokenIdPageClient } from "./TokenIdPage.client"; import { TokenIdPage } from "./token-id"; export default async function Page(props: { @@ -16,18 +18,24 @@ export default async function Page(props: { notFound(); } + if (!isOnlyNumbers(props.params.tokenId)) { + // redirect to nfts index page + redirect(`/${props.params.chain_id}/${props.params.contractAddress}/nfts`); + } + const { contract } = info; + if (contract.chain.id === localhost.id) { + return ( + + ); + } + const { supportedERCs } = await getContractPageMetadata(contract); if (!supportedERCs.isERC721 && !supportedERCs.isERC1155) { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - if (!isOnlyNumbers(props.params.tokenId)) { - // redirect to nfts index page - redirect(`/${props.params.chain_id}/${props.params.contractAddress}/nfts`); - } - return ( ; + } + + const { supportedERCs, functionSelectors } = + await getContractPageMetadata(contract); if (!supportedERCs.isERC721 && !supportedERCs.isERC1155) { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - const functionSelectors = await resolveFunctionSelectors(contract); - return ( = ({ chainSlug, isAnalyticsSupported, functionSelectors, + publishedBy, }) => { return (
@@ -99,11 +99,7 @@ export const ContractOverviewPage: React.FC = ({ contractAddress={contract.address} />
-
- - - -
+
{publishedBy}
); }; diff --git a/apps/dashboard/src/components/contract-components/shared/published-by.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by-ui.tsx similarity index 79% rename from apps/dashboard/src/components/contract-components/shared/published-by.tsx rename to apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by-ui.tsx index 50802e0fb01..29a42696e17 100644 --- a/apps/dashboard/src/components/contract-components/shared/published-by.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by-ui.tsx @@ -1,20 +1,14 @@ -import { getThirdwebClient } from "@/constants/thirdweb.server"; import { ContractCard } from "components/explore/contract-card"; import { THIRDWEB_DEPLOYER_ADDRESS } from "constants/addresses"; -import type { ThirdwebContract } from "thirdweb"; +import type { ThirdwebClient, 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 { 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; -} +import { fetchPublishedContractsFromDeploy } from "../../../../../../../components/contract-components/fetchPublishedContractsFromDeploy"; +import { isEnsName, resolveEns } from "../../../../../../../lib/ens"; type ModuleMetadataPickedKeys = { publisher: string; @@ -23,15 +17,18 @@ type ModuleMetadataPickedKeys = { version: string; }; -export const PublishedBy: React.FC = async ({ contract }) => { - const client = getThirdwebClient(); +export async function getPublishedByCardProps(params: { + address: string | null; + contract: ThirdwebContract; + client: ThirdwebClient; +}) { + const { address, contract, client } = params; + const publishedContractsFromDeploy = await fetchPublishedContractsFromDeploy({ contract, client, }); - const address = getAuthTokenWalletAddress(); - const reversedPublishedContractsFromDeploy = [ ...(publishedContractsFromDeploy || []), ].reverse(); @@ -133,19 +130,35 @@ export const PublishedBy: React.FC = async ({ contract }) => { } } + return { + modules, + name: publishedContractToShow.name, + publisher: publisherAddressOrEns, + version: publishedContractToShow.version, + isBeta: (publishedContractToShow.displayName || "") + .toLowerCase() + .includes("beta"), + }; +} + +export function PublishedByUI(props: { + modules: ModuleMetadataPickedKeys[]; + name: string; + publisher: string; + version: string | undefined; + isBeta: boolean; +}) { return ( ({ + contractId={props.name} + publisher={props.publisher} + version={props.version} + isBeta={props.isBeta} + modules={props.modules.map((m) => ({ publisher: m.publisher, moduleId: m.name, version: m.version, }))} /> ); -}; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by.server.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by.server.tsx new file mode 100644 index 00000000000..b5f4bd2da62 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/published-by.server.tsx @@ -0,0 +1,32 @@ +import { getThirdwebClient } from "@/constants/thirdweb.server"; +import type { ThirdwebContract } from "thirdweb"; +import { getAuthTokenWalletAddress } from "../../../../../../api/lib/getAuthToken"; +import { PublishedByUI, getPublishedByCardProps } from "./published-by-ui"; + +interface PublishedByProps { + contract: ThirdwebContract; +} + +export const PublishedBy: React.FC = async ({ contract }) => { + const client = getThirdwebClient(); + const address = getAuthTokenWalletAddress(); + const props = await getPublishedByCardProps({ + address, + contract, + client, + }); + + if (!props) { + return null; + } + + return ( + + ); +}; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/contract-overview-page.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/contract-overview-page.client.tsx new file mode 100644 index 00000000000..9ed5a8bf3f9 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/contract-overview-page.client.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { useQuery } from "@tanstack/react-query"; +import type { ThirdwebContract } from "thirdweb"; +import type { ChainMetadata } from "thirdweb/chains"; +import { useActiveAccount } from "thirdweb/react"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractOverviewPage } from "./ContractOverviewPage"; +import { + PublishedByUI, + getPublishedByCardProps, +} from "./components/published-by-ui"; + +export function ContractOverviewPageClient(props: { + contract: ThirdwebContract; + chainMetadata: ChainMetadata; +}) { + const { contract, chainMetadata } = props; + const metadataQuery = useContractPageMetadata(contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + const contractPageMetadata = metadataQuery.data; + + return ( + } + /> + ); +} + +function PublishedByClient(props: { + contract: ThirdwebContract; +}) { + const client = useThirdwebClient(); + const address = useActiveAccount()?.address; + const propsQuery = useQuery({ + queryKey: ["getPublishedByCardProps", props], + queryFn: () => + getPublishedByCardProps({ + address: address || null, + client: client, + contract: props.contract, + }), + }); + + if (!propsQuery.data) { + return null; + } + + return ; +} 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 51846f31621..d54145ee3f6 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 @@ -1,7 +1,11 @@ import { notFound } from "next/navigation"; +import { ErrorBoundary } from "react-error-boundary"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "./_utils/getContractFromParams"; import { getContractPageMetadata } from "./_utils/getContractPageMetadata"; import { ContractOverviewPage } from "./overview/ContractOverviewPage"; +import { PublishedBy } from "./overview/components/published-by.server"; +import { ContractOverviewPageClient } from "./overview/contract-overview-page.client"; export default async function Page(props: { params: { @@ -16,6 +20,15 @@ export default async function Page(props: { } const { contract, chainMetadata } = info; + if (contract.chain.id === localhost.id) { + return ( + + ); + } + const contractPageMetadata = await getContractPageMetadata(contract); return ( @@ -32,6 +45,11 @@ export default async function Page(props: { chainSlug={chainMetadata.slug} isAnalyticsSupported={contractPageMetadata.isAnalyticsSupported} functionSelectors={contractPageMetadata.functionSelectors} + publishedBy={ + + + + } /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx new file mode 100644 index 00000000000..aba31579ff6 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx @@ -0,0 +1,32 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import type { ChainMetadata } from "thirdweb/chains"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractPermissionsPage } from "./ContractPermissionsPage"; + +export function ContractPermissionsPageClient(props: { + contract: ThirdwebContract; + chainMetadata: ChainMetadata; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/page.tsx index 9f99ad31c26..216157b1a7a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/page.tsx @@ -1,7 +1,9 @@ import { notFound } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractPermissionsPage } from "./ContractPermissionsPage"; +import { ContractPermissionsPageClient } from "./ContractPermissionsPage.client"; export default async function Page(props: { params: { @@ -16,6 +18,15 @@ export default async function Page(props: { } const { contract } = info; + if (contract.chain.id === localhost.id) { + return ( + + ); + } + const { isPermissionsEnumerableSupported } = await getContractPageMetadata(contract); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx new file mode 100644 index 00000000000..40e5459e6cc --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractProposalsPage } from "./ContractProposalsPage"; + +export function ContractProposalsPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isVoteContract) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/page.tsx index 7a895f20a84..146672d0bde 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractProposalsPage } from "./ContractProposalsPage"; +import { ContractProposalsPageClient } from "./ContractProposalsPage.client"; export default async function Page(props: { params: { @@ -15,11 +17,16 @@ export default async function Page(props: { notFound(); } - const { isVoteContract } = await getContractPageMetadata(info.contract); + const { contract } = info; + if (contract.chain.id === localhost.id) { + return ; + } + + const { isVoteContract } = await getContractPageMetadata(contract); if (!isVoteContract) { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx new file mode 100644 index 00000000000..08bfe31df62 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractSettingsPage } from "./ContractSettingsPage"; + +export function ContractSettingsPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx index 21933560a20..fb96bda8ac4 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx @@ -1,6 +1,7 @@ "use client"; import { Flex, GridItem, SimpleGrid } from "@chakra-ui/react"; import type { ThirdwebContract } from "thirdweb"; +import * as CommonExt from "thirdweb/extensions/common"; import { SettingsMetadata } from "./components/metadata"; import { SettingsPlatformFees } from "./components/platform-fees"; import { SettingsPrimarySale } from "./components/primary-sale"; @@ -14,7 +15,7 @@ interface ContractSettingsPageProps { isPlatformFeesSupported: boolean; } -export const ContractSettingsPage: React.FC = ({ +const ContractSettingsPageInner: React.FC = ({ contract, isContractMetadataSupported, isPrimarySaleSupported, @@ -66,3 +67,31 @@ export const ContractSettingsPage: React.FC = ({
); }; + +export function ContractSettingsPage(props: { + contract: ThirdwebContract; + functionSelectors: string[]; +}) { + const { functionSelectors, contract } = props; + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx index 19efe679e02..a93de6dd66a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx @@ -1,8 +1,9 @@ import { notFound } from "next/navigation"; -import * as CommonExt from "thirdweb/extensions/common"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractSettingsPage } from "./ContractSettingsPage"; +import { ContractSettingsPageClient } from "./ContractSettingsPage.client"; export default async function Page(props: { params: { @@ -16,27 +17,17 @@ export default async function Page(props: { notFound(); } + const { contract } = info; + if (contract.chain.id === localhost.id) { + return ; + } + const { functionSelectors } = await getContractPageMetadata(info.contract); return ( ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx new file mode 100644 index 00000000000..9ab1c461388 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractSplitPage } from "./ContractSplitPage"; + +export function ContractSplitPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + if (!metadataQuery.data.isSplitSupported) { + return ; + } + + return ; +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/page.tsx index 95e68990b0d..19f652182ca 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/page.tsx @@ -1,7 +1,9 @@ import { notFound, redirect } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractSplitPage } from "./ContractSplitPage"; +import { ContractSplitPageClient } from "./ContractSplitPage.client"; export default async function Page(props: { params: { @@ -14,12 +16,16 @@ export default async function Page(props: { if (!info) { notFound(); } + const { contract } = info; + if (contract.chain.id === localhost.id) { + return ; + } - const { isSplitSupported } = await getContractPageMetadata(info.contract); + const { isSplitSupported } = await getContractPageMetadata(contract); if (!isSplitSupported) { redirect(`/${props.params.chain_id}/${props.params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx new file mode 100644 index 00000000000..8445d4a79d4 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx @@ -0,0 +1,35 @@ +"use client"; + +import type { ThirdwebContract } from "thirdweb"; +import { + isClaimToSupported, + isMintToSupported, +} from "thirdweb/extensions/erc20"; +import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; +import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; +import { ContractTokensPage } from "./ContractTokensPage"; + +export function ContractTokensPageClient(props: { + contract: ThirdwebContract; +}) { + const metadataQuery = useContractPageMetadata(props.contract); + + if (metadataQuery.isPending) { + return ; + } + + if (metadataQuery.isError) { + return ; + } + + const { supportedERCs, functionSelectors } = metadataQuery.data; + + return ( + + ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/page.tsx index 18216a4bb80..d6c861d5fcb 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/page.tsx @@ -1,4 +1,5 @@ import { notFound } from "next/navigation"; +import { localhost } from "thirdweb/chains"; import { isClaimToSupported, isMintToSupported, @@ -6,6 +7,7 @@ import { import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { ContractTokensPage } from "./ContractTokensPage"; +import { ContractTokensPageClient } from "./ContractTokensPage.client"; export default async function Page(props: { params: { @@ -19,6 +21,10 @@ export default async function Page(props: { notFound(); } + if (info.contract.chain.id === localhost.id) { + return ; + } + const { supportedERCs, functionSelectors } = await getContractPageMetadata( info.contract, );