Skip to content

Commit a4ee55c

Browse files
committed
Move Deployed Contracts Page to App Router
1 parent 2914a50 commit a4ee55c

File tree

9 files changed

+118
-69
lines changed

9 files changed

+118
-69
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.all(chainIds.map((chainId) => fetchChain(chainId.toString())))
17+
).filter((c) => c !== null);
18+
19+
const mainnetContracts: BasicContract[] = [];
20+
const testnetContracts: BasicContract[] = [];
21+
22+
for (const contract of contracts) {
23+
const chain = chains.find((chain) => contract.chainId === chain.chainId);
24+
if (chain && chain.status !== "deprecated") {
25+
if (chain.testnet) {
26+
testnetContracts.push(contract);
27+
} else {
28+
mainnetContracts.push(contract);
29+
}
30+
}
31+
}
32+
33+
mainnetContracts.sort((a, b) => a.chainId - b.chainId);
34+
testnetContracts.sort((a, b) => a.chainId - b.chainId);
35+
return [...mainnetContracts, ...testnetContracts];
36+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { DeployedContracts } from "components/contract-components/tables/deployed-contracts";
2+
import { redirect } from "next/navigation";
3+
import { getAuthTokenWalletAddress } from "../../../../api/lib/getAuthToken";
4+
import { GetStartedWithContractsDeploy } from "../../../../team/[team_slug]/[project_slug]/contracts/_components/GetStartedWithContractsDeploy";
5+
import { getSortedDeployedContracts } from "./getSortedDeployedContracts";
6+
7+
export default async function Page() {
8+
const accountAddress = getAuthTokenWalletAddress();
9+
if (!accountAddress) {
10+
return redirect(`/login?next=${encodeURIComponent("/dashboard")}`);
11+
}
12+
13+
const contracts = await getSortedDeployedContracts({
14+
address: accountAddress,
15+
});
16+
17+
if (contracts.length === 0) {
18+
return <GetStartedWithContractsDeploy />;
19+
}
20+
21+
return (
22+
<DeployedContracts contractList={contracts} limit={50} isPending={false} />
23+
);
24+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ContractsSidebarLayout } from "../../../../core-ui/sidebar/contracts";
2+
3+
export default function Layout(props: {
4+
children: React.ReactNode;
5+
}) {
6+
return <ContractsSidebarLayout>{props.children}</ContractsSidebarLayout>;
7+
}

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+
}

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

Lines changed: 1 addition & 12 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 (

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ export default function Page() {
2424
{!hasContracts ? (
2525
<GetStartedWithContractsDeploy />
2626
) : (
27-
<DeployedContracts contractListQuery={deployedContracts} limit={50} />
27+
<DeployedContracts
28+
contractList={deployedContracts.data}
29+
isPending={deployedContracts.isPending}
30+
limit={50}
31+
/>
2832
)}
2933
</div>
3034
);

apps/dashboard/src/components/contract-components/tables/deployed-contracts.tsx

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
usePagination,
4343
useTable,
4444
} from "react-table";
45+
import { SkeletonContainer } from "../../../@/components/ui/skeleton";
4546
import { useAllChainsData } from "../../../hooks/chains/allChains";
4647
import { useChainSlug } from "../../../hooks/chains/chainSlug";
4748
import { ImportModal } from "../import-contract/modal";
@@ -50,25 +51,27 @@ import { ShowMoreButton } from "./show-more-button";
5051

5152
interface DeployedContractsProps {
5253
noHeader?: boolean;
53-
contractListQuery: ReturnType<typeof useAllContractList>;
54+
contractList: ReturnType<typeof useAllContractList>["data"];
55+
isPending: boolean;
5456
limit?: number;
5557
}
5658

5759
export const DeployedContracts: React.FC<DeployedContractsProps> = ({
5860
noHeader,
59-
contractListQuery,
61+
contractList,
6062
limit = 10,
63+
isPending,
6164
}) => {
6265
const [importModalOpen, setImportModalOpen] = useState(false);
6366

6467
const chainIdsWithDeployments = useMemo(() => {
6568
const set = new Set<number>();
6669
// biome-ignore lint/complexity/noForEach: FIXME
67-
contractListQuery.data.forEach((contract) => {
70+
contractList.forEach((contract) => {
6871
set.add(contract.chainId);
6972
});
7073
return [...set];
71-
}, [contractListQuery.data]);
74+
}, [contractList]);
7275

7376
return (
7477
<div className="flex flex-col gap-8">
@@ -111,13 +114,13 @@ export const DeployedContracts: React.FC<DeployedContractsProps> = ({
111114
)}
112115

113116
<ContractTable
114-
combinedList={contractListQuery.data}
117+
combinedList={contractList}
115118
limit={limit}
116119
chainIdsWithDeployments={chainIdsWithDeployments}
117-
loading={contractListQuery.isPending}
120+
loading={isPending}
118121
/>
119122

120-
{contractListQuery.data.length === 0 && contractListQuery.isFetched && (
123+
{contractList.length === 0 && !isPending && (
121124
<div className="flex h-[100px] items-center justify-center text-muted-foreground">
122125
No contracts found
123126
</div>
@@ -136,13 +139,21 @@ const RemoveFromDashboardButton: React.FC<RemoveFromDashboardButtonProps> = ({
136139
contractAddress,
137140
}) => {
138141
const mutation = useRemoveContractMutation();
142+
const router = useDashboardRouter();
139143

140144
return (
141145
<Button
142146
variant="ghost"
143147
onClick={(e) => {
144148
e.stopPropagation();
145-
mutation.mutate({ chainId, contractAddress });
149+
mutation.mutateAsync(
150+
{ chainId, contractAddress },
151+
{
152+
onSuccess: () => {
153+
router.refresh();
154+
},
155+
},
156+
);
146157
}}
147158
disabled={mutation.isPending}
148159
className="!bg-background hover:!bg-accent gap-2"
@@ -236,9 +247,14 @@ const ContractTable: React.FC<ContractTableProps> = ({
236247
return (
237248
<div className="flex items-center gap-2">
238249
<ChainIcon size={24} ipfsSrc={data?.icon?.url} />
239-
<p className="text-muted-foreground text-sm">
240-
{cleanedChainName}
241-
</p>
250+
<SkeletonContainer
251+
loadedData={data ? cleanedChainName : undefined}
252+
skeletonData={`Chain ID ${cell.row.original.chainId}`}
253+
render={(v) => {
254+
return <p className="text-muted-foreground text-sm">{v}</p>;
255+
}}
256+
/>
257+
242258
{data?.testnet && (
243259
<Badge variant="outline" className="text-muted-foreground">
244260
Testnet

apps/dashboard/src/pages/dashboard/contracts/deploy.tsx

Lines changed: 0 additions & 44 deletions
This file was deleted.

apps/dashboard/src/pages/profile/[profileAddress].tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ const UserPage: ThirdwebNextPage = (props: UserPageProps) => {
178178
{ens.data?.address && (
179179
<DeployedContracts
180180
noHeader
181-
contractListQuery={mainnetsContractList}
181+
contractList={mainnetsContractList.data}
182+
isPending={mainnetsContractList.isPending}
182183
/>
183184
)}
184185
</Flex>

0 commit comments

Comments
 (0)