import Uploader from '@/components/PostEditor/Uploader' import ProfileBanner from '@/components/ProfileBanner' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import { createProfileDraftEvent } from '@/lib/draft-event' import { formatError } from '@/lib/error' import { generateImageByPubkey } from '@/lib/pubkey' import { isEmail } from '@/lib/utils' import { useSecondaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' import { Loader, Upload } from 'lucide-react' import { forwardRef, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => { const { t } = useTranslation() const { pop } = useSecondaryPage() const { account, profile, profileEvent, publish, updateProfileEvent } = useNostr() const [banner, setBanner] = useState('') const [avatar, setAvatar] = useState('') const [username, setUsername] = useState('') const [about, setAbout] = useState('') const [website, setWebsite] = useState('') const [nip05, setNip05] = useState('') const [nip05Error, setNip05Error] = useState('') const [lightningAddress, setLightningAddress] = useState('') const [lightningAddressError, setLightningAddressError] = useState('') const [hasChanged, setHasChanged] = useState(false) const [saving, setSaving] = useState(false) const [uploadingBanner, setUploadingBanner] = useState(false) const [uploadingAvatar, setUploadingAvatar] = useState(false) const defaultImage = useMemo( () => (account ? generateImageByPubkey(account.pubkey) : undefined), [account] ) useEffect(() => { if (profile) { setBanner(profile.banner ?? '') setAvatar(profile.avatar ?? '') setUsername(profile.original_username ?? '') setAbout(profile.about ?? '') setWebsite(profile.website ?? '') setNip05(profile.nip05 ?? '') setLightningAddress(profile.lightningAddress || '') } else { setBanner('') setAvatar('') setUsername('') setAbout('') setWebsite('') setNip05('') setLightningAddress('') } }, [profile]) if (!account || !profile) return null const save = async () => { if (nip05 && !isEmail(nip05)) { setNip05Error(t('Invalid NIP-05 address')) return } const oldProfileContent = profileEvent ? JSON.parse(profileEvent.content) : {} const newProfileContent = { ...oldProfileContent, display_name: username, displayName: username, name: username, about, website, nip05, banner, picture: avatar } if (lightningAddress) { if (isEmail(lightningAddress)) { newProfileContent.lud16 = lightningAddress } else if (lightningAddress.startsWith('lnurl')) { newProfileContent.lud06 = lightningAddress } else { setLightningAddressError(t('Invalid Lightning Address')) return } } else { delete newProfileContent.lud16 } setSaving(true) setHasChanged(false) const profileDraftEvent = createProfileDraftEvent( JSON.stringify(newProfileContent), profileEvent?.tags ) try { const newProfileEvent = await publish(profileDraftEvent) await updateProfileEvent(newProfileEvent) setSaving(false) pop() } catch (error) { const errors = formatError(error) errors.forEach((err) => { toast.error(`${t('Failed to save profile')}: ${err}`, { duration: 10_000 }) }) } } const onBannerUploadSuccess = ({ url }: { url: string }) => { setBanner(url) setHasChanged(true) } const onAvatarUploadSuccess = ({ url }: { url: string }) => { setAvatar(url) setHasChanged(true) } const controls = (
) return (
setUploadingBanner(true)} onUploadEnd={() => setUploadingBanner(false)} className="w-full relative cursor-pointer" >
{uploadingBanner ? : }
setUploadingAvatar(true)} onUploadEnd={() => setUploadingAvatar(false)} className="w-24 h-24 absolute bottom-0 left-4 translate-y-1/2 border-4 border-background cursor-pointer rounded-full" >
{uploadingAvatar ? : }
{ setUsername(e.target.value) setHasChanged(true) }} />