feat: nip5 in profile path
This commit is contained in:
parent
688c5cf4f8
commit
45c4c3bce1
@ -4,7 +4,7 @@ import { RootState } from "State/Store";
|
|||||||
import { HexKey, Lists } from "@snort/nostr";
|
import { HexKey, Lists } from "@snort/nostr";
|
||||||
import useNotelistSubscription from "Feed/useNotelistSubscription";
|
import useNotelistSubscription from "Feed/useNotelistSubscription";
|
||||||
|
|
||||||
export default function useBookmarkFeed(pubkey: HexKey) {
|
export default function useBookmarkFeed(pubkey?: HexKey) {
|
||||||
const { bookmarked } = useSelector((s: RootState) => s.login);
|
const { bookmarked } = useSelector((s: RootState) => s.login);
|
||||||
return useNotelistSubscription(pubkey, Lists.Bookmarked, bookmarked);
|
return useNotelistSubscription(pubkey, Lists.Bookmarked, bookmarked);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ import { HexKey } from "@snort/nostr";
|
|||||||
import { EventKind, Subscriptions } from "@snort/nostr";
|
import { EventKind, Subscriptions } from "@snort/nostr";
|
||||||
import useSubscription from "Feed/Subscription";
|
import useSubscription from "Feed/Subscription";
|
||||||
|
|
||||||
export default function useFollowersFeed(pubkey: HexKey) {
|
export default function useFollowersFeed(pubkey?: HexKey) {
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
|
if (!pubkey) return null;
|
||||||
const x = new Subscriptions();
|
const x = new Subscriptions();
|
||||||
x.Id = `followers:${pubkey.slice(0, 12)}`;
|
x.Id = `followers:${pubkey.slice(0, 12)}`;
|
||||||
x.Kinds = new Set([EventKind.ContactList]);
|
x.Kinds = new Set([EventKind.ContactList]);
|
||||||
|
@ -5,12 +5,12 @@ import { HexKey, TaggedRawEvent, EventKind, Subscriptions } from "@snort/nostr";
|
|||||||
import useSubscription from "Feed/Subscription";
|
import useSubscription from "Feed/Subscription";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
|
|
||||||
export default function useFollowsFeed(pubkey: HexKey) {
|
export default function useFollowsFeed(pubkey?: HexKey) {
|
||||||
const { publicKey, follows } = useSelector((s: RootState) => s.login);
|
const { publicKey, follows } = useSelector((s: RootState) => s.login);
|
||||||
const isMe = publicKey === pubkey;
|
const isMe = publicKey === pubkey;
|
||||||
|
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
if (isMe) return null;
|
if (isMe || !pubkey) return null;
|
||||||
const x = new Subscriptions();
|
const x = new Subscriptions();
|
||||||
x.Id = `follows:${pubkey.slice(0, 12)}`;
|
x.Id = `follows:${pubkey.slice(0, 12)}`;
|
||||||
x.Kinds = new Set([EventKind.ContactList]);
|
x.Kinds = new Set([EventKind.ContactList]);
|
||||||
@ -23,11 +23,12 @@ export default function useFollowsFeed(pubkey: HexKey) {
|
|||||||
if (isMe) {
|
if (isMe) {
|
||||||
return follows;
|
return follows;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getFollowing(contactFeed.store.notes ?? [], pubkey);
|
return getFollowing(contactFeed.store.notes ?? [], pubkey);
|
||||||
}, [contactFeed.store, follows]);
|
}, [contactFeed.store, follows, pubkey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFollowing(notes: TaggedRawEvent[], pubkey: HexKey) {
|
export function getFollowing(notes: TaggedRawEvent[], pubkey?: HexKey) {
|
||||||
const contactLists = notes.filter(a => a.kind === EventKind.ContactList && a.pubkey === pubkey);
|
const contactLists = notes.filter(a => a.kind === EventKind.ContactList && a.pubkey === pubkey);
|
||||||
const pTags = contactLists?.map(a => a.tags.filter(b => b[0] === "p").map(c => c[1]));
|
const pTags = contactLists?.map(a => a.tags.filter(b => b[0] === "p").map(c => c[1]));
|
||||||
return [...new Set(pTags?.flat())];
|
return [...new Set(pTags?.flat())];
|
||||||
|
@ -7,12 +7,12 @@ import { EventKind, Subscriptions } from "@snort/nostr";
|
|||||||
import useSubscription, { NoteStore } from "Feed/Subscription";
|
import useSubscription, { NoteStore } from "Feed/Subscription";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
|
|
||||||
export default function useMutedFeed(pubkey: HexKey) {
|
export default function useMutedFeed(pubkey?: HexKey) {
|
||||||
const { publicKey, muted } = useSelector((s: RootState) => s.login);
|
const { publicKey, muted } = useSelector((s: RootState) => s.login);
|
||||||
const isMe = publicKey === pubkey;
|
const isMe = publicKey === pubkey;
|
||||||
|
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
if (isMe) return null;
|
if (isMe || !pubkey) return null;
|
||||||
const sub = new Subscriptions();
|
const sub = new Subscriptions();
|
||||||
sub.Id = `muted:${pubkey.slice(0, 12)}`;
|
sub.Id = `muted:${pubkey.slice(0, 12)}`;
|
||||||
sub.Kinds = new Set([EventKind.PubkeyLists]);
|
sub.Kinds = new Set([EventKind.PubkeyLists]);
|
||||||
@ -25,8 +25,11 @@ export default function useMutedFeed(pubkey: HexKey) {
|
|||||||
const mutedFeed = useSubscription(sub, { leaveOpen: false, cache: true });
|
const mutedFeed = useSubscription(sub, { leaveOpen: false, cache: true });
|
||||||
|
|
||||||
const mutedList = useMemo(() => {
|
const mutedList = useMemo(() => {
|
||||||
return getMuted(mutedFeed.store, pubkey);
|
if (pubkey) {
|
||||||
}, [mutedFeed.store]);
|
return getMuted(mutedFeed.store, pubkey);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [mutedFeed.store, pubkey]);
|
||||||
|
|
||||||
return isMe ? muted : mutedList;
|
return isMe ? muted : mutedList;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { RootState } from "State/Store";
|
|||||||
import { HexKey, Lists } from "@snort/nostr";
|
import { HexKey, Lists } from "@snort/nostr";
|
||||||
import useNotelistSubscription from "Feed/useNotelistSubscription";
|
import useNotelistSubscription from "Feed/useNotelistSubscription";
|
||||||
|
|
||||||
export default function usePinnedFeed(pubkey: HexKey) {
|
export default function usePinnedFeed(pubkey?: HexKey) {
|
||||||
const { pinned } = useSelector((s: RootState) => s.login);
|
const { pinned } = useSelector((s: RootState) => s.login);
|
||||||
return useNotelistSubscription(pubkey, Lists.Pinned, pinned);
|
return useNotelistSubscription(pubkey, Lists.Pinned, pinned);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ import { HexKey, FullRelaySettings } from "@snort/nostr";
|
|||||||
import { EventKind, Subscriptions } from "@snort/nostr";
|
import { EventKind, Subscriptions } from "@snort/nostr";
|
||||||
import useSubscription from "./Subscription";
|
import useSubscription from "./Subscription";
|
||||||
|
|
||||||
export default function useRelaysFeed(pubkey: HexKey) {
|
export default function useRelaysFeed(pubkey?: HexKey) {
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
|
if (!pubkey) return null;
|
||||||
const x = new Subscriptions();
|
const x = new Subscriptions();
|
||||||
x.Id = `relays:${pubkey.slice(0, 12)}`;
|
x.Id = `relays:${pubkey.slice(0, 12)}`;
|
||||||
x.Kinds = new Set([EventKind.ContactList]);
|
x.Kinds = new Set([EventKind.ContactList]);
|
||||||
|
@ -3,8 +3,9 @@ import { HexKey, EventKind, Subscriptions } from "@snort/nostr";
|
|||||||
import { parseZap } from "Element/Zap";
|
import { parseZap } from "Element/Zap";
|
||||||
import useSubscription from "./Subscription";
|
import useSubscription from "./Subscription";
|
||||||
|
|
||||||
export default function useZapsFeed(pubkey: HexKey) {
|
export default function useZapsFeed(pubkey?: HexKey) {
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
|
if (!pubkey) return null;
|
||||||
const x = new Subscriptions();
|
const x = new Subscriptions();
|
||||||
x.Id = `zaps:${pubkey.slice(0, 12)}`;
|
x.Id = `zaps:${pubkey.slice(0, 12)}`;
|
||||||
x.Kinds = new Set([EventKind.ZapReceipt]);
|
x.Kinds = new Set([EventKind.ZapReceipt]);
|
||||||
|
@ -6,12 +6,12 @@ import { HexKey, Lists, EventKind, Subscriptions } from "@snort/nostr";
|
|||||||
import useSubscription from "Feed/Subscription";
|
import useSubscription from "Feed/Subscription";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
|
|
||||||
export default function useNotelistSubscription(pubkey: HexKey, l: Lists, defaultIds: HexKey[]) {
|
export default function useNotelistSubscription(pubkey: HexKey | undefined, l: Lists, defaultIds: HexKey[]) {
|
||||||
const { preferences, publicKey } = useSelector((s: RootState) => s.login);
|
const { preferences, publicKey } = useSelector((s: RootState) => s.login);
|
||||||
const isMe = publicKey === pubkey;
|
const isMe = publicKey === pubkey;
|
||||||
|
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
if (isMe) return null;
|
if (isMe || !pubkey) return null;
|
||||||
const sub = new Subscriptions();
|
const sub = new Subscriptions();
|
||||||
sub.Id = `note-list-${l}:${pubkey.slice(0, 12)}`;
|
sub.Id = `note-list-${l}:${pubkey.slice(0, 12)}`;
|
||||||
sub.Kinds = new Set([EventKind.NoteLists]);
|
sub.Kinds = new Set([EventKind.NoteLists]);
|
||||||
@ -33,12 +33,13 @@ export default function useNotelistSubscription(pubkey: HexKey, l: Lists, defaul
|
|||||||
}, [store.notes, isMe, defaultIds]);
|
}, [store.notes, isMe, defaultIds]);
|
||||||
|
|
||||||
const esub = useMemo(() => {
|
const esub = useMemo(() => {
|
||||||
|
if (!pubkey) return null;
|
||||||
const s = new Subscriptions();
|
const s = new Subscriptions();
|
||||||
s.Id = `${l}-notes:${pubkey.slice(0, 12)}`;
|
s.Id = `${l}-notes:${pubkey.slice(0, 12)}`;
|
||||||
s.Kinds = new Set([EventKind.TextNote]);
|
s.Kinds = new Set([EventKind.TextNote]);
|
||||||
s.Ids = new Set(etags);
|
s.Ids = new Set(etags);
|
||||||
return s;
|
return s;
|
||||||
}, [etags]);
|
}, [etags, pubkey]);
|
||||||
|
|
||||||
const subRelated = useMemo(() => {
|
const subRelated = useMemo(() => {
|
||||||
let sub: Subscriptions | undefined;
|
let sub: Subscriptions | undefined;
|
||||||
|
@ -46,6 +46,19 @@ const Artwork: Array<ArtworkEntry> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export async function getNip05PubKey(addr: string): Promise<string> {
|
||||||
|
const [username, domain] = addr.split("@");
|
||||||
|
const rsp = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(username)}`);
|
||||||
|
if (rsp.ok) {
|
||||||
|
const data = await rsp.json();
|
||||||
|
const pKey = data.names[username];
|
||||||
|
if (pKey) {
|
||||||
|
return pKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("User key not found");
|
||||||
|
}
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -69,19 +82,6 @@ export default function LoginPage() {
|
|||||||
setArt(ret);
|
setArt(ret);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function getNip05PubKey(addr: string) {
|
|
||||||
const [username, domain] = addr.split("@");
|
|
||||||
const rsp = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(username)}`);
|
|
||||||
if (rsp.ok) {
|
|
||||||
const data = await rsp.json();
|
|
||||||
const pKey = data.names[username];
|
|
||||||
if (pKey) {
|
|
||||||
return pKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("User key not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doLogin() {
|
async function doLogin() {
|
||||||
try {
|
try {
|
||||||
if (key.startsWith("nsec")) {
|
if (key.startsWith("nsec")) {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import "./ProfilePage.css";
|
import "./ProfilePage.css";
|
||||||
import { useEffect, useMemo, useState, CSSProperties } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { NostrPrefix } from "@snort/nostr";
|
||||||
|
|
||||||
import { unwrap } from "Util";
|
import { unwrap } from "Util";
|
||||||
import { formatShort } from "Number";
|
import { formatShort } from "Number";
|
||||||
@ -32,20 +33,20 @@ import Text from "Element/Text";
|
|||||||
import SendSats from "Element/SendSats";
|
import SendSats from "Element/SendSats";
|
||||||
import Nip05 from "Element/Nip05";
|
import Nip05 from "Element/Nip05";
|
||||||
import Copy from "Element/Copy";
|
import Copy from "Element/Copy";
|
||||||
import ProfilePreview from "Element/ProfilePreview";
|
|
||||||
import ProfileImage from "Element/ProfileImage";
|
import ProfileImage from "Element/ProfileImage";
|
||||||
import BlockList from "Element/BlockList";
|
import BlockList from "Element/BlockList";
|
||||||
import MutedList from "Element/MutedList";
|
import MutedList from "Element/MutedList";
|
||||||
import FollowsList from "Element/FollowListBase";
|
import FollowsList from "Element/FollowListBase";
|
||||||
import IconButton from "Element/IconButton";
|
import IconButton from "Element/IconButton";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
import { HexKey, NostrPrefix } from "@snort/nostr";
|
|
||||||
import FollowsYou from "Element/FollowsYou";
|
import FollowsYou from "Element/FollowsYou";
|
||||||
import QrCode from "Element/QrCode";
|
import QrCode from "Element/QrCode";
|
||||||
import Modal from "Element/Modal";
|
import Modal from "Element/Modal";
|
||||||
import { ProxyImg } from "Element/ProxyImg";
|
import { ProxyImg } from "Element/ProxyImg";
|
||||||
import useHorizontalScroll from "Hooks/useHorizontalScroll";
|
import useHorizontalScroll from "Hooks/useHorizontalScroll";
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
import { EmailRegex } from "Const";
|
||||||
|
import { getNip05PubKey } from "./Login";
|
||||||
|
|
||||||
const NOTES = 0;
|
const NOTES = 0;
|
||||||
const REACTIONS = 1;
|
const REACTIONS = 1;
|
||||||
@ -61,10 +62,9 @@ export default function ProfilePage() {
|
|||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const id = useMemo(() => parseId(params.id ?? ""), [params]);
|
const [id, setId] = useState<string>();
|
||||||
const user = useUserProfile(id);
|
const user = useUserProfile(id);
|
||||||
const loggedOut = useSelector<RootState, boolean | undefined>(s => s.login.loggedOut);
|
const loginPubKey = useSelector((s: RootState) => s.login.publicKey);
|
||||||
const loginPubKey = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
|
|
||||||
const isMe = loginPubKey === id;
|
const isMe = loginPubKey === id;
|
||||||
const [showLnQr, setShowLnQr] = useState<boolean>(false);
|
const [showLnQr, setShowLnQr] = useState<boolean>(false);
|
||||||
const [showProfileQr, setShowProfileQr] = useState<boolean>(false);
|
const [showProfileQr, setShowProfileQr] = useState<boolean>(false);
|
||||||
@ -75,7 +75,7 @@ export default function ProfilePage() {
|
|||||||
users: new Map(),
|
users: new Map(),
|
||||||
creator: "",
|
creator: "",
|
||||||
});
|
});
|
||||||
const npub = !id.startsWith("npub") ? hexToBech32("npub", id || undefined) : id;
|
const npub = !id?.startsWith("npub") ? hexToBech32("npub", id || undefined) : id;
|
||||||
|
|
||||||
const lnurl = extractLnAddress(user?.lud16 || user?.lud06 || "");
|
const lnurl = extractLnAddress(user?.lud16 || user?.lud06 || "");
|
||||||
const website_url =
|
const website_url =
|
||||||
@ -112,6 +112,13 @@ export default function ProfilePage() {
|
|||||||
const horizontalScroll = useHorizontalScroll();
|
const horizontalScroll = useHorizontalScroll();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (params.id?.match(EmailRegex)) {
|
||||||
|
getNip05PubKey(params.id).then(a => {
|
||||||
|
setId(a);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setId(parseId(params.id ?? ""));
|
||||||
|
}
|
||||||
setTab(ProfileTab.Notes);
|
setTab(ProfileTab.Notes);
|
||||||
}, [params]);
|
}, [params]);
|
||||||
|
|
||||||
@ -174,6 +181,8 @@ export default function ProfilePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tabContent() {
|
function tabContent() {
|
||||||
|
if (!id) return null;
|
||||||
|
|
||||||
switch (tab.value) {
|
switch (tab.value) {
|
||||||
case NOTES:
|
case NOTES:
|
||||||
return (
|
return (
|
||||||
@ -254,7 +263,7 @@ export default function ProfilePage() {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
{showProfileQr && (
|
{showProfileQr && (
|
||||||
<Modal className="qr-modal" onClose={() => setShowProfileQr(false)}>
|
<Modal className="qr-modal" onClose={() => setShowProfileQr(false)}>
|
||||||
<ProfileImage pubkey={id} />
|
<ProfileImage pubkey={id ?? ""} />
|
||||||
<QrCode
|
<QrCode
|
||||||
data={`nostr:${hexToBech32(NostrPrefix.PublicKey, id)}`}
|
data={`nostr:${hexToBech32(NostrPrefix.PublicKey, id)}`}
|
||||||
link={undefined}
|
link={undefined}
|
||||||
@ -275,7 +284,7 @@ export default function ProfilePage() {
|
|||||||
<Zap width={14} height={16} />
|
<Zap width={14} height={16} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
{!loggedOut && (
|
{loginPubKey && (
|
||||||
<>
|
<>
|
||||||
<IconButton onClick={() => navigate(`/messages/${hexToBech32(NostrPrefix.PublicKey, id)}`)}>
|
<IconButton onClick={() => navigate(`/messages/${hexToBech32(NostrPrefix.PublicKey, id)}`)}>
|
||||||
<Envelope width={16} height={13} />
|
<Envelope width={16} height={13} />
|
||||||
@ -294,7 +303,7 @@ export default function ProfilePage() {
|
|||||||
{username()}
|
{username()}
|
||||||
<div className="profile-actions">
|
<div className="profile-actions">
|
||||||
{renderIcons()}
|
{renderIcons()}
|
||||||
{!isMe && <FollowButton pubkey={id} />}
|
{!isMe && <FollowButton pubkey={id ?? ""} />}
|
||||||
</div>
|
</div>
|
||||||
{bio()}
|
{bio()}
|
||||||
</div>
|
</div>
|
||||||
@ -306,7 +315,6 @@ export default function ProfilePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const w = window.document.querySelector(".page")?.clientWidth;
|
const w = window.document.querySelector(".page")?.clientWidth;
|
||||||
const bannerStyle = user?.banner ? ({ "--img-url": `url(${user.banner})` } as CSSProperties) : {};
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="profile flex">
|
<div className="profile flex">
|
||||||
|
Loading…
Reference in New Issue
Block a user