Skip to content

Commit bcf1624

Browse files
committed
Move Deployed Contracts Page to App Router
1 parent 926d823 commit bcf1624

File tree

14 files changed

+263
-147
lines changed

14 files changed

+263
-147
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { redirect } from "next/navigation";
2+
import { getAuthTokenWalletAddress } from "../../../../api/lib/getAuthToken";
3+
import { DeployedContractsPage } from "../../../../team/[team_slug]/[project_slug]/contracts/_components/DeployedContractsPage";
4+
5+
export default function Page() {
6+
const accountAddress = getAuthTokenWalletAddress();
7+
if (!accountAddress) {
8+
return redirect(`/login?next=${encodeURIComponent("/dashboard")}`);
9+
}
10+
11+
return (
12+
<DeployedContractsPage
13+
address={accountAddress}
14+
className="flex grow flex-col"
15+
/>
16+
);
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { BillingAlerts } from "../../../../components/settings/Account/Billing/alerts/Alert";
2+
import { ContractsSidebarLayout } from "../../../../core-ui/sidebar/contracts";
3+
4+
export default function Layout(props: {
5+
children: React.ReactNode;
6+
}) {
7+
return (
8+
<ContractsSidebarLayout>
9+
<BillingAlerts className="pb-6" />
10+
{props.children}
11+
</ContractsSidebarLayout>
12+
);
13+
}

apps/dashboard/src/app/(dashboard)/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default function DashboardLayout(props: { children: React.ReactNode }) {
77
<ErrorProvider>
88
<div className="flex min-h-screen flex-col bg-background">
99
<DashboardHeader />
10-
<main className="grow">{props.children}</main>
10+
<div className="flex grow flex-col">{props.children}</div>
1111
<AppFooter />
1212
</div>
1313
</ErrorProvider>

apps/dashboard/src/app/api/lib/getAuthToken.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,19 @@ export function getAuthToken() {
1010

1111
return token;
1212
}
13+
14+
export function getAuthTokenWalletAddress() {
15+
const cookiesManager = cookies();
16+
const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
17+
if (!activeAccount) {
18+
return null;
19+
}
20+
21+
const token = cookiesManager.get(COOKIE_PREFIX_TOKEN + activeAccount)?.value;
22+
23+
if (token) {
24+
return activeAccount;
25+
}
26+
27+
return null;
28+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"use client";
2+
3+
import { DownloadIcon, PlusIcon } from "lucide-react";
4+
import Link from "next/link";
5+
import { useState } from "react";
6+
import { Button } from "../../../../../@/components/ui/button";
7+
import { ImportModal } from "../../../../../components/contract-components/import-contract/modal";
8+
9+
export function DeployedContractsPageHeader() {
10+
const [importModalOpen, setImportModalOpen] = useState(false);
11+
12+
return (
13+
<div>
14+
<ImportModal
15+
isOpen={importModalOpen}
16+
onClose={() => {
17+
setImportModalOpen(false);
18+
}}
19+
/>
20+
<div className="flex flex-col gap-4 md:pb-4 lg:flex-row lg:justify-between">
21+
<div>
22+
<h1 className="mb-1.5 font-semibold text-3xl tracking-tight lg:text-4xl">
23+
Your contracts
24+
</h1>
25+
<p className="text-muted-foreground text-sm">
26+
The list of contract instances that you have deployed or imported
27+
with thirdweb across all networks
28+
</p>
29+
</div>
30+
<div className="flex gap-2 [&>*]:grow">
31+
<Button
32+
className="gap-2"
33+
variant="outline"
34+
onClick={() => setImportModalOpen(true)}
35+
>
36+
<DownloadIcon className="size-4" />
37+
Import contract
38+
</Button>
39+
<Button asChild className="gap-2">
40+
<Link href="/explore">
41+
<PlusIcon className="size-4" />
42+
Deploy contract
43+
</Link>
44+
</Button>
45+
</div>
46+
</div>
47+
</div>
48+
);
49+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Spinner } from "@/components/ui/Spinner/Spinner";
2+
import { Suspense } from "react";
3+
import { ClientOnly } from "../../../../../../components/ClientOnly/ClientOnly";
4+
import { DeployedContractsPageHeader } from "../DeployedContractsPageHeader";
5+
import { DeployedContractsTable } from "./DeployedContractsTable";
6+
import { GetStartedWithContractsDeploy } from "./GetStartedWithContractsDeploy";
7+
import { getSortedDeployedContracts } from "./getSortedDeployedContracts";
8+
9+
export function DeployedContractsPage(props: {
10+
address: string;
11+
className?: string;
12+
}) {
13+
return (
14+
<div className={props.className}>
15+
<DeployedContractsPageHeader />
16+
<div className="h-6" />
17+
<Suspense fallback={<Loading />}>
18+
<DeployedContractsPageAsync {...props} />
19+
</Suspense>
20+
</div>
21+
);
22+
}
23+
24+
async function DeployedContractsPageAsync(props: {
25+
address: string;
26+
}) {
27+
const deployedContracts = await getSortedDeployedContracts({
28+
address: props.address,
29+
});
30+
31+
if (deployedContracts.length === 0) {
32+
return <GetStartedWithContractsDeploy />;
33+
}
34+
35+
return (
36+
<ClientOnly ssr={<Loading />}>
37+
<DeployedContractsTable contracts={deployedContracts} />
38+
</ClientOnly>
39+
);
40+
}
41+
42+
function Loading() {
43+
return (
44+
<div className="flex min-h-[300px] grow items-center justify-center rounded-lg border border-border">
45+
<Spinner className="size-10" />
46+
</div>
47+
);
48+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"use client";
2+
import { useDashboardRouter } from "@/lib/DashboardRouter";
3+
import type { BasicContract } from "contract-ui/types/types";
4+
import { DeployedContracts } from "../../../../../../components/contract-components/tables/deployed-contracts";
5+
6+
export function DeployedContractsTable(props: {
7+
contracts: BasicContract[];
8+
}) {
9+
const router = useDashboardRouter();
10+
return (
11+
<DeployedContracts
12+
contractList={props.contracts}
13+
limit={50}
14+
isPending={false}
15+
onContractRemoved={router.refresh}
16+
/>
17+
);
18+
}

apps/dashboard/src/app/team/[team_slug]/[project_slug]/contracts/_components/GetStartedWithContractsDeploy.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
"use client";
22
import { TabButtons } from "@/components/ui/tabs";
33
import { useDashboardRouter } from "@/lib/DashboardRouter";
4-
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
54
import Image from "next/image";
65
import { useMemo, useState } from "react";
7-
import { useActiveAccount } from "thirdweb/react";
86
import { ImportModal } from "../../../../../../components/contract-components/import-contract/modal";
97
import { StepsCard } from "../../../../../../components/dashboard/StepsCard";
108
import { useTrack } from "../../../../../../hooks/analytics/useTrack";
119

1210
export function GetStartedWithContractsDeploy() {
13-
const address = useActiveAccount()?.address;
1411
const steps = useMemo(
1512
() => [
16-
{
17-
title: "Connect your wallet to get started",
18-
description:
19-
"In order to interact with your contracts you need to connect an EVM compatible wallet.",
20-
children: <CustomConnectWallet />,
21-
completed: !!address,
22-
},
23-
2413
{
2514
title: "Build, deploy or import a contract",
2615
description:
@@ -29,7 +18,7 @@ export function GetStartedWithContractsDeploy() {
2918
completed: false, // because we only show this component if the user does not have any contracts
3019
},
3120
],
32-
[address],
21+
[],
3322
);
3423

3524
return (
@@ -139,7 +128,7 @@ const DeployOptions = () => {
139128
<h4 className="text-start font-semibold text-lg">
140129
{activeTabContent.title}
141130
</h4>
142-
<p className="text-muted-foreground text-sm">
131+
<p className="text-start text-muted-foreground text-sm">
143132
{activeTabContent.description}
144133
</p>
145134
</div>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { BasicContract } from "contract-ui/types/types";
2+
import { MULTICHAIN_REGISTRY_CONTRACT } from "../../../../../../constants/contracts";
3+
import { getAllMultichainRegistry } from "../../../../../../dashboard-extensions/common/read/getAllMultichainRegistry";
4+
import { fetchChain } from "../../../../../../utils/fetchChain";
5+
6+
export async function getSortedDeployedContracts(params: {
7+
address: string;
8+
}) {
9+
const contracts = await getAllMultichainRegistry({
10+
contract: MULTICHAIN_REGISTRY_CONTRACT,
11+
address: params.address,
12+
});
13+
14+
const chainIds = Array.from(new Set(contracts.map((c) => c.chainId)));
15+
const chains = (
16+
await Promise.allSettled(
17+
chainIds.map((chainId) => fetchChain(chainId.toString())),
18+
)
19+
)
20+
.filter((c) => c.status === "fulfilled")
21+
.map((c) => c.value)
22+
.filter((c) => c !== null);
23+
24+
const mainnetContracts: BasicContract[] = [];
25+
const testnetContracts: BasicContract[] = [];
26+
27+
for (const contract of contracts) {
28+
const chain = chains.find((chain) => contract.chainId === chain.chainId);
29+
if (chain && chain.status !== "deprecated") {
30+
if (chain.testnet) {
31+
testnetContracts.push(contract);
32+
} else {
33+
mainnetContracts.push(contract);
34+
}
35+
}
36+
}
37+
38+
mainnetContracts.sort((a, b) => a.chainId - b.chainId);
39+
testnetContracts.sort((a, b) => a.chainId - b.chainId);
40+
41+
return [...mainnetContracts, ...testnetContracts];
42+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export default function Layout(props: {
22
children: React.ReactNode;
33
}) {
4-
return <div className="container">{props.children}</div>;
4+
return <div className="container flex grow flex-col">{props.children}</div>;
55
}

0 commit comments

Comments
 (0)