"use client"; import { useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Me, UpdateProfileSchema, uploadAvatar, uploadBanner } from "@/lib/api"; import { updateProfile } from "@/app/actions/profile"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardFooter } from "@/components/ui/card"; import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { UserAvatar } from "@/components/user-avatar"; import { Camera, ImagePlus, Loader2 } from "lucide-react"; interface EditProfileFormProps { currentUser: Me; token: string; } export function EditProfileForm({ currentUser, token }: EditProfileFormProps) { const router = useRouter(); const avatarInputRef = useRef(null); const bannerInputRef = useRef(null); const [avatarSrc, setAvatarSrc] = useState(currentUser.avatarUrl ?? null); const [bannerSrc, setBannerSrc] = useState(currentUser.headerUrl ?? null); const [uploadingAvatar, setUploadingAvatar] = useState(false); const [uploadingBanner, setUploadingBanner] = useState(false); async function handleAvatarChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; setUploadingAvatar(true); try { const updated = await uploadAvatar(file, token); setAvatarSrc(updated.avatarUrl ?? null); router.refresh(); toast.success("Avatar updated"); } catch { toast.error("Failed to upload avatar"); } finally { setUploadingAvatar(false); e.target.value = ""; } } async function handleBannerChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; setUploadingBanner(true); try { const updated = await uploadBanner(file, token); setBannerSrc(updated.headerUrl ?? null); router.refresh(); toast.success("Banner updated"); } catch { toast.error("Failed to upload banner"); } finally { setUploadingBanner(false); e.target.value = ""; } } const form = useForm>({ resolver: zodResolver(UpdateProfileSchema), defaultValues: { displayName: currentUser.displayName ?? undefined, bio: currentUser.bio ?? undefined, customCss: currentUser.customCss ?? undefined, }, }); async function onSubmit(values: z.infer) { toast.info("Updating your profile..."); try { await updateProfile(currentUser.username, values); toast.success("Profile updated successfully!"); } catch (err) { toast.error(`Failed to update profile. ${err}`); } } return (
{/* Banner */}

Banner

!uploadingBanner && bannerInputRef.current?.click()} > {bannerSrc ? ( Banner ) : (
No banner
)}
{uploadingBanner ? ( ) : ( )}
{/* Avatar */}

Avatar

!uploadingAvatar && avatarInputRef.current?.click()} >
{uploadingAvatar ? ( ) : ( )}

Click to upload · JPEG, PNG, GIF, WebP, AVIF · max 5 MB

( Display Name )} /> ( Bio