diff --git a/.changeset/blue-bees-ring.md b/.changeset/blue-bees-ring.md new file mode 100644 index 00000000000..2fd7b3b5152 --- /dev/null +++ b/.changeset/blue-bees-ring.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +default account components to not retry on failure diff --git a/apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx b/apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx index e87e2410f70..43ea0dcd758 100644 --- a/apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx +++ b/apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx @@ -25,10 +25,6 @@ export async function getCodeHtml(code: string, lang: BundledLanguage) { }).catch((e) => { console.error(e); console.error("Failed to format code"); - console.log({ - code, - lang, - }); return code; }) : code; diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx index 4747892fe29..3c54a8870b5 100644 --- a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx +++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/ProfileUI.tsx @@ -1,62 +1,20 @@ 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 { ProfileHeader } from "./components/profile-header"; import { PublishedContracts } from "./components/published-contracts"; export function ProfileUI(props: { profileAddress: string; ensName: string | undefined; - publisherProfile: ProfileMetadata | null; - showEditProfile: boolean; }) { - const { profileAddress, ensName, publisherProfile, showEditProfile } = props; - - const displayName = shortenIfAddress(ensName || profileAddress).replace( - "deployer.thirdweb.eth", - "thirdweb.eth", - ); + const { profileAddress, ensName } = props; return (
- {/* Header */} -
-
- -
-

- {displayName} -

- - {publisherProfile?.bio && ( -

- {publisherProfile.bio} -

- )} - -
- {publisherProfile && ( - - )} -
-
-
- - {showEditProfile && ( -
- {publisherProfile && ( - - )} -
- )} -
- +
diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx index 9ba2bd49322..c051cfbd979 100644 --- a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx +++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/PublishedContractTable.tsx @@ -11,8 +11,8 @@ import { } 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 { replaceDeployerAddress } from "lib/publisher-utils"; import { replaceIpfsUrl } from "lib/sdk"; import { ShieldCheckIcon } from "lucide-react"; import Link from "next/link"; diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/profile-header.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/profile-header.tsx new file mode 100644 index 00000000000..36e6d6d2b6b --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/components/profile-header.tsx @@ -0,0 +1,47 @@ +"use client"; + +import { Skeleton } from "@/components/ui/skeleton"; +import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { replaceDeployerAddress } from "lib/publisher-utils"; +import { + AccountAddress, + AccountAvatar, + AccountBlobbie, + AccountName, + AccountProvider, +} from "thirdweb/react"; +import { shortenIfAddress } from "utils/usedapp-external"; + +export function ProfileHeader(props: { profileAddress: string }) { + const client = useThirdwebClient(); + return ( + +
+
+ } + fallbackComponent={ + + } + /> +
+

+ + shortenIfAddress(replaceDeployerAddress(addr)) + } + /> + } + loadingComponent={} + formatFn={(name) => replaceDeployerAddress(name)} + /> +

+
+
+
+
+ ); +} diff --git a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx index 59fe2a9efa8..f9c5ef1a707 100644 --- a/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/profile/[addressOrEns]/page.tsx @@ -1,8 +1,6 @@ -import { getActiveAccountCookie } from "@/constants/cookie"; -import { fetchPublisherProfile } from "components/contract-components/fetch-contracts-with-versions"; +import { replaceDeployerAddress } from "lib/publisher-utils"; 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"; @@ -16,22 +14,15 @@ type PageProps = { 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); - return ( ); } @@ -45,23 +36,11 @@ export async function generateMetadata(props: PageProps): Promise { } const displayName = shortenIfAddress( - resolvedInfo.ensName || resolvedInfo.address, - ).replace("deployer.thirdweb.eth", "thirdweb.eth"); + replaceDeployerAddress(resolvedInfo.ensName || resolvedInfo.address), + ); return { title: displayName, description: `Visit ${displayName}'s profile. See their published contracts and deploy them in one click.`, }; } - -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)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx index cbbf996510c..9f9d25d4428 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx @@ -1,6 +1,8 @@ -import { fetchPublisherProfile } from "components/contract-components/fetch-contracts-with-versions"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; import { format } from "date-fns/format"; +import { resolveEns } from "lib/ens"; import { correctAndUniqueLicenses } from "lib/licenses"; +import { getSocialProfiles } from "thirdweb/social"; import { getPublishedContractsWithPublisherMapping } from "../utils/getPublishedContractsWithPublisherMapping"; import { publishedContractOGImageTemplate } from "../utils/publishedContractOGImageTemplate"; @@ -18,20 +20,33 @@ export default async function Image(props: { version: string; }; }) { + const client = getThirdwebClient(); const { publisher, contract_id } = props.params; - const [publishedContracts, publisherProfile] = await Promise.all([ + const [publishedContracts, socialProfiles] = await Promise.all([ getPublishedContractsWithPublisherMapping({ publisher: publisher, contract_id: contract_id, }), - fetchPublisherProfile(publisher), + getSocialProfiles({ + address: (await resolveEns(publisher)).address || publisher, + client, + }), ]); if (!publishedContracts) { return null; } + const publisherProfile = (() => { + const name = socialProfiles.find((p) => p.name)?.name || publisher; + const avatar = socialProfiles.find((p) => p.avatar)?.avatar; + return { + name, + avatar, + }; + })(); + const publishedContract = publishedContracts.find((p) => p.version === props.params.version) || publishedContracts[0]; diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx index e3929c14c8b..ef1ca2fbf21 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/opengraph-image.tsx @@ -1,6 +1,8 @@ -import { fetchPublisherProfile } from "components/contract-components/fetch-contracts-with-versions"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; import { format } from "date-fns/format"; +import { resolveEns } from "lib/ens"; import { correctAndUniqueLicenses } from "lib/licenses"; +import { getSocialProfiles } from "thirdweb/social"; import { getPublishedContractsWithPublisherMapping } from "./utils/getPublishedContractsWithPublisherMapping"; import { publishedContractOGImageTemplate } from "./utils/publishedContractOGImageTemplate"; @@ -17,16 +19,29 @@ export default async function Image(props: { contract_id: string; }; }) { + const client = getThirdwebClient(); const { publisher, contract_id } = props.params; - const [publishedContracts, publisherProfile] = await Promise.all([ + const [publishedContracts, socialProfiles] = await Promise.all([ getPublishedContractsWithPublisherMapping({ publisher: publisher, contract_id: contract_id, }), - fetchPublisherProfile(publisher), + getSocialProfiles({ + address: (await resolveEns(publisher)).address || publisher, + client, + }), ]); + const publisherProfile = (() => { + const name = socialProfiles.find((p) => p.name)?.name || publisher; + const avatar = socialProfiles.find((p) => p.avatar)?.avatar; + return { + name, + avatar, + }; + })(); + if (!publishedContracts) { return null; } 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 aa2a7033a29..3a166d1be5f 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 @@ -1,14 +1,11 @@ import { getThirdwebClient } from "@/constants/thirdweb.server"; -import type { ProfileMetadata } from "constants/schemas"; import { isAddress } from "thirdweb"; import { fetchDeployMetadata } from "thirdweb/contract"; import { resolveAddress } from "thirdweb/extensions/ens"; import { getContractPublisher, getPublishedContractVersions, - getPublisherProfileUri, } from "thirdweb/extensions/thirdweb"; -import { download } from "thirdweb/storage"; export function mapThirdwebPublisher(publisher: string) { if (publisher === "thirdweb.eth") { @@ -18,34 +15,6 @@ export function mapThirdwebPublisher(publisher: string) { return publisher; } -export async function fetchPublisherProfile(publisherAddress: string) { - const client = getThirdwebClient(); - - const profileUri = await getPublisherProfileUri({ - contract: getContractPublisher(client), - publisher: isAddress(publisherAddress) - ? publisherAddress - : await resolveAddress({ - client, - name: mapThirdwebPublisher(publisherAddress), - }), - }); - - if (!profileUri) { - return null; - } - - try { - const res = await download({ - client, - uri: profileUri, - }); - return res.json() as Promise; - } catch { - return null; - } -} - export async function fetchPublishedContractVersions( publisherAddress: string, contractId: string, diff --git a/apps/dashboard/src/components/contract-components/hooks.ts b/apps/dashboard/src/components/contract-components/hooks.ts index e6ebbf1eedf..56a83bf6f85 100644 --- a/apps/dashboard/src/components/contract-components/hooks.ts +++ b/apps/dashboard/src/components/contract-components/hooks.ts @@ -12,32 +12,10 @@ import { isAddress } from "thirdweb/utils"; import { type PublishedContractWithVersion, fetchPublishedContractVersions, - fetchPublisherProfile, } from "./fetch-contracts-with-versions"; import { fetchPublishedContracts } from "./fetchPublishedContracts"; import { fetchPublishedContractsFromDeploy } from "./fetchPublishedContractsFromDeploy"; -function publisherProfileQuery(publisherAddress?: string) { - return queryOptions({ - queryKey: ["releaser-profile", publisherAddress], - queryFn: () => { - if (!publisherAddress) { - throw new Error("publisherAddress is not defined"); - } - return fetchPublisherProfile(publisherAddress); - }, - enabled: !!publisherAddress, - // 24h - gcTime: 60 * 60 * 24 * 1000, - // 1h - staleTime: 60 * 60 * 1000, - }); -} - -export function usePublisherProfile(publisherAddress?: string) { - return useQuery(publisherProfileQuery(publisherAddress)); -} - export function useAllVersions( publisherAddress: string | undefined, contractId: string | undefined, diff --git a/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx b/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx deleted file mode 100644 index 2538de3f19a..00000000000 --- a/apps/dashboard/src/components/contract-components/publisher/PublisherSocials.tsx +++ /dev/null @@ -1,137 +0,0 @@ -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"; -import { MediumIcon } from "components/icons/brand-icons/MediumIcon"; -import { MetaIcon } from "components/icons/brand-icons/MetaIcon"; -import { RedditIcon } from "components/icons/brand-icons/RedditIcon"; -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 { hostnameEndsWith } from "../../../utils/url"; - -const TRACKING_CATEGORY = "releaser-header"; - -export const PublisherSocials: React.FC<{ - publisherProfile: ProfileMetadata; -}> = ({ publisherProfile }) => ( -
- {publisherProfile.twitter && ( - - )} - - {publisherProfile.discord && ( - - )} - - {publisherProfile.github && ( - - )} - - {publisherProfile.website && ( - - )} - - {publisherProfile.medium && ( - - )} - - {publisherProfile.telegram && ( - - )} - - {publisherProfile.facebook && ( - - )} - - {publisherProfile.reddit && ( - - )} - - {publisherProfile.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 deleted file mode 100644 index 8664c42ec20..00000000000 --- a/apps/dashboard/src/components/contract-components/publisher/edit-profile.tsx +++ /dev/null @@ -1,242 +0,0 @@ -"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, - SheetHeader, - SheetTitle, - SheetTrigger, -} from "@/components/ui/sheet"; -import { Textarea } from "@/components/ui/textarea"; -import { useThirdwebClient } from "@/constants/thirdweb.client"; -import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler"; -import { useQueryClient } from "@tanstack/react-query"; -import { FileInput } from "components/shared/FileInput"; -import { - DASHBOARD_ENGINE_RELAYER_URL, - DASHBOARD_FORWARDER_ADDRESS, -} from "constants/misc"; -import type { ProfileMetadata, ProfileMetadataInput } from "constants/schemas"; -import { useTrack } from "hooks/analytics/useTrack"; -import { useImageFileOrUrl } from "hooks/useImageFileOrUrl"; -import { EditIcon } from "lucide-react"; -import { useId, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { - getContractPublisher, - setPublisherProfileUri, -} from "thirdweb/extensions/thirdweb"; -import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react"; -import { upload } from "thirdweb/storage"; -import { MaskedAvatar } from "tw-components/masked-avatar"; - -interface EditProfileProps { - publisherProfile: ProfileMetadata; -} - -export const EditProfile: React.FC = ({ - publisherProfile, -}) => { - const FORM_ID = useId(); - const client = useThirdwebClient(); - const { - register, - handleSubmit, - setValue, - watch, - formState: { errors }, - } = useForm({ - defaultValues: publisherProfile, - values: publisherProfile, - }); - - const imageUrl = useImageFileOrUrl(watch("avatar")); - - const address = useActiveAccount()?.address; - const queryClient = useQueryClient(); - const sendTx = useSendAndConfirmTransaction({ - gasless: { - experimentalChainlessSupport: true, - provider: "engine", - relayerUrl: DASHBOARD_ENGINE_RELAYER_URL, - relayerForwarderAddress: DASHBOARD_FORWARDER_ADDRESS, - }, - }); - const trackEvent = useTrack(); - const [open, setOpen] = useState(false); - - return ( - - - - - - - Edit your profile - -
{ - if (!address) { - return; - } - trackEvent({ - category: "profile", - action: "edit", - label: "attempt", - }); - - const tx = setPublisherProfileUri({ - contract: getContractPublisher(client), - asyncParams: async () => { - return { - publisher: address, - uri: await upload({ - files: [d], - client, - }), - }; - }, - }); - - const promise = sendTx.mutateAsync(tx, { - onSuccess: async () => { - await queryClient.invalidateQueries({ - queryKey: ["releaser-profile", address], - }); - trackEvent({ - category: "profile", - action: "edit", - label: "success", - }); - setOpen(false); - }, - onError: (error) => { - trackEvent({ - category: "profile", - action: "edit", - label: "error", - error, - }); - }, - }); - - toast.promise(promise, { - success: "Profile updated successfully", - error: "Failed to update profile", - }); - })} - > - - setValue("avatar", file)} - className="max-w-[250px] rounded border border-border transition-all" - renderPreview={(fileUrl) => ( - - )} - previewMaxWidth="200px" - /> - - - -