mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-19 20:11:31 +02:00
add relay scores
This commit is contained in:
@@ -36,6 +36,8 @@ export enum RelayMode {
|
|||||||
}
|
}
|
||||||
export type RelayConfig = { url: string; mode: RelayMode };
|
export type RelayConfig = { url: string; mode: RelayMode };
|
||||||
|
|
||||||
|
const CONNECTION_TIMEOUT = 1000 * 30;
|
||||||
|
|
||||||
export class Relay {
|
export class Relay {
|
||||||
url: string;
|
url: string;
|
||||||
onOpen = new Subject<Relay>();
|
onOpen = new Subject<Relay>();
|
||||||
@@ -64,7 +66,25 @@ export class Relay {
|
|||||||
this.ws = new WebSocket(this.url);
|
this.ws = new WebSocket(this.url);
|
||||||
|
|
||||||
this.connectionTimer = relayScoreboardService.relayConnectionTime.get(this.url).createTimer();
|
this.connectionTimer = relayScoreboardService.relayConnectionTime.get(this.url).createTimer();
|
||||||
|
const connectionTimeout: number = window.setTimeout(() => {
|
||||||
|
// end the connection timer after CONNECTION_TIMEOUT
|
||||||
|
if (this.connectionTimer) {
|
||||||
|
this.connectionTimer();
|
||||||
|
this.connectionTimer = undefined;
|
||||||
|
}
|
||||||
|
// relayScoreboardService.relayTimeouts.get(this.url).addIncident();
|
||||||
|
}, CONNECTION_TIMEOUT);
|
||||||
|
|
||||||
|
// for local dev, cancel timeout if module reloads
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.prune(() => {
|
||||||
|
window.clearTimeout(connectionTimeout);
|
||||||
|
this.ws?.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.ws.onopen = () => {
|
this.ws.onopen = () => {
|
||||||
|
window.clearTimeout(connectionTimeout);
|
||||||
this.onOpen.next(this);
|
this.onOpen.next(this);
|
||||||
|
|
||||||
this.ejectTimer = relayScoreboardService.relayEjectTime.get(this.url).createTimer();
|
this.ejectTimer = relayScoreboardService.relayEjectTime.get(this.url).createTimer();
|
||||||
|
@@ -26,6 +26,7 @@ import { RelayIcon } from "./icons";
|
|||||||
import { Relay } from "../classes/relay";
|
import { Relay } from "../classes/relay";
|
||||||
import { RelayFavicon } from "./relay-favicon";
|
import { RelayFavicon } from "./relay-favicon";
|
||||||
import relayScoreboardService from "../services/relay-scoreboard";
|
import relayScoreboardService from "../services/relay-scoreboard";
|
||||||
|
import { RelayScoreBreakdown } from "./relay-score-breakdown";
|
||||||
|
|
||||||
export const ConnectedRelays = () => {
|
export const ConnectedRelays = () => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
@@ -63,11 +64,9 @@ export const ConnectedRelays = () => {
|
|||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Relay</Th>
|
<Th>Relay</Th>
|
||||||
<Th isNumeric>Claims</Th>
|
<Th>Claims</Th>
|
||||||
<Th isNumeric>Avg Connect</Th>
|
<Th>Score</Th>
|
||||||
<Th isNumeric>Avg Response</Th>
|
<Th>Status</Th>
|
||||||
<Th isNumeric>Avg Eject</Th>
|
|
||||||
<Th isNumeric>Status</Th>
|
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
@@ -79,11 +78,11 @@ export const ConnectedRelays = () => {
|
|||||||
<Text>{url}</Text>
|
<Text>{url}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Td>
|
</Td>
|
||||||
<Td isNumeric>{relayPoolService.getRelayClaims(url).size}</Td>
|
<Td>{relayPoolService.getRelayClaims(url).size}</Td>
|
||||||
<Td isNumeric>{relayScoreboardService.getAverageConnectionTime(url).toFixed(0)}</Td>
|
<Td>
|
||||||
<Td isNumeric>{relayScoreboardService.getAverageResponseTime(url).toFixed(0)}</Td>
|
<RelayScoreBreakdown relay={url} />
|
||||||
<Td isNumeric>{relayScoreboardService.getAverageEjectTime(url).toFixed(0)}</Td>
|
</Td>
|
||||||
<Td isNumeric>
|
<Td>
|
||||||
<RelayStatus url={url} />
|
<RelayStatus url={url} />
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
43
src/components/relay-score-breakdown.tsx
Normal file
43
src/components/relay-score-breakdown.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Popover,
|
||||||
|
PopoverArrow,
|
||||||
|
PopoverBody,
|
||||||
|
PopoverCloseButton,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
Text,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import relayScoreboardService from "../services/relay-scoreboard";
|
||||||
|
|
||||||
|
export function RelayScoreBreakdown({ relay }: { relay: string }) {
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Button variant="link">{relayScoreboardService.getRelayScore(relay)}</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<PopoverArrow />
|
||||||
|
<PopoverCloseButton />
|
||||||
|
<PopoverBody>
|
||||||
|
<Text>
|
||||||
|
Avg Connect: {relayScoreboardService.getAverageConnectionTime(relay).toFixed(0)} (
|
||||||
|
{relayScoreboardService.getConnectionTimeScore(relay)} pt)
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Avg Response: {relayScoreboardService.getAverageResponseTime(relay).toFixed(0)} (
|
||||||
|
{relayScoreboardService.getResponseTimeScore(relay)} pt)
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Avg Eject: {relayScoreboardService.getAverageEjectTime(relay).toFixed(0)} (
|
||||||
|
{relayScoreboardService.getEjectTimeScore(relay)} pt)
|
||||||
|
</Text>
|
||||||
|
{/* <Text>
|
||||||
|
Timeouts: {relayScoreboardService.getTimeoutCount(relay).toFixed(0)} (
|
||||||
|
{relayScoreboardService.getTimeoutsScore(relay)} pt)
|
||||||
|
</Text> */}
|
||||||
|
</PopoverBody>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
@@ -155,7 +155,9 @@ export default function ZapModal({
|
|||||||
document.removeEventListener("visibilitychange", listener);
|
document.removeEventListener("visibilitychange", listener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener("visibilitychange", listener);
|
setTimeout(() => {
|
||||||
|
document.addEventListener("visibilitychange", listener);
|
||||||
|
}, 1000 * 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
@@ -4,9 +4,9 @@ import { normalizeRelayConfigs } from "../helpers/relay";
|
|||||||
import { useUserContacts } from "./use-user-contacts";
|
import { useUserContacts } from "./use-user-contacts";
|
||||||
import { useUserRelays } from "./use-user-relays";
|
import { useUserRelays } from "./use-user-relays";
|
||||||
|
|
||||||
export default function useMergedUserRelays(pubkey: string) {
|
export default function useFallbackUserRelays(pubkey: string, alwaysFetch = false) {
|
||||||
const contacts = useUserContacts(pubkey);
|
const contacts = useUserContacts(pubkey, [], alwaysFetch);
|
||||||
const userRelays = useUserRelays(pubkey);
|
const userRelays = useUserRelays(pubkey, [], alwaysFetch);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
let relays: RelayConfig[] = userRelays?.relays ?? [];
|
let relays: RelayConfig[] = userRelays?.relays ?? [];
|
@@ -37,6 +37,7 @@ export interface CustomSchema extends DBSchema {
|
|||||||
responseTimes?: [number, Date][];
|
responseTimes?: [number, Date][];
|
||||||
ejectTimes?: [number, Date][];
|
ejectTimes?: [number, Date][];
|
||||||
connectionTimes?: [number, Date][];
|
connectionTimes?: [number, Date][];
|
||||||
|
timeouts?: Date[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
settings: {
|
settings: {
|
||||||
|
@@ -67,10 +67,18 @@ export class RelayPoolService {
|
|||||||
const relayPoolService = new RelayPoolService();
|
const relayPoolService = new RelayPoolService();
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
relayPoolService.reconnectRelays();
|
if (document.visibilityState === "visible") {
|
||||||
relayPoolService.pruneRelays();
|
relayPoolService.reconnectRelays();
|
||||||
|
// relayPoolService.pruneRelays();
|
||||||
|
}
|
||||||
}, 1000 * 15);
|
}, 1000 * 15);
|
||||||
|
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.visibilityState === "visible") {
|
||||||
|
relayPoolService.reconnectRelays();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.relayPoolService = relayPoolService;
|
window.relayPoolService = relayPoolService;
|
||||||
|
@@ -2,12 +2,17 @@ import moment from "moment";
|
|||||||
import { SuperMap } from "../classes/super-map";
|
import { SuperMap } from "../classes/super-map";
|
||||||
import db from "./db";
|
import db from "./db";
|
||||||
|
|
||||||
|
function clamp(v: number, min: number, max: number) {
|
||||||
|
return Math.min(Math.max(v, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
interface PersistentMeasure {
|
interface PersistentMeasure {
|
||||||
load(data: any): this;
|
load(data: any): this;
|
||||||
save(): any;
|
save(): any;
|
||||||
}
|
}
|
||||||
interface RelayMeasure {
|
interface RelayMeasure {
|
||||||
relay: string;
|
relay: string;
|
||||||
|
reset(): this;
|
||||||
prune(cutOff: Date): this;
|
prune(cutOff: Date): this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +27,15 @@ class IncidentMeasure implements RelayMeasure, PersistentMeasure {
|
|||||||
addIncident(date: Date = new Date()) {
|
addIncident(date: Date = new Date()) {
|
||||||
this.incidents.unshift(date);
|
this.incidents.unshift(date);
|
||||||
}
|
}
|
||||||
|
getCount(since?: Date) {
|
||||||
|
const points = since ? this.incidents.filter((d) => d > since) : this.incidents;
|
||||||
|
return points.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.incidents = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
prune(cutOff: Date): this {
|
prune(cutOff: Date): this {
|
||||||
while (true) {
|
while (true) {
|
||||||
const last = this.incidents.pop();
|
const last = this.incidents.pop();
|
||||||
@@ -60,13 +73,21 @@ class TimeMeasure implements RelayMeasure, PersistentMeasure {
|
|||||||
addTime(time: number, date: Date = new Date()) {
|
addTime(time: number, date: Date = new Date()) {
|
||||||
this.measures.unshift([time, date]);
|
this.measures.unshift([time, date]);
|
||||||
}
|
}
|
||||||
|
getCount(since?: Date){
|
||||||
|
const points = since ? this.measures.filter((m) => m[1] > since) : this.measures;
|
||||||
|
return points.length
|
||||||
|
}
|
||||||
getAverage(since?: Date, undef: number = Infinity) {
|
getAverage(since?: Date, undef: number = Infinity) {
|
||||||
const points = since ? this.measures.filter((m) => m[1] > since) : this.measures;
|
const points = since ? this.measures.filter((m) => m[1] > since) : this.measures;
|
||||||
if (points.length === 0) return Infinity;
|
if (points.length === 0) return undef;
|
||||||
const total = points.reduce((total, [time]) => total + time, 0);
|
const total = points.reduce((total, [time]) => total + time, 0);
|
||||||
return total / points.length;
|
return total / points.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.measures = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
prune(cutOff: Date): this {
|
prune(cutOff: Date): this {
|
||||||
while (true) {
|
while (true) {
|
||||||
const last = this.measures.pop();
|
const last = this.measures.pop();
|
||||||
@@ -90,15 +111,21 @@ class TimeMeasure implements RelayMeasure, PersistentMeasure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RelayScoreboardService {
|
class RelayScoreboardService {
|
||||||
|
/** the time it takes for relays to respond to queries */
|
||||||
relayResponseTimes = new SuperMap<string, TimeMeasure>((relay) => new TimeMeasure(relay));
|
relayResponseTimes = new SuperMap<string, TimeMeasure>((relay) => new TimeMeasure(relay));
|
||||||
|
/** the time it takes before the relay closes the connection */
|
||||||
relayEjectTime = new SuperMap<string, TimeMeasure>((relay) => new TimeMeasure(relay));
|
relayEjectTime = new SuperMap<string, TimeMeasure>((relay) => new TimeMeasure(relay));
|
||||||
|
/** the time it takes to connect to the relay */
|
||||||
relayConnectionTime = new SuperMap<string, TimeMeasure>((relay) => new TimeMeasure(relay));
|
relayConnectionTime = new SuperMap<string, TimeMeasure>((relay) => new TimeMeasure(relay));
|
||||||
|
/** the number of times the connection has timed out */
|
||||||
|
// relayTimeouts = new SuperMap<string, IncidentMeasure>((relay) => new IncidentMeasure(relay));
|
||||||
|
|
||||||
prune() {
|
prune() {
|
||||||
const cutOff = moment().subtract(1, "week").toDate();
|
const cutOff = moment().subtract(1, "week").toDate();
|
||||||
for (const [relay, measure] of this.relayResponseTimes) measure.prune(cutOff);
|
for (const [relay, measure] of this.relayResponseTimes) measure.prune(cutOff);
|
||||||
for (const [relay, measure] of this.relayEjectTime) measure.prune(cutOff);
|
for (const [relay, measure] of this.relayEjectTime) measure.prune(cutOff);
|
||||||
for (const [relay, measure] of this.relayConnectionTime) measure.prune(cutOff);
|
for (const [relay, measure] of this.relayConnectionTime) measure.prune(cutOff);
|
||||||
|
// for (const [relay, measure] of this.relayTimeouts) measure.prune(cutOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAverageResponseTime(relay: string, since?: Date) {
|
getAverageResponseTime(relay: string, since?: Date) {
|
||||||
@@ -110,26 +137,71 @@ class RelayScoreboardService {
|
|||||||
getAverageConnectionTime(relay: string, since?: Date) {
|
getAverageConnectionTime(relay: string, since?: Date) {
|
||||||
return this.relayConnectionTime.get(relay).getAverage(since);
|
return this.relayConnectionTime.get(relay).getAverage(since);
|
||||||
}
|
}
|
||||||
|
// getTimeoutCount(relay: string, since?: Date) {
|
||||||
|
// return this.relayTimeouts.get(relay).getCount(since);
|
||||||
|
// }
|
||||||
|
|
||||||
|
hasConnected(relay: string, since?: Date){
|
||||||
|
return this.relayConnectionTime.get(relay).getCount(since)>0;
|
||||||
|
}
|
||||||
|
getResponseTimeScore(relay: string, since?: Date) {
|
||||||
|
const responseTime = this.getAverageResponseTime(relay, since);
|
||||||
|
const connected = this.hasConnected(relay, since);
|
||||||
|
|
||||||
|
// no points if we have never connected
|
||||||
|
if (!connected) return 0;
|
||||||
|
|
||||||
|
// 1 point (max 10) for ever 10 ms under 1000. negative points for over 1000
|
||||||
|
return clamp(Math.round(-(responseTime - 1000) / 100), -10, 10);
|
||||||
|
}
|
||||||
|
getConnectionTimeScore(relay: string, since?: Date) {
|
||||||
|
const connectionTime = this.getAverageConnectionTime(relay, since);
|
||||||
|
|
||||||
|
// no points if we have never connected
|
||||||
|
if (connectionTime === Infinity) return 0;
|
||||||
|
|
||||||
|
// 1 point (max 10) for ever 10 ms under 1000. negative points for over 1000
|
||||||
|
return clamp(Math.round(-(connectionTime - 1000) / 100), -10, 10);
|
||||||
|
}
|
||||||
|
getEjectTimeScore(relay: string, since?: Date) {
|
||||||
|
const ejectTime = this.getAverageEjectTime(relay, since);
|
||||||
|
const connected = this.hasConnected(relay, since);
|
||||||
|
|
||||||
|
// no points if we have never connected
|
||||||
|
if (!connected) return 0;
|
||||||
|
|
||||||
|
let score = 0;
|
||||||
|
if (ejectTime > 1000 * 20) score += 1;
|
||||||
|
if (ejectTime > 1000 * 60) score += 4;
|
||||||
|
if (ejectTime > 1000 * 120) score += 5;
|
||||||
|
if (ejectTime > 1000 * 200) score += 5;
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
// getTimeoutsScore(relay: string, since?: Date) {
|
||||||
|
// const timeouts = this.getTimeoutCount(relay, since);
|
||||||
|
// // subtract 5 points for ever time its timed out
|
||||||
|
// return -(timeouts * 5);
|
||||||
|
// }
|
||||||
|
getRelayScore(relay: string, since?: Date) {
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
score += this.getResponseTimeScore(relay, since);
|
||||||
|
score += this.getConnectionTimeScore(relay, since);
|
||||||
|
score += this.getEjectTimeScore(relay, since);
|
||||||
|
// score += this.getTimeoutsScore(relay, since);
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
getRankedRelays(customRelays?: string[]) {
|
getRankedRelays(customRelays?: string[]) {
|
||||||
const relays = customRelays ?? this.getRelays();
|
const relays = customRelays ?? this.getRelays();
|
||||||
const relayAverageResponseTime = new SuperMap<string, number>(() => 0);
|
const relayScores = new Map<string, number>();
|
||||||
const relayAverageConnectionTime = new SuperMap<string, number>(() => 0);
|
|
||||||
const relayAverageEjectTime = new SuperMap<string, number>(() => 0);
|
|
||||||
|
|
||||||
for (const relay of relays) {
|
for (const relay of relays) {
|
||||||
relayAverageResponseTime.set(relay, this.relayResponseTimes.get(relay).getAverage());
|
relayScores.set(relay, this.getRelayScore(relay));
|
||||||
relayAverageConnectionTime.set(relay, this.relayConnectionTime.get(relay).getAverage());
|
|
||||||
relayAverageEjectTime.set(relay, this.relayEjectTime.get(relay).getAverage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return relays.sort((a, b) => {
|
return relays.sort((a, b) => (relayScores.get(b) ?? 0) - (relayScores.get(a) ?? 0));
|
||||||
let diff = 0;
|
|
||||||
diff += Math.sign(relayAverageResponseTime.get(a) - relayAverageResponseTime.get(b));
|
|
||||||
diff += Math.sign(relayAverageConnectionTime.get(a) - relayAverageConnectionTime.get(b)) / 2;
|
|
||||||
diff += Math.sign(relayAverageEjectTime.get(b) - relayAverageEjectTime.get(a));
|
|
||||||
return diff;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRelays() {
|
private getRelays() {
|
||||||
@@ -137,9 +209,17 @@ class RelayScoreboardService {
|
|||||||
for (const [relay, measure] of this.relayResponseTimes) relays.add(relay);
|
for (const [relay, measure] of this.relayResponseTimes) relays.add(relay);
|
||||||
for (const [relay, measure] of this.relayEjectTime) relays.add(relay);
|
for (const [relay, measure] of this.relayEjectTime) relays.add(relay);
|
||||||
for (const [relay, measure] of this.relayConnectionTime) relays.add(relay);
|
for (const [relay, measure] of this.relayConnectionTime) relays.add(relay);
|
||||||
|
// for (const [relay, measure] of this.relayTimeouts) relays.add(relay);
|
||||||
return Array.from(relays);
|
return Array.from(relays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetScores() {
|
||||||
|
this.relayResponseTimes.forEach((m) => m.reset());
|
||||||
|
this.relayEjectTime.forEach((m) => m.reset());
|
||||||
|
this.relayConnectionTime.forEach((m) => m.reset());
|
||||||
|
// this.relayTimeouts.forEach((m) => m.reset());
|
||||||
|
}
|
||||||
|
|
||||||
async loadStats() {
|
async loadStats() {
|
||||||
const stats = await db.getAll("relayScoreboardStats");
|
const stats = await db.getAll("relayScoreboardStats");
|
||||||
|
|
||||||
@@ -147,6 +227,7 @@ class RelayScoreboardService {
|
|||||||
this.relayResponseTimes.get(relayStats.relay).load(relayStats.responseTimes);
|
this.relayResponseTimes.get(relayStats.relay).load(relayStats.responseTimes);
|
||||||
this.relayEjectTime.get(relayStats.relay).load(relayStats.ejectTimes);
|
this.relayEjectTime.get(relayStats.relay).load(relayStats.ejectTimes);
|
||||||
this.relayConnectionTime.get(relayStats.relay).load(relayStats.connectionTimes);
|
this.relayConnectionTime.get(relayStats.relay).load(relayStats.connectionTimes);
|
||||||
|
// this.relayTimeouts.get(relayStats.relay).load(relayStats.timeouts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +238,7 @@ class RelayScoreboardService {
|
|||||||
const responseTimes = this.relayResponseTimes.get(relay).save();
|
const responseTimes = this.relayResponseTimes.get(relay).save();
|
||||||
const ejectTimes = this.relayEjectTime.get(relay).save();
|
const ejectTimes = this.relayEjectTime.get(relay).save();
|
||||||
const connectionTimes = this.relayConnectionTime.get(relay).save();
|
const connectionTimes = this.relayConnectionTime.get(relay).save();
|
||||||
|
// const timeouts = this.relayTimeouts.get(relay).save();
|
||||||
transaction.store.put({ relay, responseTimes, ejectTimes, connectionTimes });
|
transaction.store.put({ relay, responseTimes, ejectTimes, connectionTimes });
|
||||||
}
|
}
|
||||||
await transaction.done;
|
await transaction.done;
|
||||||
|
@@ -24,8 +24,8 @@ import { useList } from "react-use";
|
|||||||
import { RelayUrlInput } from "../../components/relay-url-input";
|
import { RelayUrlInput } from "../../components/relay-url-input";
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import { RelayStatus } from "../../components/relay-status";
|
import { RelayStatus } from "../../components/relay-status";
|
||||||
import relayScoreboardService from "../../services/relay-scoreboard";
|
|
||||||
import { normalizeRelayUrl } from "../../helpers/url";
|
import { normalizeRelayUrl } from "../../helpers/url";
|
||||||
|
import { RelayScoreBreakdown } from "../../components/relay-score-breakdown";
|
||||||
|
|
||||||
export default function RelaysView() {
|
export default function RelaysView() {
|
||||||
const relays = useSubject(clientRelaysService.relays);
|
const relays = useSubject(clientRelaysService.relays);
|
||||||
@@ -81,9 +81,7 @@ export default function RelaysView() {
|
|||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Url</Th>
|
<Th>Url</Th>
|
||||||
<Th isNumeric>Avg Connect</Th>
|
<Th>Score</Th>
|
||||||
<Th isNumeric>Avg Response</Th>
|
|
||||||
<Th isNumeric>Avg Eject</Th>
|
|
||||||
<Th>Status</Th>
|
<Th>Status</Th>
|
||||||
<Th></Th>
|
<Th></Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
@@ -97,9 +95,9 @@ export default function RelaysView() {
|
|||||||
<Text>{relay.url}</Text>
|
<Text>{relay.url}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Td>
|
</Td>
|
||||||
<Td isNumeric>{relayScoreboardService.getAverageConnectionTime(relay.url).toFixed(0)}</Td>
|
<Td>
|
||||||
<Td isNumeric>{relayScoreboardService.getAverageResponseTime(relay.url).toFixed(0)}</Td>
|
<RelayScoreBreakdown relay={relay.url}/>
|
||||||
<Td isNumeric>{relayScoreboardService.getAverageEjectTime(relay.url).toFixed(0)}</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<RelayStatus url={relay.url} />
|
<RelayStatus url={relay.url} />
|
||||||
</Td>
|
</Td>
|
||||||
|
@@ -15,13 +15,13 @@ import { truncatedId } from "../../../helpers/nostr-event";
|
|||||||
import { fixWebsiteUrl, getUserDisplayName } from "../../../helpers/user-metadata";
|
import { fixWebsiteUrl, getUserDisplayName } from "../../../helpers/user-metadata";
|
||||||
import { useCurrentAccount } from "../../../hooks/use-current-account";
|
import { useCurrentAccount } from "../../../hooks/use-current-account";
|
||||||
import { useIsMobile } from "../../../hooks/use-is-mobile";
|
import { useIsMobile } from "../../../hooks/use-is-mobile";
|
||||||
import useMergedUserRelays from "../../../hooks/use-merged-user-relays";
|
import useFallbackUserRelays from "../../../hooks/use-fallback-user-relays";
|
||||||
import { useUserMetadata } from "../../../hooks/use-user-metadata";
|
import { useUserMetadata } from "../../../hooks/use-user-metadata";
|
||||||
import relayScoreboardService from "../../../services/relay-scoreboard";
|
import relayScoreboardService from "../../../services/relay-scoreboard";
|
||||||
import { UserProfileMenu } from "./user-profile-menu";
|
import { UserProfileMenu } from "./user-profile-menu";
|
||||||
|
|
||||||
function useUserShareLink(pubkey: string) {
|
function useUserShareLink(pubkey: string) {
|
||||||
const userRelays = useMergedUserRelays(pubkey);
|
const userRelays = useFallbackUserRelays(pubkey);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const writeUrls = userRelays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
const writeUrls = userRelays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
||||||
|
@@ -22,17 +22,15 @@ import { RelayMode } from "../../classes/relay";
|
|||||||
import { RelayIcon } from "../../components/icons";
|
import { RelayIcon } from "../../components/icons";
|
||||||
import { Note } from "../../components/note";
|
import { Note } from "../../components/note";
|
||||||
import { isNote } from "../../helpers/nostr-event";
|
import { isNote } from "../../helpers/nostr-event";
|
||||||
import useMergedUserRelays from "../../hooks/use-merged-user-relays";
|
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
|
||||||
|
import useRankedRelayConfigs from "../../hooks/use-ranked-relay-configs";
|
||||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||||
|
|
||||||
const UserNotesTab = () => {
|
const UserNotesTab = () => {
|
||||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||||
const userRelays = useMergedUserRelays(pubkey);
|
const userRelays = useFallbackUserRelays(pubkey).filter((r) => r.mode & RelayMode.WRITE);
|
||||||
const relays = userRelays
|
const ranked = useRankedRelayConfigs(userRelays);
|
||||||
.filter((r) => r.mode & RelayMode.WRITE)
|
const relays = ranked.map((r) => r.url).slice(0, 4) as string[];
|
||||||
.map((r) => r.url)
|
|
||||||
.filter(Boolean)
|
|
||||||
.slice(0, 4) as string[];
|
|
||||||
|
|
||||||
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
|
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
|
||||||
|
|
||||||
|
@@ -1,21 +1,24 @@
|
|||||||
import { Text, Box, IconButton, Flex, Badge } from "@chakra-ui/react";
|
import { Text, Box, IconButton, Flex, Badge } from "@chakra-ui/react";
|
||||||
import { useNavigate, useOutletContext } from "react-router-dom";
|
import { useNavigate, useOutletContext } from "react-router-dom";
|
||||||
import { GlobalIcon } from "../../components/icons";
|
import { GlobalIcon } from "../../components/icons";
|
||||||
import relayScoreboardService from "../../services/relay-scoreboard";
|
|
||||||
import { RelayMode } from "../../classes/relay";
|
import { RelayMode } from "../../classes/relay";
|
||||||
import useMergedUserRelays from "../../hooks/use-merged-user-relays";
|
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
|
||||||
|
import { RelayScoreBreakdown } from "../../components/relay-score-breakdown";
|
||||||
|
import useRankedRelayConfigs from "../../hooks/use-ranked-relay-configs";
|
||||||
|
|
||||||
const UserRelaysTab = () => {
|
const UserRelaysTab = () => {
|
||||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||||
const userRelays = useMergedUserRelays(pubkey);
|
const userRelays = useFallbackUserRelays(pubkey);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const ranked = useRankedRelayConfigs(userRelays);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
{userRelays.map((relayConfig) => (
|
{ranked.map((relayConfig) => (
|
||||||
<Box key={relayConfig.url} display="flex" gap="2" alignItems="center" pr="2" pl="2">
|
<Box key={relayConfig.url} display="flex" gap="2" alignItems="center" pr="2" pl="2">
|
||||||
<Text flex={1}>{relayConfig.url}</Text>
|
<Text flex={1}>{relayConfig.url}</Text>
|
||||||
<Text>{relayScoreboardService.getAverageResponseTime(relayConfig.url).toFixed(2)}ms</Text>
|
<RelayScoreBreakdown relay={relayConfig.url} />
|
||||||
{relayConfig.mode & RelayMode.WRITE ? <Badge colorScheme="green">Write</Badge> : null}
|
{relayConfig.mode & RelayMode.WRITE ? <Badge colorScheme="green">Write</Badge> : null}
|
||||||
{relayConfig.mode & RelayMode.READ ? <Badge>Read</Badge> : null}
|
{relayConfig.mode & RelayMode.READ ? <Badge>Read</Badge> : null}
|
||||||
<IconButton
|
<IconButton
|
||||||
|
Reference in New Issue
Block a user