"use client" import { Trash2Icon, UploadCloudIcon } from "lucide-react" import { type ComponentProps, useContext, useRef, useState } from "react" import { AuthUIContext } from "../../../lib/auth-ui-provider" import { fileToBase64, resizeAndCropImage } from "../../../lib/image-utils" import { cn, getLocalizedError } from "../../../lib/utils" import type { AuthLocalization } from "../../../localization/auth-localization" import { Button } from "../../ui/button" import { Card } from "../../ui/card" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../ui/dropdown-menu" import { UserAvatar } from "../../user-avatar" import type { SettingsCardClassNames } from "../shared/settings-card" import { SettingsCardFooter } from "../shared/settings-card-footer" import { SettingsCardHeader } from "../shared/settings-card-header" export interface UpdateAvatarCardProps extends ComponentProps { className?: string classNames?: SettingsCardClassNames localization?: AuthLocalization } export function UpdateAvatarCard({ className, classNames, localization, ...props }: UpdateAvatarCardProps) { const { hooks: { useSession }, mutators: { updateUser }, localization: authLocalization, optimistic, avatar, toast, localizeErrors } = useContext(AuthUIContext) localization = { ...authLocalization, ...localization } const { data: sessionData, isPending, refetch } = useSession() const fileInputRef = useRef(null) const [loading, setLoading] = useState(false) const handleAvatarChange = async (file: File) => { if (!sessionData || !avatar) return setLoading(true) const resizedFile = await resizeAndCropImage( file, crypto.randomUUID(), avatar.size, avatar.extension ) let image: string | undefined | null if (avatar.upload) { image = await avatar.upload(resizedFile) } else { image = await fileToBase64(resizedFile) } if (!image) { setLoading(false) return } if (optimistic && !avatar.upload) setLoading(false) try { await updateUser({ image }) await refetch?.() // If a custom storage remover is provided, clean up the old asset if (avatar.upload && avatar.delete && sessionData.user.image) { try { await avatar.delete(sessionData.user.image) } catch (error) { console.error("Failed to delete old avatar:", error) } } } catch (error) { toast({ variant: "error", message: getLocalizedError({ error, localization, localizeErrors }) }) } setLoading(false) } const handleDeleteAvatar = async () => { if (!sessionData) return setLoading(true) try { // If a custom storage remover is provided, attempt to clean up the old asset first if (sessionData.user.image && avatar?.delete) { await avatar.delete(sessionData.user.image) } await updateUser({ image: null }) await refetch?.() } catch (error) { toast({ variant: "error", message: getLocalizedError({ error, localization, localizeErrors }) }) } setLoading(false) } const openFileDialog = () => fileInputRef.current?.click() return ( { const file = e.target.files?.item(0) if (file) handleAvatarChange(file) e.target.value = "" }} />
e.preventDefault()} > {localization.UPLOAD_AVATAR} {sessionData?.user.image && ( {localization.DELETE_AVATAR} )}
) }