From 6f3a0e83ced383bb447791d495a3577a0a1a1730 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 11 Jan 2023 13:05:32 +0000 Subject: [PATCH] Relay stats --- src/element/Relay.css | 16 +++++++++- src/element/Relay.js | 67 +++++++++++++++++++++++++++++------------ src/index.css | 12 ++++++++ src/nostr/Connection.js | 28 ++++++++++++++--- 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/src/element/Relay.css b/src/element/Relay.css index 7ff20731..a83c468c 100644 --- a/src/element/Relay.css +++ b/src/element/Relay.css @@ -1,10 +1,24 @@ .relay { - margin-bottom: 10px; + margin-top: 10px; background-color: var(--gray-secondary); border-radius: 5px; text-align: start; + display: grid; + grid-template-columns: min-content auto; + overflow: hidden; } .relay > div { padding: 5px; } + +.relay > div:first-child { +} + +.relay-extra { + padding: 5px; + margin: 0 5px; + background-color: var(--gray-tertiary); + border-radius: 0 0 5px 5px; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/element/Relay.js b/src/element/Relay.js index 7ab13828..f618f616 100644 --- a/src/element/Relay.js +++ b/src/element/Relay.js @@ -1,9 +1,9 @@ import "./Relay.css" -import { faPlug, faTrash, faSquareCheck, faSquareXmark } from "@fortawesome/free-solid-svg-icons"; +import { faPlug, faTrash, faSquareCheck, faSquareXmark, faWifi, faUpload, faDownload, faPlugCircleXmark, faEllipsisVertical } from "@fortawesome/free-solid-svg-icons"; import useRelayState from "../feed/RelayState"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { removeRelay, setRelays } from "../state/Login"; @@ -13,6 +13,7 @@ export default function Relay(props) { const relaySettings = useSelector(s => s.login.relays[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) { dispatch(setRelays({ @@ -20,31 +21,59 @@ export default function Relay(props) { })); } + + let latency = parseInt(state?.avgLatency ?? 0); return ( <> -
-
- +
+
+
- {name} -
- Write - configure({ write: !relaySettings.write, read: relaySettings.read })}> - - - Read - configure({ write: relaySettings.write, read: !relaySettings.read })}> - - +
+ {name} +
+ Write + configure({ write: !relaySettings.write, read: relaySettings.read })}> + + +
+
+ Read + configure({ write: relaySettings.write, read: !relaySettings.read })}> + + +
+
+
+
+ {latency > 2000 ? `${(latency / 1000).toFixed(0)} secs` : `${latency.toLocaleString()} ms`} +   + {state?.disconnects} +
+
+ setShowExtra(s => !s)}> + + +
-
- - dispatch(removeRelay(props.addr))} /> +
+ {showExtra ?
+
+ {state?.events.send} +
+
+ {state?.events.received} +
+ +
+ Delete + dispatch(removeRelay(props.addr))}> +
-
+
: null} ) } diff --git a/src/index.css b/src/index.css index 0134d982..a7744894 100644 --- a/src/index.css +++ b/src/index.css @@ -133,6 +133,10 @@ textarea:placeholder { flex: 1; } +.f-2 { + flex: 2; +} + .f-grow { flex-grow: 1; } @@ -271,6 +275,14 @@ body.scroll-lock { color: var(--error); } +.bg-error { + background-color: var(--error); +} + +.bg-success { + background-color: var(--success); +} + .root-tabs { padding: 0; align-items: center; diff --git a/src/nostr/Connection.js b/src/nostr/Connection.js index 5ab9536b..a8931341 100644 --- a/src/nostr/Connection.js +++ b/src/nostr/Connection.js @@ -12,6 +12,7 @@ export class ConnectionStats { this.SubsTimeout = 0; this.EventsReceived = 0; this.EventsSent = 0; + this.Disconnects = 0; } } @@ -28,7 +29,13 @@ export default class Connection { this.StateHooks = {}; this.HasStateChange = true; this.CurrentState = { - connected: false + connected: false, + disconnects: 0, + avgLatency: 0, + events: { + received: 0, + send: 0 + } }; this.LastState = Object.freeze({ ...this.CurrentState }); this.IsClosed = false; @@ -47,7 +54,7 @@ export default class Connection { Close() { this.IsClosed = true; - if(this.ReconnectTimer !== null) { + if (this.ReconnectTimer !== null) { clearTimeout(this.ReconnectTimer); this.ReconnectTimer = null; } @@ -74,6 +81,7 @@ export default class Connection { this.ReconnectTimer = setTimeout(() => { this.Connect(); }, this.ConnectTimeout); + this.Stats.Disconnects++; } else { console.log(`[${this.Address}] Closed!`); this.ReconnectTimer = null; @@ -88,6 +96,8 @@ export default class Connection { switch (tag) { case "EVENT": { this._OnEvent(msg[1], msg[2]); + this.Stats.EventsReceived++; + this._UpdateState(); break; } case "EOSE": { @@ -96,7 +106,7 @@ export default class Connection { } case "OK": { // feedback to broadcast call - console.debug("OK: ", msg[1]); + console.debug("OK: ", msg); break; } case "NOTICE": { @@ -126,6 +136,8 @@ export default class Connection { } let req = ["EVENT", e.ToObject()]; this._SendJson(req); + this.Stats.EventsSent++; + this._UpdateState(); } /** @@ -199,6 +211,11 @@ export default class Connection { _UpdateState() { this.CurrentState.connected = this.Socket?.readyState === WebSocket.OPEN; + this.CurrentState.events.received = this.Stats.EventsReceived; + 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.Stats.Latency = this.Stats.Latency.slice(this.Stats.Latency.length - 20); // trim this.HasStateChange = true; this._NotifyState(); } @@ -239,9 +256,10 @@ export default class Connection { console.warn(`[${this.Address}][${subId}] Slow response time ${(responseTime / 1000).toFixed(1)} seconds`); } sub.OnEnd(this); + this.Stats.Latency.push(responseTime); + this._UpdateState(); } else { - // console.warn(`No subscription for end! ${subId}`); - // ignored for now, track as "dropped event" with connection stats + console.warn(`No subscription for end! ${subId}`); } }