import "./ProfilePage.css"; import { useEffect, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import { encodeTLV, EventKind, HexKey, NostrPrefix } from "@snort/nostr"; import { parseNostrLink, getReactions, unwrap } from "Util"; 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 { useUserProfile } from "Hooks/useUserProfile"; 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 "Util"; 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/Login"; import { LNURL } from "LNURL"; import useLogin from "Hooks/useLogin"; import messages from "./messages"; 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 { formatMessage } = useIntl(); const params = useParams(); const navigate = useNavigate(); const [id, setId] = useState(); const user = useUserProfile(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: formatMessage(messages.Notes), value: NOTES }, Reactions: { text: formatMessage(messages.Reactions), value: REACTIONS }, Followers: { text: formatMessage(messages.Followers), value: FOLLOWERS }, Follows: { text: formatMessage(messages.Follows), value: FOLLOWS }, Zaps: { text: formatMessage(messages.Zaps), value: ZAPS }, Muted: { text: formatMessage(messages.Muted), value: MUTED }, Blocked: { text: formatMessage(messages.BlockedCount, { n: blocked.length }), value: BLOCKED }, Relays: { text: formatMessage(messages.Relays), value: RELAYS }, Bookmarks: { text: formatMessage(messages.Bookmarks), value: BOOKMARKS }, }; 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 = parseNostrLink(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 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 (
); } function renderIcons() { if (!id) return; const link = encodeTLV(id, NostrPrefix.Profile); return (
setShowProfileQr(true)}> {showProfileQr && ( setShowProfileQr(false)}> )} {isMe ? ( <> ) : ( <> {lnurl && ( setShowLnQr(true)}> )} {loginPubKey && ( <> navigate(`/messages/${hexToBech32(NostrPrefix.PublicKey, id)}`)}> )} )}
); } function userDetails() { if (!id) return; return (
{username()}
{renderIcons()} {!isMe && }
{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()} ); }