diff --git a/apps/dashboard/src/@/actions/updateAccount.ts b/apps/dashboard/src/@/actions/updateAccount.ts index 2b196c4ec2a..0c4d2378d47 100644 --- a/apps/dashboard/src/@/actions/updateAccount.ts +++ b/apps/dashboard/src/@/actions/updateAccount.ts @@ -5,6 +5,7 @@ import { API_SERVER_URL } from "../constants/env"; export async function updateAccount(values: { name?: string; email?: string; + image?: string | null; }) { const token = await getAuthToken(); diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts index 02588436966..cfb6b1c753b 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts @@ -39,6 +39,7 @@ export type Account = { id: string; isStaff: boolean; creatorWalletAddress: string; + image?: string | null; name?: string; email?: string; advancedEnabled: boolean; diff --git a/apps/dashboard/src/app/account/components/AccountHeaderUI.tsx b/apps/dashboard/src/app/account/components/AccountHeaderUI.tsx index e193826c3de..bf1d93da093 100644 --- a/apps/dashboard/src/app/account/components/AccountHeaderUI.tsx +++ b/apps/dashboard/src/app/account/components/AccountHeaderUI.tsx @@ -17,7 +17,7 @@ export type AccountHeaderCompProps = { connectButton: React.ReactNode; teamsAndProjects: Array<{ team: Team; projects: Project[] }>; createProject: (team: Team) => void; - account: Pick | undefined; + account: Pick | undefined; client: ThirdwebClient; }; @@ -41,10 +41,9 @@ export function AccountHeaderDesktopUI(props: AccountHeaderCompProps) { href="/account" className="flex flex-row items-center gap-2 font-normal text-sm" > - {/* TODO - set account Image */} @@ -90,10 +89,9 @@ export function AccountHeaderMobileUI(props: AccountHeaderCompProps) { "flex flex-row items-center gap-2 font-normal text-foreground text-sm", )} > - {/* TODO - set account image */} diff --git a/apps/dashboard/src/app/account/settings/AccountSettingsPage.tsx b/apps/dashboard/src/app/account/settings/AccountSettingsPage.tsx index 549ec3164c0..0e903819d54 100644 --- a/apps/dashboard/src/app/account/settings/AccountSettingsPage.tsx +++ b/apps/dashboard/src/app/account/settings/AccountSettingsPage.tsx @@ -5,6 +5,7 @@ import { updateAccount } from "@/actions/updateAccount"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebClient } from "thirdweb"; +import { upload } from "thirdweb/storage"; import { AccountSettingsPageUI } from "./AccountSettingsPageUI"; export function AccountSettingsPage(props: { @@ -23,9 +24,25 @@ export function AccountSettingsPage(props: {
{ + let uri: string | undefined = undefined; + + if (file) { + // upload to IPFS + uri = await upload({ + client: props.client, + files: [file], + }); + } + + await updateAccount({ + image: uri, + }); + + router.refresh(); + }} account={props.account} updateEmailWithOTP={async (otp) => { const res = await confirmEmailWithOTP(otp); diff --git a/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.stories.tsx b/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.stories.tsx index 2e03f07b000..8c6cf1df2df 100644 --- a/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.stories.tsx +++ b/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.stories.tsx @@ -1,4 +1,5 @@ import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; import type { Meta, StoryObj } from "@storybook/react"; import { useState } from "react"; import { Toaster } from "sonner"; @@ -33,6 +34,8 @@ export const Mobile: Story = { }, }; +const client = getThirdwebClient(); + function Variants() { const [isVerifiedEmail, setIsVerifiedEmail] = useState(true); const [sendEmailFails, setSendEmailFails] = useState(false); @@ -74,6 +77,10 @@ function Variants() { ? new Date().toISOString() : undefined, }} + client={client} + updateAccountAvatar={async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + }} updateEmailWithOTP={async () => { await new Promise((resolve) => setTimeout(resolve, 1000)); if (emailConfirmationFails) { diff --git a/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx b/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx index 1f6c65c700d..3ca02aec757 100644 --- a/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx +++ b/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx @@ -30,6 +30,7 @@ import { InputOTPSlot, } from "@/components/ui/input-otp"; import { useDashboardRouter } from "@/lib/DashboardRouter"; +import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler"; import { cn } from "@/lib/utils"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -39,30 +40,35 @@ import { EllipsisIcon } from "lucide-react"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import type { ThirdwebClient } from "thirdweb"; import { z } from "zod"; import { FileInput } from "../../../components/shared/FileInput"; type MinimalAccount = Pick< Account, - "name" | "email" | "emailConfirmedAt" | "unconfirmedEmail" + "name" | "email" | "emailConfirmedAt" | "unconfirmedEmail" | "image" >; export function AccountSettingsPageUI(props: { account: MinimalAccount; - // TODO - remove hide props these when these fields are functional - hideAvatar?: boolean; hideDeleteAccount?: boolean; sendEmail: (email: string) => Promise; updateName: (name: string) => Promise; updateEmailWithOTP: (otp: string) => Promise; + updateAccountAvatar: (avatar: File | undefined) => Promise; + client: ThirdwebClient; }) { return (
- {!props.hideAvatar && } props.updateName(name)} /> + Promise; + avatar: string | undefined; + client: ThirdwebClient; +}) { + const accountAvatarUrl = resolveSchemeWithErrorHandler({ + client: props.client, + uri: props.avatar, + }); const [avatar, setAvatar] = useState(); - - // TODO - implement const updateAvatarMutation = useMutation({ - mutationFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 3000)); + mutationFn: (_avatar: File | undefined) => { + return props.updateAccountAvatar(_avatar); }, }); function handleSave() { - const promises = updateAvatarMutation.mutateAsync(); + const promises = updateAvatarMutation.mutateAsync(avatar); toast.promise(promises, { success: "Account avatar updated successfully", error: "Failed to update account avatar", @@ -118,6 +130,7 @@ function AccountAvatarFormControl() { setValue={setAvatar} className="w-20 rounded-full lg:w-28" disableHelperText + fileUrl={accountAvatarUrl} />
diff --git a/apps/dashboard/src/app/components/Header/SecondaryNav/account-button.client.tsx b/apps/dashboard/src/app/components/Header/SecondaryNav/account-button.client.tsx index d3a327e1ca3..dcfde81ebb8 100644 --- a/apps/dashboard/src/app/components/Header/SecondaryNav/account-button.client.tsx +++ b/apps/dashboard/src/app/components/Header/SecondaryNav/account-button.client.tsx @@ -18,7 +18,7 @@ import type { ThirdwebClient } from "thirdweb"; export function AccountButton(props: { logout: () => void; connectButton: React.ReactNode; - account?: Pick; + account?: Pick; client: ThirdwebClient; }) { const { setTheme, theme } = useTheme(); @@ -35,11 +35,10 @@ export function AccountButton(props: { > {/* Don't remove the div */}
- {/* TODO - set account image */}
diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/NebulaAccountButton.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/NebulaAccountButton.tsx index 6c7ca7d421d..9ffc495960a 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/NebulaAccountButton.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/NebulaAccountButton.tsx @@ -61,7 +61,7 @@ export function NebulaAccountButton(props: {
@@ -88,7 +88,7 @@ export function NebulaAccountButton(props: {
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamSettingsSidebar.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamSettingsSidebar.tsx index 0dbd0e6b035..e435c2da92b 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamSettingsSidebar.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamSettingsSidebar.tsx @@ -7,7 +7,7 @@ import { getTeamSettingsLinks } from "./getTeamSettingsLinks"; export function TeamSettingsSidebar(props: { team: Team; - account: Pick | undefined; + account: Pick | undefined; client: ThirdwebClient; }) { const teamLinks = getTeamSettingsLinks(props.team.slug); @@ -34,7 +34,7 @@ export function TeamSettingsSidebar(props: { team={props.team} titleAvatarIcon={{ id: props.account?.id, - src: "", // TODO - set account image + src: props.account?.image || "", }} client={props.client} /> @@ -60,7 +60,7 @@ function RenderLinkGroup(props: {
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPage.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPage.tsx index 169d569adb9..8316867373c 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPage.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPage.tsx @@ -1,24 +1,22 @@ "use client"; import type { Team } from "@/api/team"; -import { useThirdwebClient } from "@/constants/thirdweb.client"; -import { getThirdwebClient } from "@/constants/thirdweb.server"; import { useDashboardRouter } from "@/lib/DashboardRouter"; +import type { ThirdwebClient } from "thirdweb"; import { upload } from "thirdweb/storage"; import { TeamGeneralSettingsPageUI } from "./TeamGeneralSettingsPageUI"; import { updateTeam } from "./updateTeam"; export function TeamGeneralSettingsPage(props: { team: Team; - authToken: string; + client: ThirdwebClient; }) { const router = useDashboardRouter(); - const client = useThirdwebClient(); return ( { await updateTeam({ teamId: props.team.id, @@ -38,7 +36,7 @@ export function TeamGeneralSettingsPage(props: { if (file) { // upload to IPFS uri = await upload({ - client: getThirdwebClient(props.authToken), + client: props.client, files: [file], }); } diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPageUI.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPageUI.tsx index 63fcdc3a034..6866d0a566f 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPageUI.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/general/TeamGeneralSettingsPageUI.tsx @@ -188,7 +188,7 @@ function TeamAvatarFormControl(props: { disabled: false, isPending: updateTeamAvatarMutation.isPending, }} - noPermissionText={undefined} // TODO + noPermissionText={undefined} errorText={undefined} >
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/page.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/page.tsx index 1837c412567..78663689bbc 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/page.tsx @@ -1,4 +1,5 @@ import { getTeamBySlug } from "@/api/team"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; import { notFound } from "next/navigation"; import { getAuthToken } from "../../../../../api/lib/getAuthToken"; import { TeamGeneralSettingsPage } from "./general/TeamGeneralSettingsPage"; @@ -14,5 +15,7 @@ export default async function Page(props: { notFound(); } - return ; + return ( + + ); } diff --git a/apps/dashboard/src/app/team/components/TeamHeader/TeamSelectionUI.tsx b/apps/dashboard/src/app/team/components/TeamHeader/TeamSelectionUI.tsx index edd6586fe5e..59b862da670 100644 --- a/apps/dashboard/src/app/team/components/TeamHeader/TeamSelectionUI.tsx +++ b/apps/dashboard/src/app/team/components/TeamHeader/TeamSelectionUI.tsx @@ -19,7 +19,7 @@ export function TeamSelectionUI(props: { currentTeam: Team | undefined; teamsAndProjects: Array<{ team: Team; projects: Project[] }>; upgradeTeamLink: string | undefined; - account: Pick | undefined; + account: Pick | undefined; client: ThirdwebClient; }) { const { setHoveredTeam, currentTeam, teamsAndProjects } = props; @@ -52,9 +52,8 @@ export function TeamSelectionUI(props: { asChild > - {/* TODO set Image src */}