NIP-65: Relay list metada (#238)

This commit is contained in:
Alejandro
2023-02-10 20:23:52 +01:00
committed by GitHub
parent d13904b8e2
commit 5153f5c90a
29 changed files with 304 additions and 20 deletions

View File

@ -2,10 +2,11 @@ import "./Layout.css";
import { useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { randomSample } from "Util";
import Envelope from "Icons/Envelope";
import Bell from "Icons/Bell";
import Search from "Icons/Search";
import { RootState } from "State/Store";
import { init, setRelays } from "State/Login";
import { System } from "Nostr/System";
@ -147,8 +148,7 @@ export default function Layout() {
const rsp = await fetch("https://api.nostr.watch/v1/online");
if (rsp.ok) {
const online: string[] = await rsp.json();
const pickRandom = online.sort(() => (Math.random() >= 0.5 ? 1 : -1)).slice(0, 4); // pick 4 random relays
const pickRandom = randomSample(online, 4);
const relayObjects = pickRandom.map(a => [a, { read: true, write: true }]);
newRelays = Object.fromEntries(relayObjects);
dispatch(

View File

@ -4,19 +4,21 @@ import { useIntl, FormattedMessage } from "react-intl";
import { useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router-dom";
import { unwrap } from "Util";
import { formatShort } from "Number";
import RelaysMetadata from "Element/RelaysMetadata";
import { Tab, TabElement } from "Element/Tabs";
import Link from "Icons/Link";
import Qr from "Icons/Qr";
import Zap from "Icons/Zap";
import Envelope from "Icons/Envelope";
import useRelaysFeed from "Feed/RelaysFeed";
import { useUserProfile } from "Feed/ProfileFeed";
import useZapsFeed from "Feed/ZapsFeed";
import { default as ZapElement, parseZap } from "Element/Zap";
import FollowButton from "Element/FollowButton";
import { extractLnAddress, parseId, hexToBech32 } from "Util";
import Avatar from "Element/Avatar";
import LogoutButton from "Element/LogoutButton";
import Timeline from "Element/Timeline";
import Text from "Element/Text";
import SendSats from "Element/SendSats";
@ -46,6 +48,7 @@ const FOLLOWS = 3;
const ZAPS = 4;
const MUTED = 5;
const BLOCKED = 6;
const RELAYS = 7;
export default function ProfilePage() {
const { formatMessage } = useIntl();
@ -69,6 +72,7 @@ export default function ProfilePage() {
const lnurl = extractLnAddress(user?.lud16 || user?.lud06 || "");
const website_url =
user?.website && !user.website.startsWith("http") ? "https://" + user.website : user?.website || "";
const relays = useRelaysFeed(id);
const zapFeed = useZapsFeed(id);
const zaps = useMemo(() => {
const profileZaps = zapFeed.store.notes.map(parseZap).filter(z => z.valid && z.p === id && !z.e && z.zapper !== id);
@ -85,8 +89,12 @@ export default function ProfilePage() {
Zaps: { text: formatMessage(messages.Zaps), value: ZAPS },
Muted: { text: formatMessage(messages.Muted), value: MUTED },
Blocked: { text: formatMessage(messages.Blocked), value: BLOCKED },
Relays: { text: formatMessage(messages.Relays), value: RELAYS },
};
const [tab, setTab] = useState<Tab>(ProfileTab.Notes);
const optionalTabs = [zapsTotal > 0 && ProfileTab.Zaps, relays.length > 0 && ProfileTab.Relays].filter(a =>
unwrap(a)
) as Tab[];
useEffect(() => {
setTab(ProfileTab.Notes);
@ -204,6 +212,9 @@ export default function ProfilePage() {
case BLOCKED: {
return isMe ? <BlockList variant="blocked" /> : null;
}
case RELAYS: {
return <RelaysMetadata relays={relays} />;
}
}
}
@ -229,7 +240,6 @@ export default function ProfilePage() {
)}
{isMe ? (
<>
<LogoutButton />
<button type="button" onClick={() => navigate("/settings")}>
<FormattedMessage {...messages.Settings} />
</button>
@ -282,7 +292,8 @@ export default function ProfilePage() {
</div>
</div>
<div className="tabs main-content" ref={horizontalScroll}>
{[ProfileTab.Notes, ProfileTab.Followers, ProfileTab.Follows, ProfileTab.Zaps, ProfileTab.Muted].map(renderTab)}
{[ProfileTab.Notes, ProfileTab.Followers, ProfileTab.Follows, ProfileTab.Muted].map(renderTab)}
{optionalTabs.map(renderTab)}
{isMe && renderTab(ProfileTab.Blocked)}
</div>
{tabContent()}

View File

@ -33,4 +33,7 @@ export default defineMessages({
NostrPlebsNip: {
defaultMessage: `Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices`,
},
Relays: {
defaultMessage: "Relays",
},
});

View File

@ -5,6 +5,7 @@ import { FormattedMessage } from "react-intl";
import { DefaultImgProxy, setPreferences, UserPreferences } from "State/Login";
import { RootState } from "State/Store";
import emoji from "@jukben/emoji-search";
import messages from "./messages";
import { unwrap } from "Util";
@ -197,6 +198,40 @@ const PreferencesPage = () => {
/>
</div>
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>
<FormattedMessage {...messages.ReactionEmoji} />
</div>
<small>
<FormattedMessage {...messages.ReactionEmojiHelp} />
</small>
</div>
<div>
<select
className="emoji-selector"
value={perf.reactionEmoji}
onChange={e =>
dispatch(
setPreferences({
...perf,
reactionEmoji: e.target.value,
} as UserPreferences)
)
}>
<option value="+">
+ <FormattedMessage {...messages.Default} />
</option>
{emoji("").map(({ name, char }) => {
return (
<option value={char}>
{name} {char}
</option>
);
})}
</select>
</div>
</div>
<div className="card flex">
<div className="flex f-col f-grow">
<div>

View File

@ -2,6 +2,7 @@ import { useState } from "react";
import { FormattedMessage } from "react-intl";
import { useDispatch, useSelector } from "react-redux";
import { randomSample } from "Util";
import Relay from "Element/Relay";
import useEventPublisher from "Feed/EventPublisher";
import { RootState } from "State/Store";
@ -20,6 +21,14 @@ const RelaySettingsPage = () => {
const ev = await publisher.saveRelays();
publisher.broadcast(ev);
publisher.broadcastForBootstrap(ev);
try {
const onlineRelays = await fetch("https://api.nostr.watch/v1/online").then(r => r.json());
const settingsEv = await publisher.saveRelaysSettings();
const rs = Object.keys(relays).concat(randomSample(onlineRelays, 20));
publisher.broadcastAll(settingsEv, rs);
} catch (error) {
console.error(error);
}
}
function addRelay() {

View File

@ -55,4 +55,6 @@ export default defineMessages({
DisplayName: { defaultMessage: "Display name" },
Buy: { defaultMessage: "Buy" },
Nip05: { defaultMessage: "NIP-05" },
ReactionEmoji: { defaultMessage: "Reaction emoji" },
ReactionEmojiHelp: { defaultMessage: "Emoji to send when reactiong to a note" },
});