diff --git a/src/Element/Relay.tsx b/src/Element/Relay.tsx index a133aec7..92b63fc4 100644 --- a/src/Element/Relay.tsx +++ b/src/Element/Relay.tsx @@ -1,6 +1,6 @@ import "./Relay.css" -import { faPlug, faTrash, faSquareCheck, faSquareXmark, faWifi, faUpload, faDownload, faPlugCircleXmark, faEllipsisVertical } from "@fortawesome/free-solid-svg-icons"; +import { faPlug, faTrash, faSquareCheck, faSquareXmark, faWifi, faPlugCircleXmark, faGear } from "@fortawesome/free-solid-svg-icons"; import useRelayState from "Feed/RelayState"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useMemo, useState } from "react"; @@ -8,6 +8,7 @@ import { useDispatch, useSelector } from "react-redux"; import { removeRelay, setRelays } from "State/Login"; import { RootState } from "State/Store"; import { RelaySettings } from "Nostr/Connection"; +import { useNavigate } from "react-router-dom"; export interface RelayProps { addr: string @@ -15,11 +16,11 @@ export interface RelayProps { export default function Relay(props: RelayProps) { const dispatch = useDispatch(); + const navigate = useNavigate(); const allRelaySettings = useSelector>(s => s.login.relays); const relaySettings = allRelaySettings[props.addr]; const state = useRelayState(props.addr); const name = useMemo(() => new URL(props.addr).host, [props.addr]); - const [showExtra, setShowExtra] = useState(false); function configure(o: RelaySettings) { dispatch(setRelays({ @@ -62,28 +63,13 @@ export default function Relay(props: RelayProps) { {state?.disconnects}
- setShowExtra(s => !s)}> - + navigate(name)}> +
- {showExtra ?
-
- {state?.events.send} -
-
- {state?.events.received} -
- -
- Delete - dispatch(removeRelay(props.addr))}> - - -
-
: null} ) } diff --git a/src/Nostr/Connection.ts b/src/Nostr/Connection.ts index 1ddd14f8..49819622 100644 --- a/src/Nostr/Connection.ts +++ b/src/Nostr/Connection.ts @@ -6,6 +6,7 @@ import { default as NEvent } from "Nostr/Event"; import { DefaultConnectTimeout } from "Const"; import { ConnectionStats } from "Nostr/ConnectionStats"; import { RawEvent, TaggedRawEvent, u256 } from "Nostr"; +import { RelayInfo } from "./RelayInfo"; export type CustomHook = (state: Readonly) => void; @@ -27,7 +28,8 @@ export type StateSnapshot = { events: { received: number, send: number - } + }, + info?: RelayInfo }; export default class Connection { @@ -36,6 +38,7 @@ export default class Connection { Pending: Subscriptions[]; Subscriptions: Map; Settings: RelaySettings; + Info?: RelayInfo; ConnectTimeout: number; Stats: ConnectionStats; StateHooks: Map; @@ -72,7 +75,29 @@ export default class Connection { this.Connect(); } - Connect() { + async Connect() { + try { + if (this.Info === undefined) { + let u = new URL(this.Address); + let rsp = await fetch(`https://${u.host}`, { + headers: { + "accept": "application/nostr+json" + } + }); + if (rsp.ok) { + let data = await rsp.json(); + for (let [k, v] of Object.entries(data)) { + if (v === "unset" || v === "") { + data[k] = undefined; + } + } + this.Info = data; + } + } + } catch (e) { + console.warn("Could not load relay information", e); + } + this.IsClosed = false; this.Socket = new WebSocket(this.Address); this.Socket.onopen = (e) => this.OnOpen(e); @@ -259,6 +284,7 @@ export default class Connection { this.CurrentState.events.send = this.Stats.EventsSent; this.CurrentState.avgLatency = this.Stats.Latency.length > 0 ? (this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length) : 0; this.CurrentState.disconnects = this.Stats.Disconnects; + this.CurrentState.info = this.Info; this.Stats.Latency = this.Stats.Latency.slice(-20); // trim this.HasStateChange = true; this._NotifyState(); diff --git a/src/Nostr/ConnectionStats.ts b/src/Nostr/ConnectionStats.ts index 0de56615..844e9dd0 100644 --- a/src/Nostr/ConnectionStats.ts +++ b/src/Nostr/ConnectionStats.ts @@ -1,4 +1,3 @@ - /** * Stats class for tracking metrics per connection */ diff --git a/src/Nostr/RelayInfo.ts b/src/Nostr/RelayInfo.ts new file mode 100644 index 00000000..21d01cef --- /dev/null +++ b/src/Nostr/RelayInfo.ts @@ -0,0 +1,9 @@ +export interface RelayInfo { + name?: string, + description?: string, + pubkey?: string, + contact?: string, + supported_nips?: number[], + software?: string, + version?: string +} \ No newline at end of file diff --git a/src/Pages/SettingsPage.tsx b/src/Pages/SettingsPage.tsx index aa9c89f5..aa60cd87 100644 --- a/src/Pages/SettingsPage.tsx +++ b/src/Pages/SettingsPage.tsx @@ -3,6 +3,7 @@ import SettingsIndex from "Pages/settings/Index"; import Profile from "Pages/settings/Profile"; import Relay from "Pages/settings/Relays"; import Preferences from "Pages/settings/Preferences"; +import RelayInfo from "Pages/settings/RelayInfo"; export default function SettingsPage() { const navigate = useNavigate(); @@ -26,7 +27,11 @@ export const SettingsRoutes: RouteObject[] = [ }, { path: "relays", - element: + element: , + }, + { + path: "relays/:addr", + element: }, { path: "preferences", diff --git a/src/Pages/settings/RelayInfo.tsx b/src/Pages/settings/RelayInfo.tsx new file mode 100644 index 00000000..2dd6aa7b --- /dev/null +++ b/src/Pages/settings/RelayInfo.tsx @@ -0,0 +1,57 @@ +import ProfilePreview from "Element/ProfilePreview"; +import useRelayState from "Feed/RelayState"; +import { System } from "Nostr/System"; +import { useDispatch } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { removeRelay } from "State/Login"; +import { parseId } from "Util"; + +const RelayInfo = () => { + const params = useParams(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const addr: string = `wss://${params.addr}`; + + const con = System.Sockets.get(addr) ?? System.Sockets.get(`${addr}/`); + const stats = useRelayState(con?.Address ?? addr); + + return ( + <> +

navigate("/settings/relays")}>Relays

+
+

{stats?.info?.name ?? addr}

+

{stats?.info?.description}

+ + {stats?.info?.pubkey && (<> +

Owner

+ + )} + {stats?.info?.software && (
+

Software

+
+ {stats.info.software.startsWith("http") ? {stats.info.software} : <>{stats.info.software}} + {!stats.info.version?.startsWith("v") && "v"}{stats.info.version} +
+
)} + {stats?.info?.contact && (
+

Contact

+ {stats.info.contact} +
)} + {stats?.info?.supported_nips && (<> +

Supports

+
+ {stats.info.supported_nips.map(a => navigate(`https://github.com/nostr-protocol/nips/blob/master/${a.toString().padStart(2, "0")}.md`)}>NIP-{a.toString().padStart(2, "0")})} +
+ )} +
+
{ + dispatch(removeRelay(con!.Address)); + navigate("/settings/relays") + }}>Remove
+
+
+ + ) +} + +export default RelayInfo; \ No newline at end of file diff --git a/src/Pages/settings/Relays.tsx b/src/Pages/settings/Relays.tsx index 3ca0fca4..b5e2d241 100644 --- a/src/Pages/settings/Relays.tsx +++ b/src/Pages/settings/Relays.tsx @@ -52,7 +52,7 @@ const RelaySettingsPage = () => {
{Object.keys(relays || {}).map(a => )}
-
+
saveRelays()}>Save
diff --git a/src/index.css b/src/index.css index 38e9ae7a..d4328f8a 100644 --- a/src/index.css +++ b/src/index.css @@ -222,6 +222,10 @@ textarea:placeholder { align-items: flex-start !important; } +.f-end { + justify-content: flex-end; +} + .w-max { width: 100%; width: -moz-available;