import "./ProfilePage.css"; import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import { encodeTLV, encodeTLVEntries, EventKind, HexKey, NostrPrefix, TLVEntryType, tryParseNostrLink, } from "@snort/system"; import { LNURL } from "@snort/shared"; import { useUserProfile } from "@snort/system-react"; import { getReactions, unwrap } from "SnortUtils"; import { formatShort } from "Number"; import Note from "Element/Note"; import Bookmarks from "Element/Bookmarks"; import RelaysMetadata from "Element/RelaysMetadata"; import { Tab, TabElement } from "Element/Tabs"; import Icon from "Icons/Icon"; import useMutedFeed from "Feed/MuteList"; import useRelaysFeed from "Feed/RelaysFeed"; import usePinnedFeed from "Feed/PinnedFeed"; import useBookmarkFeed from "Feed/BookmarkFeed"; import useFollowersFeed from "Feed/FollowersFeed"; import useFollowsFeed from "Feed/FollowsFeed"; import useProfileBadges from "Feed/BadgesFeed"; import useModeration from "Hooks/useModeration"; import useZapsFeed from "Feed/ZapsFeed"; import { default as ZapElement } from "Element/Zap"; import FollowButton from "Element/FollowButton"; import { parseId, hexToBech32 } from "SnortUtils"; import Avatar from "Element/Avatar"; import Timeline from "Element/Timeline"; import Text from "Element/Text"; import SendSats from "Element/SendSats"; import Nip05 from "Element/Nip05"; import Copy from "Element/Copy"; import ProfileImage from "Element/ProfileImage"; import BlockList from "Element/BlockList"; import MutedList from "Element/MutedList"; import FollowsList from "Element/FollowListBase"; import IconButton from "Element/IconButton"; import FollowsYou from "Element/FollowsYou"; import QrCode from "Element/QrCode"; import Modal from "Element/Modal"; import BadgeList from "Element/BadgeList"; import { ProxyImg } from "Element/ProxyImg"; import useHorizontalScroll from "Hooks/useHorizontalScroll"; import { EmailRegex } from "Const"; import { getNip05PubKey } from "Pages/LoginPage"; import useLogin from "Hooks/useLogin"; import messages from "./messages"; import { System } from "index"; const NOTES = 0; const REACTIONS = 1; const FOLLOWERS = 2; const FOLLOWS = 3; const ZAPS = 4; const MUTED = 5; const BLOCKED = 6; const RELAYS = 7; const BOOKMARKS = 8; function ZapsProfileTab({ id }: { id: HexKey }) { const zaps = useZapsFeed(id); const zapsTotal = zaps.reduce((acc, z) => acc + z.amount, 0); return (
{zaps.map(z => ( ))}
); } function FollowersTab({ id }: { id: HexKey }) { const followers = useFollowersFeed(id); return ; } function FollowsTab({ id }: { id: HexKey }) { const follows = useFollowsFeed(id); return ; } function RelaysTab({ id }: { id: HexKey }) { const relays = useRelaysFeed(id); return ; } function BookMarksTab({ id }: { id: HexKey }) { const bookmarks = useBookmarkFeed(id); return ( e.kind === EventKind.TextNote)} related={bookmarks.filter(e => e.kind !== EventKind.TextNote)} /> ); } export default function ProfilePage() { const params = useParams(); const navigate = useNavigate(); const [id, setId] = useState(); const user = useUserProfile(System, id); const loginPubKey = useLogin().publicKey; const isMe = loginPubKey === id; const [showLnQr, setShowLnQr] = useState(false); const [showProfileQr, setShowProfileQr] = useState(false); const aboutText = user?.about || ""; const about = Text({ content: aboutText, tags: [], creator: "", disableMedia: true, }); 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 website_url = user?.website && !user.website.startsWith("http") ? "https://" + user.website : user?.website || ""; // feeds const { blocked } = useModeration(); const pinned = usePinnedFeed(id); const muted = useMutedFeed(id); const badges = useProfileBadges(id); const follows = useFollowsFeed(id); // tabs const ProfileTab = { Notes: { text: ( <> ), value: NOTES, }, Reactions: { text: ( <> ), value: REACTIONS, }, Followers: { text: ( <> ), value: FOLLOWERS, }, Follows: { text: ( <> ), value: FOLLOWS, }, Zaps: { text: ( <> ), value: ZAPS, }, Muted: { text: ( <> ), value: MUTED, }, Blocked: { text: ( <> ), value: BLOCKED, }, Relays: { text: ( <> ), value: RELAYS, }, Bookmarks: { text: ( <> ), value: BOOKMARKS, }, } as { [key: string]: Tab }; 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 (params.id?.match(EmailRegex)) { getNip05PubKey(params.id).then(a => { setId(a); }); } else { const nav = tryParseNostrLink(params.id ?? ""); if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) { // todo: use relays if any for nprofile setId(nav.id); } else { setId(parseId(params.id ?? "")); } } setTab(ProfileTab.Notes); }, [params]); function username() { return ( <>

{user?.display_name || user?.name || "Nostrich"}

{user?.nip05 && }
{links()}
); } function tryFormatWebsite(url: string) { try { const u = new URL(url); return `${u.hostname}${u.pathname !== "/" ? u.pathname : ""}`; } catch { // ignore } return url; } function links() { return ( <> {user?.website && ( )} {lnurl && (
setShowLnQr(true)}> {lnurl.name}
)} setShowLnQr(false)} author={id} target={user?.display_name || user?.name} /> ); } function bio() { return ( aboutText.length > 0 && (
{about}
) ); } function tabContent() { if (!id) return null; switch (tab.value) { case NOTES: return ( <> {pinned .filter(a => a.kind === EventKind.TextNote) .map(n => { return ( ); })} ); case ZAPS: { return ; } case FOLLOWS: { if (isMe) { return ; } else { return ; } } case FOLLOWERS: { return ; } case MUTED: { return ; } case BLOCKED: { return ; } case RELAYS: { return ; } case BOOKMARKS: { return ; } } } function avatar() { return (
{renderIcons()} {!isMe && id && }
); } function renderIcons() { if (!id) return; const link = encodeTLV(NostrPrefix.Profile, id); return (
setShowProfileQr(true)}> {showProfileQr && ( setShowProfileQr(false)}> )} {isMe ? ( <> ) : ( <> {lnurl && ( setShowLnQr(true)}> )} {loginPubKey && ( <> navigate( `/messages/${encodeTLVEntries("chat4" as NostrPrefix, { type: TLVEntryType.Author, length: 64, value: id, })}` ) }> )} )}
); } function userDetails() { if (!id) return; return (
{username()} {bio()}
); } function renderTab(v: Tab) { return ; } const w = window.document.querySelector(".page")?.clientWidth; return ( <>
{user?.banner && }
{avatar()} {userDetails()}
{[ProfileTab.Notes, ProfileTab.Followers, ProfileTab.Follows].map(renderTab)} {optionalTabs.map(renderTab)} {isMe && blocked.length > 0 && renderTab(ProfileTab.Blocked)}
{tabContent()}
); }