forked from Kieran/snort
feat: full relay info page
This commit is contained in:
parent
fe868a601a
commit
1af814b26a
@ -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<RootState, Record<string, RelaySettings>>(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) {
|
||||
<FontAwesomeIcon icon={faPlugCircleXmark} /> {state?.disconnects}
|
||||
</div>
|
||||
<div>
|
||||
<span className="icon-btn" onClick={() => setShowExtra(s => !s)}>
|
||||
<FontAwesomeIcon icon={faEllipsisVertical} />
|
||||
<span className="icon-btn" onClick={() => navigate(name)}>
|
||||
<FontAwesomeIcon icon={faGear} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showExtra ? <div className="flex relay-extra w-max">
|
||||
<div className="f-1">
|
||||
<FontAwesomeIcon icon={faUpload} /> {state?.events.send}
|
||||
</div>
|
||||
<div className="f-1">
|
||||
<FontAwesomeIcon icon={faDownload} /> {state?.events.received}
|
||||
</div>
|
||||
|
||||
<div className="f-1">
|
||||
Delete
|
||||
<span className="icon-btn" onClick={() => dispatch(removeRelay(props.addr))}>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</span>
|
||||
</div>
|
||||
</div> : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -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<StateSnapshot>) => 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<string, Subscriptions>;
|
||||
Settings: RelaySettings;
|
||||
Info?: RelayInfo;
|
||||
ConnectTimeout: number;
|
||||
Stats: ConnectionStats;
|
||||
StateHooks: Map<string, CustomHook>;
|
||||
@ -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();
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Stats class for tracking metrics per connection
|
||||
*/
|
||||
|
9
src/Nostr/RelayInfo.ts
Normal file
9
src/Nostr/RelayInfo.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface RelayInfo {
|
||||
name?: string,
|
||||
description?: string,
|
||||
pubkey?: string,
|
||||
contact?: string,
|
||||
supported_nips?: number[],
|
||||
software?: string,
|
||||
version?: string
|
||||
}
|
@ -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: <Relay />
|
||||
element: <Relay />,
|
||||
},
|
||||
{
|
||||
path: "relays/:addr",
|
||||
element: <RelayInfo />
|
||||
},
|
||||
{
|
||||
path: "preferences",
|
||||
|
57
src/Pages/settings/RelayInfo.tsx
Normal file
57
src/Pages/settings/RelayInfo.tsx
Normal file
@ -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 (
|
||||
<>
|
||||
<h3 className="pointer" onClick={() => navigate("/settings/relays")}>Relays</h3>
|
||||
<div className="card">
|
||||
<h3>{stats?.info?.name ?? addr}</h3>
|
||||
<p>{stats?.info?.description}</p>
|
||||
|
||||
{stats?.info?.pubkey && (<>
|
||||
<h4>Owner</h4>
|
||||
<ProfilePreview pubkey={parseId(stats.info.pubkey)} />
|
||||
</>)}
|
||||
{stats?.info?.software && (<div className="flex">
|
||||
<h4 className="f-grow">Software</h4>
|
||||
<div className="flex f-col">
|
||||
{stats.info.software.startsWith("http") ? <a href={stats.info.software} target="_blank" rel="noreferrer">{stats.info.software}</a> : <>{stats.info.software}</>}
|
||||
<small>{!stats.info.version?.startsWith("v") && "v"}{stats.info.version}</small>
|
||||
</div>
|
||||
</div>)}
|
||||
{stats?.info?.contact && (<div className="flex">
|
||||
<h4 className="f-grow">Contact</h4>
|
||||
<a href={`${stats.info.contact.startsWith("mailto:") ? "" : "mailto:"}${stats.info.contact}`} target="_blank" rel="noreferrer">{stats.info.contact}</a>
|
||||
</div>)}
|
||||
{stats?.info?.supported_nips && (<>
|
||||
<h4>Supports</h4>
|
||||
<div className="f-grow">
|
||||
{stats.info.supported_nips.map(a => <span className="pill" onClick={() => navigate(`https://github.com/nostr-protocol/nips/blob/master/${a.toString().padStart(2, "0")}.md`)}>NIP-{a.toString().padStart(2, "0")}</span>)}
|
||||
</div>
|
||||
</>)}
|
||||
<div className="flex mt10 f-end">
|
||||
<div className="btn error" onClick={() => {
|
||||
dispatch(removeRelay(con!.Address));
|
||||
navigate("/settings/relays")
|
||||
}}>Remove</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default RelayInfo;
|
@ -52,7 +52,7 @@ const RelaySettingsPage = () => {
|
||||
<div className="flex f-col">
|
||||
{Object.keys(relays || {}).map(a => <Relay addr={a} key={a} />)}
|
||||
</div>
|
||||
<div className="flex actions">
|
||||
<div className="flex mt10">
|
||||
<div className="f-grow"></div>
|
||||
<div className="btn" onClick={() => saveRelays()}>Save</div>
|
||||
</div>
|
||||
|
@ -222,6 +222,10 @@ textarea:placeholder {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
.f-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.w-max {
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
|
Loading…
Reference in New Issue
Block a user