diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx
new file mode 100644
index 00000000000..22cefdb784f
--- /dev/null
+++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx
@@ -0,0 +1,133 @@
+import { Spinner } from "@/components/ui/Spinner/Spinner";
+import { fetchPublishedContracts } from "components/contract-components/fetchPublishedContracts";
+import { PublisherSocials } from "components/contract-components/publisher/PublisherSocials";
+import { EditProfile } from "components/contract-components/publisher/edit-profile";
+import { PublisherAvatar } from "components/contract-components/publisher/masked-avatar";
+import { DeployedContracts } from "components/contract-components/tables/deployed-contracts";
+import type { ProfileMetadata } from "constants/schemas";
+import { Suspense } from "react";
+import { shortenIfAddress } from "utils/usedapp-external";
+import { getSortedDeployedContracts } from "../../../account/contracts/_components/getSortedDeployedContracts";
+import { PublishedContracts } from "./components/published-contracts";
+
+export function ProfileUI(props: {
+ profileAddress: string;
+ ensName: string | undefined;
+ publisherProfile: ProfileMetadata;
+ showEditProfile: boolean;
+}) {
+ const { profileAddress, ensName, publisherProfile, showEditProfile } = props;
+
+ const displayName = shortenIfAddress(ensName || profileAddress).replace(
+ "deployer.thirdweb.eth",
+ "thirdweb.eth",
+ );
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
+ {displayName}
+
+
+ {publisherProfile.bio && (
+
+ {publisherProfile.bio}
+
+ )}
+
+
+
+
+
+ {showEditProfile && (
+
+
+
+ )}
+
+
+
+
+
+
+ Published contracts
+
+
+
+
}>
+
+
+
+
+
+
+
+
+ Deployed contracts
+
+
+
+ List of contracts deployed across all Mainnets
+
+
+
+
}>
+
+
+
+
+ );
+}
+
+async function AsyncDeployedContracts(props: {
+ profileAddress: string;
+}) {
+ const contracts = await getSortedDeployedContracts({
+ address: props.profileAddress,
+ onlyMainnet: true,
+ });
+
+ return ;
+}
+
+async function AsyncPublishedContracts(props: {
+ publisherAddress: string;
+ publisherEnsName: string | undefined;
+}) {
+ const publishedContracts = await fetchPublishedContracts(
+ props.publisherAddress,
+ );
+
+ if (publishedContracts.length === 0) {
+ return (
+
+ No published contracts found
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+function LoadingSection() {
+ return (
+
+
+
+ );
+}
diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx
new file mode 100644
index 00000000000..73c042d2e3f
--- /dev/null
+++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx
@@ -0,0 +1,205 @@
+import { Img } from "@/components/blocks/Img";
+import { Button } from "@/components/ui/button";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { ToolTipLabel } from "@/components/ui/tooltip";
+import { TrackedLinkTW } from "@/components/ui/tracked-link";
+import { replaceDeployerAddress } from "components/explore/publisher";
+import { useTrack } from "hooks/analytics/useTrack";
+import { replaceIpfsUrl } from "lib/sdk";
+import { ShieldCheckIcon } from "lucide-react";
+import Link from "next/link";
+import { useMemo } from "react";
+import { type Column, type Row, useTable } from "react-table";
+import type { PublishedContractDetails } from "../../../../../components/contract-components/hooks";
+
+interface PublishedContractTableProps {
+ contractDetails: ContractDataInput[];
+ footer?: React.ReactNode;
+ publisherEnsName: string | undefined;
+}
+
+type ContractDataInput = PublishedContractDetails;
+type ContractDataRow = ContractDataInput["metadata"] & {
+ id: string;
+};
+
+function convertContractDataToRowData(
+ input: ContractDataInput,
+): ContractDataRow {
+ return {
+ id: input.contractId,
+ ...input.metadata,
+ };
+}
+
+export function PublishedContractTable(props: PublishedContractTableProps) {
+ const { contractDetails, footer, publisherEnsName } = props;
+ const trackEvent = useTrack();
+ const rows = useMemo(
+ () => contractDetails.map(convertContractDataToRowData),
+ [contractDetails],
+ );
+
+ const tableColumns: Column[] = useMemo(() => {
+ const cols: Column[] = [
+ {
+ Header: "Logo",
+ accessor: (row) => row.logo,
+ // biome-ignore lint/suspicious/noExplicitAny:
+ Cell: (cell: any) => (
+
+ }
+ className="size-8"
+ />
+ ),
+ },
+ {
+ Header: "Name",
+ accessor: (row) => row.name,
+ // biome-ignore lint/suspicious/noExplicitAny: FIXME
+ Cell: (cell: any) => {
+ return (
+
+ {cell.value}
+
+ );
+ },
+ },
+ {
+ Header: "Description",
+ accessor: (row) => row.description,
+ // biome-ignore lint/suspicious/noExplicitAny: FIXME
+ Cell: (cell: any) => (
+
+ {cell.value}
+
+ ),
+ },
+ {
+ Header: "Version",
+ accessor: (row) => row.version,
+ // biome-ignore lint/suspicious/noExplicitAny: FIXME
+ Cell: (cell: any) => (
+ {cell.value}
+ ),
+ },
+ {
+ id: "audit-badge",
+ accessor: (row) => ({ audit: row.audit }),
+ // biome-ignore lint/suspicious/noExplicitAny: FIXME
+ Cell: (cell: any) => (
+
+ {cell.value.audit ? (
+
+
+
+ ) : null}
+
+ ),
+ },
+ ];
+
+ return cols;
+ }, [trackEvent, publisherEnsName]);
+
+ const tableInstance = useTable({
+ columns: tableColumns,
+ data: rows,
+ });
+
+ return (
+
+
+
+ {tableInstance.headerGroups.map((headerGroup) => {
+ const { key, ...rowProps } = headerGroup.getHeaderGroupProps();
+ return (
+
+ {headerGroup.headers.map((column, columnIndex) => (
+
+
+ {column.render("Header")}
+
+
+ ))}
+
+ );
+ })}
+
+
+
+ {tableInstance.rows.map((row) => {
+ tableInstance.prepareRow(row);
+ return ;
+ })}
+
+
+ {footer}
+
+ );
+}
+
+function ContractTableRow(props: {
+ row: Row;
+}) {
+ const { row } = props;
+ const { key, ...rowProps } = row.getRowProps();
+ return (
+ <>
+
+ {row.cells.map((cell) => (
+
+ {cell.render("Cell")}
+
+ ))}
+
+ >
+ );
+}
diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/published-contracts.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/published-contracts.tsx
new file mode 100644
index 00000000000..2cda428919b
--- /dev/null
+++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/published-contracts.tsx
@@ -0,0 +1,37 @@
+"use client";
+
+import { useState } from "react";
+import type { fetchPublishedContracts } from "../../../../../components/contract-components/fetchPublishedContracts";
+import { ShowMoreButton } from "../../../../../components/contract-components/tables/show-more-button";
+import { PublishedContractTable } from "./PublishedContractTable";
+
+interface PublishedContractsProps {
+ limit?: number;
+ publishedContracts: Awaited>;
+ publisherEnsName: string | undefined;
+}
+
+export const PublishedContracts: React.FC = ({
+ limit = 10,
+ publishedContracts,
+ publisherEnsName,
+}) => {
+ const [showMoreLimit, setShowMoreLimit] = useState(10);
+ const slicedData = publishedContracts.slice(0, showMoreLimit);
+
+ return (
+ slicedData.length ? (
+
+ ) : undefined
+ }
+ />
+ );
+};
diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx
new file mode 100644
index 00000000000..b94cf5f50d1
--- /dev/null
+++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx
@@ -0,0 +1,100 @@
+import { getActiveAccountCookie } from "@/constants/cookie";
+import { fetchPublisherProfile } from "components/contract-components/fetch-contracts-with-versions";
+import type { Metadata } from "next";
+import { notFound } from "next/navigation";
+import { getAddress } from "thirdweb/utils";
+import { shortenIfAddress } from "utils/usedapp-external";
+import { ProfileUI } from "./ProfileUI";
+import { resolveAddressAndEns } from "./resolveAddressAndEns";
+
+type PageProps = {
+ params: Promise<{
+ addressOrEns: string;
+ }>;
+};
+
+export default async function Page(props: PageProps) {
+ const params = await props.params;
+ const resolvedInfo = await resolveAddressAndEns(params.addressOrEns);
+ const currentUserAddress = await getCurrentUserAddress();
+
+ if (!resolvedInfo) {
+ return notFound();
+ }
+
+ const publisherProfile = await fetchPublisherProfile(
+ resolvedInfo.address,
+ ).catch(() => null);
+
+ if (!publisherProfile) {
+ return notFound();
+ }
+
+ return (
+
+ );
+}
+
+export async function generateMetadata(props: PageProps): Promise {
+ const params = await props.params;
+ const resolvedInfo = await resolveAddressAndEns(params.addressOrEns);
+
+ if (!resolvedInfo) {
+ return notFound();
+ }
+
+ const publisherProfile = await fetchPublisherProfile(
+ resolvedInfo.address,
+ ).catch(() => null);
+
+ if (!publisherProfile) {
+ return notFound();
+ }
+
+ const displayName = shortenIfAddress(
+ resolvedInfo.ensName || resolvedInfo.address,
+ ).replace("deployer.thirdweb.eth", "thirdweb.eth");
+
+ // TODO - move this to opengraph-image.tsx
+ // this is not working even on prod
+ // const ogImageLink = ProfileOG.toUrl({
+ // displayName,
+ // bio: publisherProfile.bio,
+ // avatar: publisherProfile.avatar,
+ // });
+
+ return {
+ title: displayName,
+ description: `Visit ${displayName}'s profile. See their published contracts and deploy them in one click.`,
+ // openGraph: {
+ // title: displayName,
+ // images: ogImageLink
+ // ? [
+ // {
+ // url: ogImageLink.toString(),
+ // alt: `${displayName}'s profile on thirdweb.com`,
+ // width: 1200,
+ // height: 630,
+ // },
+ // ]
+ // : undefined,
+ // },
+ };
+}
+
+async function getCurrentUserAddress() {
+ try {
+ const currentUserAddress = await getActiveAccountCookie();
+ if (!currentUserAddress) {
+ return null;
+ }
+ return getAddress(currentUserAddress);
+ } catch {
+ return null;
+ }
+}
diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/resolveAddressAndEns.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/resolveAddressAndEns.tsx
new file mode 100644
index 00000000000..68dd1ef7346
--- /dev/null
+++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/resolveAddressAndEns.tsx
@@ -0,0 +1,32 @@
+import { getAddress, isAddress } from "thirdweb";
+import { mapThirdwebPublisher } from "../../../../components/contract-components/fetch-contracts-with-versions";
+import { isEnsName, resolveEns } from "../../../../lib/ens";
+
+type ResolvedAddressInfo = {
+ address: string;
+ ensName: string | undefined;
+};
+export async function resolveAddressAndEns(
+ addressOrEns: string,
+): Promise {
+ if (isAddress(addressOrEns)) {
+ const res = await resolveEns(addressOrEns).catch(() => null);
+ return {
+ address: getAddress(addressOrEns),
+ ensName: res?.ensName || undefined,
+ };
+ }
+
+ if (isEnsName(addressOrEns)) {
+ const mappedEns = mapThirdwebPublisher(addressOrEns);
+ const res = await resolveEns(mappedEns).catch(() => null);
+ if (res?.address) {
+ return {
+ address: getAddress(res?.address),
+ ensName: mappedEns,
+ };
+ }
+ }
+
+ return undefined;
+}
diff --git a/apps/dashboard/src/app/account/contracts/_components/DeployedContractsTable.tsx b/apps/dashboard/src/app/account/contracts/_components/DeployedContractsTable.tsx
index 824a76e38fe..4bd6d3a2ac0 100644
--- a/apps/dashboard/src/app/account/contracts/_components/DeployedContractsTable.tsx
+++ b/apps/dashboard/src/app/account/contracts/_components/DeployedContractsTable.tsx
@@ -11,7 +11,6 @@ export function DeployedContractsTable(props: {
);
diff --git a/apps/dashboard/src/app/account/contracts/_components/getSortedDeployedContracts.tsx b/apps/dashboard/src/app/account/contracts/_components/getSortedDeployedContracts.tsx
index c0eb66b4a7d..ae6c6bb5b26 100644
--- a/apps/dashboard/src/app/account/contracts/_components/getSortedDeployedContracts.tsx
+++ b/apps/dashboard/src/app/account/contracts/_components/getSortedDeployedContracts.tsx
@@ -5,6 +5,7 @@ import { fetchChain } from "../../../../utils/fetchChain";
export async function getSortedDeployedContracts(params: {
address: string;
+ onlyMainnet?: boolean;
}) {
const contracts = await getAllMultichainRegistry({
contract: MULTICHAIN_REGISTRY_CONTRACT,
@@ -36,6 +37,11 @@ export async function getSortedDeployedContracts(params: {
}
mainnetContracts.sort((a, b) => a.chainId - b.chainId);
+
+ if (params.onlyMainnet) {
+ return mainnetContracts;
+ }
+
testnetContracts.sort((a, b) => a.chainId - b.chainId);
return [...mainnetContracts, ...testnetContracts];
diff --git a/apps/dashboard/src/components/connect/PlaygroundMenu.tsx b/apps/dashboard/src/components/connect/PlaygroundMenu.tsx
index 1ab0ebf9b6c..ac305222f38 100644
--- a/apps/dashboard/src/components/connect/PlaygroundMenu.tsx
+++ b/apps/dashboard/src/components/connect/PlaygroundMenu.tsx
@@ -1,7 +1,7 @@
+import { useDashboardRouter } from "@/lib/DashboardRouter";
import { type BoxProps, Flex } from "@chakra-ui/react";
import { ChakraNextImage } from "components/Image";
import type { StaticImageData } from "next/image";
-import { useRouter } from "next/router";
import { Card, Text } from "tw-components";
interface PlaygroundMenuProps extends BoxProps {
@@ -22,7 +22,7 @@ export const PlaygroundMenu: React.FC = ({
onClick,
...rest
}) => {
- const router = useRouter();
+ const router = useDashboardRouter();
return (
= ({ contractDetails, isFetching, children, hidePublisher }) => {
- const rows = useMemo(
- () => contractDetails.map(convertContractDataToRowData),
- [contractDetails],
- );
-
- const trackEvent = useTrack();
-
- const tableColumns: Column[] = useMemo(() => {
- const cols: Column[] = [
- {
- Header: "Logo",
- accessor: (row) => row.logo,
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- Cell: (cell: any) =>
- cell.value ? (
- typeof cell.value === "string" ? (
-
- ) : (
-
- )
- ) : null,
- },
- {
- Header: "Name",
- accessor: (row) => row.name,
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- Cell: (cell: any) => (
-
- {cell.value}
-
- ),
- },
- {
- Header: "Description",
- accessor: (row) => row.description,
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- Cell: (cell: any) => (
-
- {cell.value}
-
- ),
- },
- {
- Header: "Version",
- accessor: (row) => row.version,
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- Cell: (cell: any) => {cell.value},
- },
- {
- Header: "Published By",
- accessor: (row) => row.publisher,
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- Cell: (cell: any) => ,
- },
- {
- id: "audit-badge",
- accessor: (row) => ({ audit: row.audit }),
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- Cell: (cell: any) => (
-
- {cell.value.audit ? (
-
- Audited Contract
-
- }
- borderRadius="lg"
- placement="top"
- >
- }
- onClick={(e) => {
- e.stopPropagation();
- trackEvent({
- category: "visit-audit",
- action: "click",
- label: cell.value.audit,
- });
- }}
- />
-
- ) : null}
-
- ),
- },
- ];
-
- return cols.filter(
- (col) => !hidePublisher || col.Header !== "Published By",
- );
- // this is to avoid re-rendering of the table when the contractIds array changes (it will always be a string array, so we can just join it and compare the string output)
- }, [trackEvent, hidePublisher]);
-
- const tableInstance = useTable({
- columns: tableColumns,
- data: rows,
- });
- return (
-
- {isFetching && (
-
- )}
-
-
- {tableInstance.headerGroups.map((headerGroup, headerGroupIndex) => (
- // biome-ignore lint/suspicious/noArrayIndexKey: FIXME
-
- {headerGroup.headers.map((column, columnIndex) => (
- |
-
- {column.render("Header")}
-
- |
- ))}
- {/* Need to add an empty header for the icon button */}
- |
-
- ))}
-
-
- {tableInstance.rows.map((row) => {
- tableInstance.prepareRow(row);
- return ;
- })}
-
-
- {children}
-
- );
-};
-
-interface ContractTableRowProps {
- row: Row;
-}
-
-const ContractTableRow: React.FC = ({ row }) => {
- const router = useRouter();
-
- return (
- <>
- {
- router.push(
- replaceDeployerAddress(
- `/${row.original.publisher}/${row.original.id}`,
- ),
- undefined,
- {
- scroll: true,
- },
- );
- }}
- {...row.getRowProps()}
- >
- {row.cells.map((cell) => (
- |
- {cell.render("Cell")}
- |
- ))}
-
-
- |
-
- >
- );
-};
diff --git a/apps/dashboard/src/components/contract-components/fetch-contracts-with-versions.ts b/apps/dashboard/src/components/contract-components/fetch-contracts-with-versions.ts
index 8822f88eeee..aa2a7033a29 100644
--- a/apps/dashboard/src/components/contract-components/fetch-contracts-with-versions.ts
+++ b/apps/dashboard/src/components/contract-components/fetch-contracts-with-versions.ts
@@ -10,15 +10,17 @@ import {
} from "thirdweb/extensions/thirdweb";
import { download } from "thirdweb/storage";
-function mapThirdwebPublisher(publisher: string) {
+export function mapThirdwebPublisher(publisher: string) {
if (publisher === "thirdweb.eth") {
return "deployer.thirdweb.eth";
}
+
return publisher;
}
export async function fetchPublisherProfile(publisherAddress: string) {
const client = getThirdwebClient();
+
const profileUri = await getPublisherProfileUri({
contract: getContractPublisher(client),
publisher: isAddress(publisherAddress)
@@ -28,9 +30,11 @@ export async function fetchPublisherProfile(publisherAddress: string) {
name: mapThirdwebPublisher(publisherAddress),
}),
});
+
if (!profileUri) {
return null;
}
+
try {
const res = await download({
client,
diff --git a/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx b/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx
index 6bf8f4e58bf..2538de3f19a 100644
--- a/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx
+++ b/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx
@@ -1,4 +1,5 @@
-import { ButtonGroup, type ButtonGroupProps } from "@chakra-ui/react";
+import { Button } from "@/components/ui/button";
+import { TrackedLinkTW } from "@/components/ui/tracked-link";
import { DiscordIcon } from "components/icons/brand-icons/DiscordIcon";
import { GithubIcon } from "components/icons/brand-icons/GithubIcon";
import { LinkedInIcon } from "components/icons/brand-icons/LinkedinIcon";
@@ -9,163 +10,128 @@ import { TelegramIcon } from "components/icons/brand-icons/TelegramIcon";
import { XIcon } from "components/icons/brand-icons/XIcon";
import type { ProfileMetadata } from "constants/schemas";
import { GlobeIcon } from "lucide-react";
-import { LinkButton, TrackedIconButton } from "tw-components";
import { hostnameEndsWith } from "../../../utils/url";
const TRACKING_CATEGORY = "releaser-header";
-interface PublisherSocialsProps extends ButtonGroupProps {
+export const PublisherSocials: React.FC<{
publisherProfile: ProfileMetadata;
-}
-
-export const PublisherSocials: React.FC = ({
- publisherProfile,
- spacing = 0,
- size = "sm",
- ...props
-}) => (
-
+}> = ({ publisherProfile }) => (
+
{publisherProfile.twitter && (
}
- category={TRACKING_CATEGORY}
label="twitter"
+ icon={XIcon}
/>
)}
+
{publisherProfile.discord && (
}
- category={TRACKING_CATEGORY}
+ icon={DiscordIcon}
label="discord"
/>
)}
+
{publisherProfile.github && (
}
- category={TRACKING_CATEGORY}
+ icon={GithubIcon}
label="github"
/>
)}
+
{publisherProfile.website && (
}
- category={TRACKING_CATEGORY}
+ icon={GlobeIcon}
label="website"
/>
)}
{publisherProfile.medium && (
}
- category={TRACKING_CATEGORY}
+ icon={MediumIcon}
label="medium"
/>
)}
+
{publisherProfile.telegram && (
}
- category={TRACKING_CATEGORY}
+ icon={TelegramIcon}
label="telegram"
/>
)}
+
{publisherProfile.facebook && (
}
- category={TRACKING_CATEGORY}
+ icon={MetaIcon}
label="facebook"
/>
)}
+
{publisherProfile.reddit && (
}
- category={TRACKING_CATEGORY}
+ icon={RedditIcon}
label="reddit"
/>
)}
+
{publisherProfile.linkedin && (
}
- category={TRACKING_CATEGORY}
+ icon={LinkedInIcon}
label="linkedin"
/>
)}
-
+
);
+
+function TrackedIconButton(props: {
+ icon: React.FC<{ className?: string }>;
+ href: string;
+ label: string;
+}) {
+ return (
+
+ );
+}
diff --git a/apps/dashboard/src/components/contract-components/publisher/edit-profile.tsx b/apps/dashboard/src/components/contract-components/publisher/edit-profile.tsx
index 306a9660320..8664c42ec20 100644
--- a/apps/dashboard/src/components/contract-components/publisher/edit-profile.tsx
+++ b/apps/dashboard/src/components/contract-components/publisher/edit-profile.tsx
@@ -1,6 +1,9 @@
"use client";
+import { FormFieldSetup } from "@/components/blocks/FormFieldSetup";
+import { Spinner } from "@/components/ui/Spinner/Spinner";
import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
import {
Sheet,
SheetContent,
@@ -8,12 +11,10 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
+import { Textarea } from "@/components/ui/textarea";
import { useThirdwebClient } from "@/constants/thirdweb.client";
-import { Box, FormControl, Input, Textarea } from "@chakra-ui/react";
+import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
import { useQueryClient } from "@tanstack/react-query";
-import { DiscordIcon } from "components/icons/brand-icons/DiscordIcon";
-import { GithubIcon } from "components/icons/brand-icons/GithubIcon";
-import { XIcon } from "components/icons/brand-icons/XIcon";
import { FileInput } from "components/shared/FileInput";
import {
DASHBOARD_ENGINE_RELAYER_URL,
@@ -22,7 +23,7 @@ import {
import type { ProfileMetadata, ProfileMetadataInput } from "constants/schemas";
import { useTrack } from "hooks/analytics/useTrack";
import { useImageFileOrUrl } from "hooks/useImageFileOrUrl";
-import { EditIcon, GlobeIcon, ImageIcon, PencilIcon } from "lucide-react";
+import { EditIcon } from "lucide-react";
import { useId, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -32,7 +33,6 @@ import {
} from "thirdweb/extensions/thirdweb";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
import { upload } from "thirdweb/storage";
-import { FormErrorMessage, FormLabel } from "tw-components";
import { MaskedAvatar } from "tw-components/masked-avatar";
interface EditProfileProps {
@@ -73,16 +73,16 @@ export const EditProfile: React.FC = ({
return (
-
-
+
Edit your profile
diff --git a/apps/dashboard/src/components/contract-components/tables/deployed-contracts.tsx b/apps/dashboard/src/components/contract-components/tables/deployed-contracts.tsx
index da003e56089..09b32b504f0 100644
--- a/apps/dashboard/src/components/contract-components/tables/deployed-contracts.tsx
+++ b/apps/dashboard/src/components/contract-components/tables/deployed-contracts.tsx
@@ -42,7 +42,6 @@ import { ShowMoreButton } from "./show-more-button";
interface DeployedContractsProps {
contractList: ReturnType["data"];
- isPending: boolean;
limit?: number;
onContractRemoved?: () => void;
}
@@ -50,7 +49,6 @@ interface DeployedContractsProps {
export const DeployedContracts: React.FC = ({
contractList,
limit = 10,
- isPending,
onContractRemoved,
}) => {
const chainIdsWithDeployments = useMemo(() => {
@@ -68,11 +66,10 @@ export const DeployedContracts: React.FC = ({
combinedList={contractList}
limit={limit}
chainIdsWithDeployments={chainIdsWithDeployments}
- loading={isPending}
onContractRemoved={onContractRemoved}
/>
- {contractList.length === 0 && !isPending && (
+ {contractList.length === 0 && (
No contracts found
@@ -148,19 +145,15 @@ function SelectNetworkFilter({
interface ContractTableProps {
combinedList: BasicContract[];
- isFetching?: boolean;
limit: number;
chainIdsWithDeployments: number[];
- loading: boolean;
onContractRemoved?: () => void;
}
const ContractTable: React.FC = ({
combinedList,
- isFetching,
limit,
chainIdsWithDeployments,
- loading,
onContractRemoved,
}) => {
const { idToChain } = useAllChainsData();
@@ -310,7 +303,6 @@ const ContractTable: React.FC = ({
return (
- {isFetching && }
{headerGroups.map((headerGroup, index) => (
@@ -354,14 +346,6 @@ const ContractTable: React.FC = ({
setShowMoreLimit={setNumRowsOnPage}
/>
)}
- {loading && (
-
-
-
- Loading contracts
-
-
- )}
);
};
diff --git a/apps/dashboard/src/components/contract-components/tables/published-contracts.tsx b/apps/dashboard/src/components/contract-components/tables/published-contracts.tsx
deleted file mode 100644
index e2eec1c5474..00000000000
--- a/apps/dashboard/src/components/contract-components/tables/published-contracts.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import { Alert, AlertIcon, AlertTitle, Flex, Spinner } from "@chakra-ui/react";
-import { useTrack } from "hooks/analytics/useTrack";
-import { RefreshCcwIcon } from "lucide-react";
-import { useMemo, useState } from "react";
-import { Button, Heading, LinkButton, Text } from "tw-components";
-import { PublishedContractTable } from "../contract-table-v2";
-import { usePublishedContractsQuery } from "../hooks";
-import { ShowMoreButton } from "./show-more-button";
-
-interface PublishedContractsProps {
- address?: string;
- noHeader?: boolean;
- limit?: number;
-}
-
-export const PublishedContracts: React.FC = ({
- address,
- noHeader,
- limit = 10,
-}) => {
- const [showMoreLimit, setShowMoreLimit] = useState(limit);
- const trackEvent = useTrack();
- const publishedContractsQuery = usePublishedContractsQuery(address);
-
- const slicedData = useMemo(() => {
- if (publishedContractsQuery.data) {
- return publishedContractsQuery.data.slice(0, showMoreLimit);
- }
- return [];
- }, [publishedContractsQuery.data, showMoreLimit]);
-
- return (
- <>
- {!noHeader && (
-
-
- Published contracts
-
- The list of contract instances that you have published with
- thirdweb
-
-
- {
- trackEvent({
- category: "dashboard",
- action: "click",
- label: "learn-more-about-release",
- });
- }}
- >
- Learn more about Publish
-
-
- )}
-
- {publishedContractsQuery.isPending && (
-
-
-
- Loading published contracts
-
-
- )}
- {publishedContractsQuery.isError && (
-
-
-
-
-
- Failed to fetch published contracts
-
- publishedContractsQuery.refetch()}
- leftIcon={}
- ml="auto"
- size="sm"
- colorScheme="red"
- >
- Retry
-
-
-
-
- )}
- {publishedContractsQuery.isSuccess &&
- publishedContractsQuery.data.length === 0 && (
-
-
- No published contracts found.
-
-
- )}
- {publishedContractsQuery.isSuccess &&
- publishedContractsQuery.data.length > slicedData.length && (
-
- )}
-
- >
- );
-};
diff --git a/apps/dashboard/src/components/explore/publisher/index.tsx b/apps/dashboard/src/components/explore/publisher/index.tsx
index 8483ce39796..10969838dc2 100644
--- a/apps/dashboard/src/components/explore/publisher/index.tsx
+++ b/apps/dashboard/src/components/explore/publisher/index.tsx
@@ -40,7 +40,12 @@ export const ContractPublisher: React.FC = async ({
};
export function replaceDeployerAddress(address: string) {
- return address.replace("deployer.thirdweb.eth", "thirdweb.eth");
+ return (
+ address
+ .replace("deployer.thirdweb.eth", "thirdweb.eth")
+ // deployer.thirdweb.eth
+ .replace("0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024", "thirdweb.eth")
+ );
}
export function treatAddress(address: string) {
diff --git a/apps/dashboard/src/constants/urls.ts b/apps/dashboard/src/constants/urls.ts
index 1d0d026a1e2..8f9db171323 100644
--- a/apps/dashboard/src/constants/urls.ts
+++ b/apps/dashboard/src/constants/urls.ts
@@ -1,6 +1,3 @@
-export const THIRDWEB_DOMAIN =
- process.env.NEXT_PUBLIC_THIRDWEB_DOMAIN || "thirdweb.com";
-
export const THIRDWEB_API_HOST = "/api/server-proxy/api";
export const THIRDWEB_ANALYTICS_API_HOST = "/api/server-proxy/analytics";
diff --git a/apps/dashboard/src/page-id.ts b/apps/dashboard/src/page-id.ts
index 90184bc007a..9d54e2c1cdd 100644
--- a/apps/dashboard/src/page-id.ts
+++ b/apps/dashboard/src/page-id.ts
@@ -81,10 +81,6 @@ export enum PageId {
// thirdweb.com/contracts/publish
PublishMultiple = "publish-multiple-contracts",
- // thirdweb.com/:wallet
- // example: thirdweb.com/jns.eth
- Profile = "profile",
-
// ---------------------------------------------------------------------------
// "deploy" product pages
// ---------------------------------------------------------------------------
diff --git a/apps/dashboard/src/pages/profile/[profileAddress].tsx b/apps/dashboard/src/pages/profile/[profileAddress].tsx
deleted file mode 100644
index 2a50812927f..00000000000
--- a/apps/dashboard/src/pages/profile/[profileAddress].tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-import { useAllContractList } from "@3rdweb-sdk/react/hooks/useRegistry";
-import { Box, Flex, Spinner } from "@chakra-ui/react";
-import { AppLayout } from "components/app-layouts/app";
-import {
- useEns,
- usePublishedContractsQuery,
- usePublisherProfile,
-} from "components/contract-components/hooks";
-import { PublisherSocials } from "components/contract-components/publisher/PublisherSocials";
-import { EditProfile } from "components/contract-components/publisher/edit-profile";
-import { PublisherAvatar } from "components/contract-components/publisher/masked-avatar";
-import { DeployedContracts } from "components/contract-components/tables/deployed-contracts";
-import { PublishedContracts } from "components/contract-components/tables/published-contracts";
-import { THIRDWEB_DOMAIN } from "constants/urls";
-import type { GetStaticProps } from "next";
-import { NextSeo } from "next-seo";
-import { useRouter } from "next/router";
-import { ProfileOG } from "og-lib/url-utils";
-import { PageId } from "page-id";
-import { useEffect, useMemo } from "react";
-import { useActiveAccount } from "thirdweb/react";
-import { getAddress, isAddress } from "thirdweb/utils";
-import { Heading, Text } from "tw-components";
-import { getSingleQueryValue } from "utils/router";
-import type { ThirdwebNextPage } from "utils/types";
-import { shortenIfAddress } from "utils/usedapp-external";
-
-type UserPageProps = {
- profileAddress: string;
-};
-
-const UserPage: ThirdwebNextPage = (props: UserPageProps) => {
- const ens = useEns(props.profileAddress);
-
- const router = useRouter();
- // We do this so it doesn't break for users that haven't updated their CLI
-
- // TODO: re-visit, do we still need to do this?
- // eslint-disable-next-line no-restricted-syntax
- useEffect(() => {
- const previousPath = router.asPath.split("/")[2];
- if (
- previousPath !== "[profileAddress]" &&
- props.profileAddress?.startsWith("Qm") &&
- !props.profileAddress?.endsWith(".eth")
- ) {
- router.replace(`/contracts/deploy/${previousPath}`);
- }
- }, [props.profileAddress, router]);
-
- const publisherProfile = usePublisherProfile(ens.data?.address || undefined);
-
- const displayName = shortenIfAddress(
- ens?.data?.ensName || props.profileAddress,
- ).replace("deployer.thirdweb.eth", "thirdweb.eth");
-
- const currentRoute = `${THIRDWEB_DOMAIN}${router.asPath}`.replace(
- "deployer.thirdweb.eth",
- "thirdweb.eth",
- );
-
- const publishedContracts = usePublishedContractsQuery(
- ens.data?.address || undefined,
- );
-
- const mainnetsContractList = useAllContractList(
- ens.data?.address || props.profileAddress,
- { onlyMainnet: true },
- );
-
- const address = useActiveAccount()?.address;
-
- const ogImage = useMemo(() => {
- if (!publisherProfile.data || !publishedContracts.data) {
- return undefined;
- }
-
- return ProfileOG.toUrl({
- displayName,
- bio: publisherProfile.data?.bio,
- avatar: publisherProfile.data?.avatar || undefined,
- publishedCnt: publishedContracts.data?.length.toString(),
- });
- }, [displayName, publishedContracts.data, publisherProfile.data]);
-
- return (
- <>
-
-
-
- {props.profileAddress && (
-
-
-
-
-
- {displayName}
-
- {publisherProfile.data?.bio && (
-
- {publisherProfile.data.bio}
-
- )}
- {publisherProfile.data && (
-
- )}
-
-
- {ens.data?.address === address && publisherProfile.data && (
-
-
-
- )}
-
- )}
-
-
- Published contracts
-
- All contracts published by this wallet
-
-
- {ens.data?.address && (
-
- )}
-
-
-
-
- Deployed contracts
-
- The list of contract instances that this wallet has deployed
- across all mainnets
-
-
-
- {ens.data?.address && (
-
- )}
-
-
- >
- );
-};
-UserPage.getLayout = function getLayout(page, props) {
- return (
-
- {page}
-
- );
-};
-
-// TODO better skeleton needed
-UserPage.fallback = (
-
-
-
-
-
-);
-
-UserPage.pageId = PageId.Profile;
-
-export default UserPage;
-
-export const getStaticProps: GetStaticProps = async (ctx) => {
- const profileAddress = getSingleQueryValue(
- // biome-ignore lint/suspicious/noExplicitAny: FIXME
- ctx.params as any,
- "profileAddress",
- );
-
- if (!profileAddress) {
- return {
- redirect: {
- destination: "/explore",
- permanent: false,
- },
- };
- }
-
- const lowercaseAddress = profileAddress.toLowerCase();
- const checksummedAddress = isAddress(lowercaseAddress)
- ? getAddress(lowercaseAddress)
- : lowercaseAddress;
-
- return {
- props: {
- profileAddress: checksummedAddress,
- },
- };
-};
-
-export const getStaticPaths = async () => {
- return {
- fallback: true,
- paths: [],
- };
-};