Merge pull request 'Profile urls, scrollbar, ProfilePage refactoring' (#646) from mmalmi/snort:main into main
Reviewed-on: #646
This commit is contained in:
commit
c023a89271
@ -205,9 +205,10 @@ const TierThree = ({ active, isLastSubthread, notes, related, chains, onNavigate
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ThreadRoute() {
|
export function ThreadRoute({ id }: { id?: string }) {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const link = parseNostrLink(params.id ?? "", NostrPrefix.Note);
|
const resolvedId = id ?? params.id;
|
||||||
|
const link = parseNostrLink(resolvedId ?? "", NostrPrefix.Note);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThreadContextWrapper link={link}>
|
<ThreadContextWrapper link={link}>
|
||||||
|
@ -113,3 +113,10 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gallery:not(:first-child),
|
||||||
|
img:not(:first-child),
|
||||||
|
video:not(:first-child),
|
||||||
|
.link-preview-container:not(:first-child) {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ import "./Nip05.css";
|
|||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
export function useIsVerified(pubkey: HexKey, bypassCheck?: boolean) {
|
export function useIsVerified(pubkey?: HexKey, bypassCheck?: boolean) {
|
||||||
const profile = useUserProfile(pubkey);
|
const profile = useUserProfile(pubkey);
|
||||||
return { isVerified: bypassCheck || profile?.isNostrAddressValid };
|
return { isVerified: bypassCheck || profile?.isNostrAddressValid };
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
import { NostrPrefix, tryParseNostrLink } from "@snort/system";
|
import { NostrPrefix, tryParseNostrLink } from "@snort/system";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import FormattedMessage from "Element/FormattedMessage";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
import { profileLink } from "SnortUtils";
|
|
||||||
import { getNip05PubKey } from "Pages/LoginPage";
|
import { getNip05PubKey } from "Pages/LoginPage";
|
||||||
|
import ProfilePage from "Pages/Profile/ProfilePage";
|
||||||
|
import { ThreadRoute } from "Element/Event/Thread";
|
||||||
|
|
||||||
export default function NostrLinkHandler() {
|
export default function NostrLinkHandler() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [renderComponent, setRenderComponent] = useState<React.ReactNode | null>(null);
|
||||||
|
|
||||||
const link = decodeURIComponent(params["*"] ?? "").toLowerCase();
|
const link = decodeURIComponent(params["*"] ?? "").toLowerCase();
|
||||||
|
|
||||||
async function handleLink(link: string) {
|
async function handleLink(link: string) {
|
||||||
const nav = tryParseNostrLink(link);
|
const nav = tryParseNostrLink(link);
|
||||||
if (nav) {
|
if (nav) {
|
||||||
if (nav.type === NostrPrefix.Event || nav.type === NostrPrefix.Note || nav.type === NostrPrefix.Address) {
|
if (nav.type === NostrPrefix.Event || nav.type === NostrPrefix.Note || nav.type === NostrPrefix.Address) {
|
||||||
navigate(`/e/${nav.encode()}`);
|
setRenderComponent(<ThreadRoute id={nav.encode()} />); // Directly render ThreadRoute
|
||||||
} else if (nav.type === NostrPrefix.PublicKey || nav.type === NostrPrefix.Profile) {
|
} else if (nav.type === NostrPrefix.PublicKey || nav.type === NostrPrefix.Profile) {
|
||||||
navigate(`/p/${nav.encode()}`);
|
setRenderComponent(<ProfilePage id={nav.encode()} />); // Directly render ProfilePage
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const pubkey = await getNip05PubKey(`${link}@${process.env.NIP05_DOMAIN}`);
|
const pubkey = await getNip05PubKey(`${link}@${process.env.NIP05_DOMAIN}`);
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
navigate(profileLink(pubkey));
|
setRenderComponent(<ProfilePage id={pubkey} />); // Directly render ProfilePage
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
//ignored
|
//ignored
|
||||||
@ -41,6 +42,10 @@ export default function NostrLinkHandler() {
|
|||||||
}
|
}
|
||||||
}, [link]);
|
}, [link]);
|
||||||
|
|
||||||
|
if (renderComponent) {
|
||||||
|
return renderComponent;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex f-center">
|
<div className="flex f-center">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
@ -2,36 +2,19 @@ import "./ProfilePage.css";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import FormattedMessage from "Element/FormattedMessage";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import {
|
import { encodeTLV, encodeTLVEntries, EventKind, NostrPrefix, TLVEntryType, tryParseNostrLink } from "@snort/system";
|
||||||
encodeTLV,
|
|
||||||
encodeTLVEntries,
|
|
||||||
EventKind,
|
|
||||||
HexKey,
|
|
||||||
NostrLink,
|
|
||||||
NostrPrefix,
|
|
||||||
TLVEntryType,
|
|
||||||
tryParseNostrLink,
|
|
||||||
} from "@snort/system";
|
|
||||||
import { LNURL } from "@snort/shared";
|
import { LNURL } from "@snort/shared";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
import { findTag, getReactions, unwrap } from "SnortUtils";
|
import { findTag, getReactions, unwrap } from "SnortUtils";
|
||||||
import { formatShort } from "Number";
|
|
||||||
import Note from "Element/Event/Note";
|
import Note from "Element/Event/Note";
|
||||||
import Bookmarks from "Element/Bookmarks";
|
|
||||||
import RelaysMetadata from "Element/Relay/RelaysMetadata";
|
|
||||||
import { Tab, TabElement } from "Element/Tabs";
|
import { Tab, TabElement } from "Element/Tabs";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import useMutedFeed from "Feed/MuteList";
|
import useMutedFeed from "Feed/MuteList";
|
||||||
import useRelaysFeed from "Feed/RelaysFeed";
|
|
||||||
import usePinnedFeed from "Feed/PinnedFeed";
|
import usePinnedFeed from "Feed/PinnedFeed";
|
||||||
import useBookmarkFeed from "Feed/BookmarkFeed";
|
|
||||||
import useFollowersFeed from "Feed/FollowersFeed";
|
|
||||||
import useFollowsFeed from "Feed/FollowsFeed";
|
import useFollowsFeed from "Feed/FollowsFeed";
|
||||||
import useProfileBadges from "Feed/BadgesFeed";
|
import useProfileBadges from "Feed/BadgesFeed";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
import useZapsFeed from "Feed/ZapsFeed";
|
|
||||||
import { default as ZapElement } from "Element/Event/Zap";
|
|
||||||
import FollowButton from "Element/User/FollowButton";
|
import FollowButton from "Element/User/FollowButton";
|
||||||
import { parseId, hexToBech32 } from "SnortUtils";
|
import { parseId, hexToBech32 } from "SnortUtils";
|
||||||
import Avatar from "Element/User/Avatar";
|
import Avatar from "Element/User/Avatar";
|
||||||
@ -57,62 +40,24 @@ import useLogin from "Hooks/useLogin";
|
|||||||
import { ZapTarget } from "Zapper";
|
import { ZapTarget } from "Zapper";
|
||||||
import { useStatusFeed } from "Feed/StatusFeed";
|
import { useStatusFeed } from "Feed/StatusFeed";
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "../messages";
|
||||||
import { SpotlightMediaModal } from "Element/Deck/SpotlightMedia";
|
import { SpotlightMediaModal } from "Element/Deck/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 { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
||||||
|
|
||||||
const NOTES = 0;
|
interface ProfilePageProps {
|
||||||
const REACTIONS = 1;
|
id?: string;
|
||||||
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(new NostrLink(NostrPrefix.PublicKey, id));
|
|
||||||
const zapsTotal = zaps.reduce((acc, z) => acc + z.amount, 0);
|
|
||||||
return (
|
|
||||||
<div className="main-content">
|
|
||||||
<h2 className="p">
|
|
||||||
<FormattedMessage {...messages.Sats} values={{ n: formatShort(zapsTotal) }} />
|
|
||||||
</h2>
|
|
||||||
{zaps.map(z => (
|
|
||||||
<ZapElement showZapped={false} zap={z} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function FollowersTab({ id }: { id: HexKey }) {
|
export default function ProfilePage({ id: propId }: ProfilePageProps) {
|
||||||
const followers = useFollowersFeed(id);
|
|
||||||
return <FollowsList pubkeys={followers} showAbout={true} className="p" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function FollowsTab({ id }: { id: HexKey }) {
|
|
||||||
const follows = useFollowsFeed(id);
|
|
||||||
return <FollowsList pubkeys={follows} showAbout={true} className="p" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function RelaysTab({ id }: { id: HexKey }) {
|
|
||||||
const relays = useRelaysFeed(id);
|
|
||||||
return <RelaysMetadata relays={relays} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function BookMarksTab({ id }: { id: HexKey }) {
|
|
||||||
const bookmarks = useBookmarkFeed(id);
|
|
||||||
return (
|
|
||||||
<Bookmarks
|
|
||||||
pubkey={id}
|
|
||||||
bookmarks={bookmarks.filter(e => e.kind === EventKind.TextNote)}
|
|
||||||
related={bookmarks.filter(e => e.kind !== EventKind.TextNote)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ProfilePage() {
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [id, setId] = useState<string>();
|
const [id, setId] = useState<string>();
|
||||||
@ -145,89 +90,6 @@ export default function ProfilePage() {
|
|||||||
const status = useStatusFeed(showStatus ? id : undefined, true);
|
const status = useStatusFeed(showStatus ? id : undefined, true);
|
||||||
|
|
||||||
// tabs
|
// tabs
|
||||||
const ProfileTab = {
|
|
||||||
Notes: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="pencil" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Notes" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: NOTES,
|
|
||||||
},
|
|
||||||
Reactions: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="reaction" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Reactions" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: REACTIONS,
|
|
||||||
},
|
|
||||||
Followers: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="user-v2" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Followers" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: FOLLOWERS,
|
|
||||||
},
|
|
||||||
Follows: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="stars" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Follows" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: FOLLOWS,
|
|
||||||
},
|
|
||||||
Zaps: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="zap-solid" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Zaps" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: ZAPS,
|
|
||||||
},
|
|
||||||
Muted: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="mute" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Muted" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: MUTED,
|
|
||||||
},
|
|
||||||
Blocked: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="block" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Blocked" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: BLOCKED,
|
|
||||||
},
|
|
||||||
Relays: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="wifi" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Relays" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: RELAYS,
|
|
||||||
},
|
|
||||||
Bookmarks: {
|
|
||||||
text: (
|
|
||||||
<>
|
|
||||||
<Icon name="bookmark-solid" size={16} />
|
|
||||||
<FormattedMessage defaultMessage="Bookmarks" />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
value: BOOKMARKS,
|
|
||||||
},
|
|
||||||
} as { [key: string]: Tab };
|
|
||||||
const [tab, setTab] = useState<Tab>(ProfileTab.Notes);
|
const [tab, setTab] = useState<Tab>(ProfileTab.Notes);
|
||||||
const optionalTabs = [ProfileTab.Zaps, ProfileTab.Relays, ProfileTab.Bookmarks, ProfileTab.Muted].filter(a =>
|
const optionalTabs = [ProfileTab.Zaps, ProfileTab.Relays, ProfileTab.Bookmarks, ProfileTab.Muted].filter(a =>
|
||||||
unwrap(a),
|
unwrap(a),
|
||||||
@ -235,21 +97,22 @@ export default function ProfilePage() {
|
|||||||
const horizontalScroll = useHorizontalScroll();
|
const horizontalScroll = useHorizontalScroll();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (params.id?.match(EmailRegex)) {
|
const resolvedId = propId || params.id;
|
||||||
getNip05PubKey(params.id).then(a => {
|
if (resolvedId?.match(EmailRegex)) {
|
||||||
|
getNip05PubKey(resolvedId).then(a => {
|
||||||
setId(a);
|
setId(a);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const nav = tryParseNostrLink(params.id ?? "");
|
const nav = tryParseNostrLink(resolvedId ?? "");
|
||||||
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
|
if (nav?.type === NostrPrefix.PublicKey || nav?.type === NostrPrefix.Profile) {
|
||||||
// todo: use relays if any for nprofile
|
// todo: use relays if any for nprofile
|
||||||
setId(nav.id);
|
setId(nav.id);
|
||||||
} else {
|
} else {
|
||||||
setId(parseId(params.id ?? ""));
|
setId(parseId(resolvedId ?? ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTab(ProfileTab.Notes);
|
setTab(ProfileTab.Notes);
|
||||||
}, [params]);
|
}, [propId, params]);
|
||||||
|
|
||||||
function musicStatus() {
|
function musicStatus() {
|
||||||
if (!status.music) return;
|
if (!status.music) return;
|
||||||
@ -274,12 +137,21 @@ export default function ProfilePage() {
|
|||||||
return inner();
|
return inner();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user?.nip05 && user?.isNostrAddressValid) {
|
||||||
|
if (user.nip05.endsWith(`@${process.env.NIP05_DOMAIN}`)) {
|
||||||
|
const username = user.nip05?.replace(`@${process.env.NIP05_DOMAIN}`, "");
|
||||||
|
navigate(`/${username}`, { replace: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [user?.isNostrAddressValid, user?.nip05]);
|
||||||
|
|
||||||
function username() {
|
function username() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex-column g4">
|
<div className="flex-column g4">
|
||||||
<h2 className="flex g4">
|
<h2 className="flex g4">
|
||||||
{user?.display_name || user?.name || "Nostrich"}
|
<DisplayName user={user} pubkey={user?.pubkey ?? ""} />
|
||||||
<FollowsYou followsMe={follows.includes(loginPubKey ?? "")} />
|
<FollowsYou followsMe={follows.includes(loginPubKey ?? "")} />
|
||||||
</h2>
|
</h2>
|
||||||
{user?.nip05 && <Nip05 nip05={user.nip05} pubkey={user.pubkey} />}
|
{user?.nip05 && <Nip05 nip05={user.nip05} pubkey={user.pubkey} />}
|
||||||
@ -350,7 +222,7 @@ export default function ProfilePage() {
|
|||||||
if (!id) return null;
|
if (!id) return null;
|
||||||
|
|
||||||
switch (tab.value) {
|
switch (tab.value) {
|
||||||
case NOTES:
|
case ProfileTabType.NOTES:
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{pinned
|
{pinned
|
||||||
@ -380,29 +252,29 @@ export default function ProfilePage() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
case ZAPS: {
|
case ProfileTabType.ZAPS: {
|
||||||
return <ZapsProfileTab id={id} />;
|
return <ZapsProfileTab id={id} />;
|
||||||
}
|
}
|
||||||
case FOLLOWS: {
|
case ProfileTabType.FOLLOWS: {
|
||||||
if (isMe) {
|
if (isMe) {
|
||||||
return <FollowsList pubkeys={follows} showFollowAll={!isMe} showAbout={false} className="p" />;
|
return <FollowsList pubkeys={follows} showFollowAll={!isMe} showAbout={false} className="p" />;
|
||||||
} else {
|
} else {
|
||||||
return <FollowsTab id={id} />;
|
return <FollowsTab id={id} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case FOLLOWERS: {
|
case ProfileTabType.FOLLOWERS: {
|
||||||
return <FollowersTab id={id} />;
|
return <FollowersTab id={id} />;
|
||||||
}
|
}
|
||||||
case MUTED: {
|
case ProfileTabType.MUTED: {
|
||||||
return <MutedList pubkeys={muted} />;
|
return <MutedList pubkeys={muted} />;
|
||||||
}
|
}
|
||||||
case BLOCKED: {
|
case ProfileTabType.BLOCKED: {
|
||||||
return <BlockList />;
|
return <BlockList />;
|
||||||
}
|
}
|
||||||
case RELAYS: {
|
case ProfileTabType.RELAYS: {
|
||||||
return <RelaysTab id={id} />;
|
return <RelaysTab id={id} />;
|
||||||
}
|
}
|
||||||
case BOOKMARKS: {
|
case ProfileTabType.BOOKMARKS: {
|
||||||
return <BookMarksTab id={id} />;
|
return <BookMarksTab id={id} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
154
packages/app/src/Pages/Profile/ProfileTab.tsx
Normal file
154
packages/app/src/Pages/Profile/ProfileTab.tsx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import useZapsFeed from "../../Feed/ZapsFeed";
|
||||||
|
import FormattedMessage from "../../Element/FormattedMessage";
|
||||||
|
import messages from "../messages";
|
||||||
|
import { formatShort } from "../../Number";
|
||||||
|
import useFollowersFeed from "../../Feed/FollowersFeed";
|
||||||
|
import FollowsList from "../../Element/User/FollowListBase";
|
||||||
|
import useFollowsFeed from "../../Feed/FollowsFeed";
|
||||||
|
import useRelaysFeed from "../../Feed/RelaysFeed";
|
||||||
|
import RelaysMetadata from "../../Element/Relay/RelaysMetadata";
|
||||||
|
import useBookmarkFeed from "../../Feed/BookmarkFeed";
|
||||||
|
import Bookmarks from "../../Element/Bookmarks";
|
||||||
|
import Icon from "../../Icons/Icon";
|
||||||
|
import { Tab } from "../../Element/Tabs";
|
||||||
|
import { EventKind, HexKey, NostrLink, NostrPrefix } from "@snort/system";
|
||||||
|
import { default as ZapElement } from "Element/Event/Zap";
|
||||||
|
|
||||||
|
export enum ProfileTabType {
|
||||||
|
NOTES = 0,
|
||||||
|
REACTIONS = 1,
|
||||||
|
FOLLOWERS = 2,
|
||||||
|
FOLLOWS = 3,
|
||||||
|
ZAPS = 4,
|
||||||
|
MUTED = 5,
|
||||||
|
BLOCKED = 6,
|
||||||
|
RELAYS = 7,
|
||||||
|
BOOKMARKS = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ZapsProfileTab({ id }: { id: HexKey }) {
|
||||||
|
const zaps = useZapsFeed(new NostrLink(NostrPrefix.PublicKey, id));
|
||||||
|
const zapsTotal = zaps.reduce((acc, z) => acc + z.amount, 0);
|
||||||
|
return (
|
||||||
|
<div className="main-content">
|
||||||
|
<h2 className="p">
|
||||||
|
<FormattedMessage {...messages.Sats} values={{ n: formatShort(zapsTotal) }} />
|
||||||
|
</h2>
|
||||||
|
{zaps.map(z => (
|
||||||
|
<ZapElement showZapped={false} zap={z} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FollowersTab({ id }: { id: HexKey }) {
|
||||||
|
const followers = useFollowersFeed(id);
|
||||||
|
return <FollowsList pubkeys={followers} showAbout={true} className="p" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FollowsTab({ id }: { id: HexKey }) {
|
||||||
|
const follows = useFollowsFeed(id);
|
||||||
|
return <FollowsList pubkeys={follows} showAbout={true} className="p" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RelaysTab({ id }: { id: HexKey }) {
|
||||||
|
const relays = useRelaysFeed(id);
|
||||||
|
return <RelaysMetadata relays={relays} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BookMarksTab({ id }: { id: HexKey }) {
|
||||||
|
const bookmarks = useBookmarkFeed(id);
|
||||||
|
return (
|
||||||
|
<Bookmarks
|
||||||
|
pubkey={id}
|
||||||
|
bookmarks={bookmarks.filter(e => e.kind === EventKind.TextNote)}
|
||||||
|
related={bookmarks.filter(e => e.kind !== EventKind.TextNote)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfileTab = {
|
||||||
|
Notes: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="pencil" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Notes" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.NOTES,
|
||||||
|
},
|
||||||
|
Reactions: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="reaction" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Reactions" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.REACTIONS,
|
||||||
|
},
|
||||||
|
Followers: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="user-v2" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Followers" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.FOLLOWERS,
|
||||||
|
},
|
||||||
|
Follows: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="stars" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Follows" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.FOLLOWS,
|
||||||
|
},
|
||||||
|
Zaps: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="zap-solid" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Zaps" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.ZAPS,
|
||||||
|
},
|
||||||
|
Muted: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="mute" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Muted" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.MUTED,
|
||||||
|
},
|
||||||
|
Blocked: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="block" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Blocked" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.BLOCKED,
|
||||||
|
},
|
||||||
|
Relays: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="wifi" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Relays" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.RELAYS,
|
||||||
|
},
|
||||||
|
Bookmarks: {
|
||||||
|
text: (
|
||||||
|
<>
|
||||||
|
<Icon name="bookmark-solid" size={16} />
|
||||||
|
<FormattedMessage defaultMessage="Bookmarks" />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
value: ProfileTabType.BOOKMARKS,
|
||||||
|
},
|
||||||
|
} as { [key: string]: Tab };
|
||||||
|
|
||||||
|
export default ProfileTab;
|
@ -107,6 +107,7 @@ body {
|
|||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
@ -24,7 +24,7 @@ import { IntlProvider } from "IntlProvider";
|
|||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
import Layout from "Pages/Layout";
|
import Layout from "Pages/Layout";
|
||||||
import LoginPage from "Pages/LoginPage";
|
import LoginPage from "Pages/LoginPage";
|
||||||
import ProfilePage from "Pages/ProfilePage";
|
import ProfilePage from "Pages/Profile/ProfilePage";
|
||||||
import { RootRoutes, RootTabRoutes } from "Pages/Root";
|
import { RootRoutes, RootTabRoutes } from "Pages/Root";
|
||||||
import NotificationsPage from "Pages/Notifications";
|
import NotificationsPage from "Pages/Notifications";
|
||||||
import SettingsPage, { SettingsRoutes } from "Pages/SettingsPage";
|
import SettingsPage, { SettingsRoutes } from "Pages/SettingsPage";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user