import Collapsible from '@/components/Collapsible' import FollowButton from '@/components/FollowButton' import Nip05 from '@/components/Nip05' import NpubQrCode from '@/components/NpubQrCode' import ProfileAbout from '@/components/ProfileAbout' import ProfileBanner from '@/components/ProfileBanner' import ProfileOptions from '@/components/ProfileOptions' import ProfileZapButton from '@/components/ProfileZapButton' import PubkeyCopy from '@/components/PubkeyCopy' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { useFetchFollowings, useFetchProfile } from '@/hooks' import { toMuteList, toProfileEditor } from '@/lib/link' import { SecondaryPageLink, useSecondaryPage } from '@/PageManager' import { useMuteList } from '@/providers/MuteListProvider' import { useNostr } from '@/providers/NostrProvider' import client from '@/services/client.service' import { Link, Zap } from 'lucide-react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import NotFound from '../NotFound' import SearchInput from '../SearchInput' import { SimpleUserAvatar } from '../UserAvatar' import FollowedBy from './FollowedBy' import Followings from './Followings' import ProfileFeed from './ProfileFeed' import Relays from './Relays' export default function Profile({ id }: { id?: string }) { const { t } = useTranslation() const { push } = useSecondaryPage() const { profile, isFetching } = useFetchProfile(id) const { pubkey: accountPubkey } = useNostr() const { mutePubkeySet } = useMuteList() const [searchInput, setSearchInput] = useState('') const [debouncedInput, setDebouncedInput] = useState(searchInput) const { followings } = useFetchFollowings(profile?.pubkey) const isFollowingYou = useMemo(() => { return ( !!accountPubkey && accountPubkey !== profile?.pubkey && followings.includes(accountPubkey) ) }, [followings, profile, accountPubkey]) const [topContainerHeight, setTopContainerHeight] = useState(0) const isSelf = accountPubkey === profile?.pubkey const [topContainer, setTopContainer] = useState(null) const topContainerRef = useCallback((node: HTMLDivElement | null) => { if (node) { setTopContainer(node) } }, []) useEffect(() => { const handler = setTimeout(() => { setDebouncedInput(searchInput.trim()) }, 1000) return () => { clearTimeout(handler) } }, [searchInput]) useEffect(() => { if (!profile?.pubkey) return const forceUpdateCache = async () => { await Promise.all([ client.forceUpdateRelayListEvent(profile.pubkey), client.fetchProfile(profile.pubkey, true) ]) } forceUpdateCache() }, [profile?.pubkey]) useEffect(() => { if (!topContainer) return const checkHeight = () => { setTopContainerHeight(topContainer.scrollHeight) } checkHeight() const observer = new ResizeObserver(() => { checkHeight() }) observer.observe(topContainer) return () => { observer.disconnect() } }, [topContainer]) if (!profile && isFetching) { return ( <>
) } if (!profile) return const { banner, username, about, pubkey, website, lightningAddress, emojis } = profile return ( <>
{isSelf ? ( ) : ( <> {!!lightningAddress && } )}
{username}
{isFollowingYou && (
{t('Follows you')}
)}
{lightningAddress && (
{lightningAddress}
)}
{website && ( )}
{isSelf && ( {mutePubkeySet.size}
{t('Muted')}
)}
{!isSelf && }
setSearchInput(e.target.value)} placeholder={t('Search')} />
) }