import "./ProfilePage.css"; import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useLocation, useNavigate, useParams } from "react-router-dom"; import { encodeTLV, encodeTLVEntries, EventKind, MetadataCache, NostrLink, NostrPrefix, TLVEntryType, tryParseNostrLink, } from "@snort/system"; import { LNURL, fetchNip05Pubkey } from "@snort/shared"; import { useUserProfile } from "@snort/system-react"; import { findTag, getLinkReactions, unwrap } from "SnortUtils"; import Note from "Element/Event/Note"; import { Tab, TabElement } from "Element/Tabs"; import Icon from "Icons/Icon"; import useFollowsFeed from "Feed/FollowsFeed"; import useProfileBadges from "Feed/BadgesFeed"; import useModeration from "Hooks/useModeration"; import FollowButton from "Element/User/FollowButton"; import { parseId, hexToBech32 } from "SnortUtils"; import Avatar from "Element/User/Avatar"; import Timeline from "Element/Feed/Timeline"; import Text from "Element/Text"; import SendSats from "Element/SendSats"; import Nip05 from "Element/User/Nip05"; import Copy from "Element/Copy"; import ProfileImage from "Element/User/ProfileImage"; import BlockList from "Element/User/BlockList"; import MutedList from "Element/User/MutedList"; import FollowsList from "Element/User/FollowListBase"; import IconButton from "Element/IconButton"; import FollowsYou from "Element/User/FollowsYou"; import QrCode from "Element/QrCode"; import Modal from "Element/Modal"; import BadgeList from "Element/User/BadgeList"; import { ProxyImg } from "Element/ProxyImg"; import useHorizontalScroll from "Hooks/useHorizontalScroll"; import { EmailRegex } from "Const"; import useLogin from "Hooks/useLogin"; import { ZapTarget } from "Zapper"; import { useStatusFeed } from "Feed/StatusFeed"; import { SpotlightMediaModal } from "Element/SpotlightMedia"; import ProfileTab, { BookMarksTab, FollowersTab, FollowsTab, ProfileTabType, RelaysTab, ZapsProfileTab, } from "Pages/Profile/ProfileTab"; import DisplayName from "Element/User/DisplayName"; import { UserWebsiteLink } from "Element/User/UserWebsiteLink"; import { useMuteList, usePinList } from "Hooks/useLists"; import messages from "../messages"; interface ProfilePageProps { id?: string; state?: MetadataCache; } export default function ProfilePage({ id: propId, state }: ProfilePageProps) { const params = useParams(); const location = useLocation(); const profileState = (location.state as MetadataCache | undefined) || state; const navigate = useNavigate(); const [id, setId] = useState(profileState?.pubkey); const [relays, setRelays] = useState>(); const user = useUserProfile(profileState ? undefined : id) || profileState; const login = useLogin(); const loginPubKey = login.publicKey; const isMe = loginPubKey === id; const [showLnQr, setShowLnQr] = useState(false); const [showProfileQr, setShowProfileQr] = useState(false); const [modalImage, setModalImage] = useState(""); const aboutText = user?.about || ""; const npub = !id?.startsWith(NostrPrefix.PublicKey) ? hexToBech32(NostrPrefix.PublicKey, id || undefined) : id; const lnurl = (() => { try { return new LNURL(user?.lud16 || user?.lud06 || ""); } catch { // ignored } })(); const showBadges = login.preferences.showBadges ?? false; const showStatus = login.preferences.showStatus ?? true; // feeds const { blocked } = useModeration(); const pinned = usePinList(id); const muted = useMuteList(id); const badges = useProfileBadges(showBadges ? id : undefined); const follows = useFollowsFeed(id); const status = useStatusFeed(showStatus ? id : undefined, true); // tabs const [tab, setTab] = useState(ProfileTab.Notes); const optionalTabs = [ProfileTab.Zaps, ProfileTab.Relays, ProfileTab.Bookmarks, ProfileTab.Muted].filter(a => unwrap(a), ) as Tab[]; const horizontalScroll = useHorizontalScroll(); useEffect(() => { if (!id) { const resolvedId = propId || params.id; if (resolvedId?.match(EmailRegex)) { const [name, domain] = resolvedId.split("@"); fetchNip05Pubkey(name, domain).then(a => { setId(a); }); } else { const nav = tryParseNostrLink(resolvedId ?? ""); if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) { setId(nav.id); setRelays(nav.relays); } else { setId(parseId(resolvedId ?? "")); } } } setTab(ProfileTab.Notes); }, [id, propId, params]); function musicStatus() { if (!status.music) return; const link = findTag(status.music, "r"); const cover = findTag(status.music, "cover"); const inner = () => { return (
{cover && } 🎵 {unwrap(status.music).content}
); }; if (link) { return ( {inner()} ); } return inner(); } function username() { return ( <>

{user?.nip05 && }
{showBadges && } {showStatus && <>{musicStatus()}}
{links()}
); } function links() { return ( <> {lnurl && (
setShowLnQr(true)}> {lnurl.name}
)} setShowLnQr(false)} /> ); } function bio() { if (!id) return null; return ( aboutText.length > 0 && (
) ); } function tabContent() { if (!id) return null; switch (tab.value) { case ProfileTabType.NOTES: return ( <> {pinned .filter(a => a.kind === EventKind.TextNote) .map(n => { return ( ); })} ); case ProfileTabType.ZAPS: { return ; } case ProfileTabType.FOLLOWS: { if (isMe) { return ; } else { return ; } } case ProfileTabType.FOLLOWERS: { return ; } case ProfileTabType.MUTED: { return a.id)} />; } case ProfileTabType.BLOCKED: { return ; } case ProfileTabType.RELAYS: { return ; } case ProfileTabType.BOOKMARKS: { return ; } } } function avatar() { return (
setModalImage(user?.picture || "")} className="pointer" />
{renderIcons()} {!isMe && id && }
); } function renderIcons() { if (!id) return; const link = encodeTLV(NostrPrefix.Profile, id); return ( <> setShowProfileQr(true)} icon={{ name: "qr", size: 16 }} /> {showProfileQr && ( setShowProfileQr(false)}> )} {isMe ? ( <> ) : ( <> {lnurl && setShowLnQr(true)} icon={{ name: "zap", size: 16 }} />} {loginPubKey && !login.readonly && ( <> navigate( `/messages/${encodeTLVEntries("chat4" as NostrPrefix, { type: TLVEntryType.Author, length: 32, value: id, })}`, ) } icon={{ name: "envelope", size: 16 }} /> )} )} ); } function userDetails() { if (!id) return; return (
{username()} {bio()}
); } function renderTab(v: Tab) { return ; } const w = window.document.querySelector(".page")?.clientWidth; return ( <>
{user?.banner && ( setModalImage(user.banner || "")} /> )}
{avatar()} {userDetails()}
{[ProfileTab.Notes, ProfileTab.Followers, ProfileTab.Follows].map(renderTab)} {optionalTabs.map(renderTab)} {isMe && blocked.length > 0 && renderTab(ProfileTab.Blocked)}
{tabContent()}
{modalImage && setModalImage("")} images={[modalImage]} idx={0} />} ); }