feat: reliable relays
This commit is contained in:
@ -1,9 +1,10 @@
|
|||||||
import { dedupe } from "@snort/shared";
|
import { dedupe, removeUndefined, sanitizeRelayUrl } from "@snort/shared";
|
||||||
import { OutboxModel } from "@snort/system";
|
import { OutboxModel } from "@snort/system";
|
||||||
import { SnortContext } from "@snort/system-react";
|
import { SnortContext } from "@snort/system-react";
|
||||||
import { useContext, useMemo } from "react";
|
import { useContext, useMemo, useSyncExternalStore } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
import { RelayMetrics } from "@/Cache";
|
||||||
import AsyncButton from "@/Components/Button/AsyncButton";
|
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||||
import { CollapsedSection } from "@/Components/Collapsed";
|
import { CollapsedSection } from "@/Components/Collapsed";
|
||||||
import { RelayFavicon } from "@/Components/Relay/RelaysMetadata";
|
import { RelayFavicon } from "@/Components/Relay/RelaysMetadata";
|
||||||
@ -11,6 +12,7 @@ import useLogin from "@/Hooks/useLogin";
|
|||||||
import { getRelayName } from "@/Utils";
|
import { getRelayName } from "@/Utils";
|
||||||
|
|
||||||
import RelayUptime from "./uptime";
|
import RelayUptime from "./uptime";
|
||||||
|
import UptimeLabel from "./uptime-label";
|
||||||
|
|
||||||
export function DiscoverRelays() {
|
export function DiscoverRelays() {
|
||||||
const { follows, relays, state } = useLogin(l => ({
|
const { follows, relays, state } = useLogin(l => ({
|
||||||
@ -28,13 +30,38 @@ export function DiscoverRelays() {
|
|||||||
.filter(a => !(relays?.some(b => b.url === a.key) ?? false));
|
.filter(a => !(relays?.some(b => b.url === a.key) ?? false));
|
||||||
}, [follows, relays]);
|
}, [follows, relays]);
|
||||||
|
|
||||||
|
const metrics = useSyncExternalStore(
|
||||||
|
c => RelayMetrics.hook(c, "*"),
|
||||||
|
() => RelayMetrics.snapshot(),
|
||||||
|
);
|
||||||
|
/// Using collected relay metrics
|
||||||
|
const reliableRelays = useMemo(
|
||||||
|
() =>
|
||||||
|
removeUndefined(
|
||||||
|
RelayMetrics.snapshot().map(a => {
|
||||||
|
const addr = sanitizeRelayUrl(a.addr);
|
||||||
|
if (!addr) return;
|
||||||
|
return {
|
||||||
|
...a,
|
||||||
|
addr,
|
||||||
|
avgLatency: a.latency.reduce((acc, v) => acc + v, 0) / a.latency.length,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter(a => a.connects > 0 && a.addr.startsWith("wss://") && !relays?.some(b => b.url === a.addr))
|
||||||
|
.sort((a, b) =>
|
||||||
|
(isNaN(b.avgLatency) ? 99999 : b.avgLatency) > (isNaN(a.avgLatency) ? 99999 : a.avgLatency) ? -1 : 1,
|
||||||
|
),
|
||||||
|
[relays, metrics],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-4">
|
||||||
<CollapsedSection
|
<CollapsedSection
|
||||||
title={
|
title={
|
||||||
<h4>
|
<div className="text-xl">
|
||||||
<FormattedMessage defaultMessage="Recommended Relays" />
|
<FormattedMessage defaultMessage="Popular Relays" />
|
||||||
</h4>
|
</div>
|
||||||
}>
|
}>
|
||||||
<small>
|
<small>
|
||||||
<FormattedMessage defaultMessage="Popular relays used by people you follow." />
|
<FormattedMessage defaultMessage="Popular relays used by people you follow." />
|
||||||
@ -84,6 +111,51 @@ export function DiscoverRelays() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</CollapsedSection>
|
</CollapsedSection>
|
||||||
|
<CollapsedSection
|
||||||
|
title={
|
||||||
|
<div className="text-xl">
|
||||||
|
<FormattedMessage defaultMessage="Reliable Relays" />
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<small>
|
||||||
|
<FormattedMessage defaultMessage="Relays which you have connected to before and appear to be reliable." />
|
||||||
|
</small>
|
||||||
|
<table className="table">
|
||||||
|
<thead>
|
||||||
|
<tr className="text-gray-light uppercase">
|
||||||
|
<th>
|
||||||
|
<FormattedMessage defaultMessage="Relay" description="Relay name (URL)" />
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage defaultMessage="Uptime" />
|
||||||
|
</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{reliableRelays.slice(0, 40).map(a => (
|
||||||
|
<tr key={a.addr}>
|
||||||
|
<td className="flex gap-2 items-center" title={a.addr}>
|
||||||
|
<RelayFavicon url={a.addr} />
|
||||||
|
{getRelayName(a.addr)}
|
||||||
|
</td>
|
||||||
|
<td className="text-center">
|
||||||
|
<UptimeLabel avgPing={a.avgLatency} />
|
||||||
|
</td>
|
||||||
|
<td className="text-end">
|
||||||
|
<AsyncButton
|
||||||
|
className="!py-1 mb-1"
|
||||||
|
onClick={async () => {
|
||||||
|
await state.addRelay(a.addr, { read: true, write: true });
|
||||||
|
}}>
|
||||||
|
<FormattedMessage defaultMessage="Add" />
|
||||||
|
</AsyncButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</CollapsedSection>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
21
packages/app/src/Pages/settings/relays/uptime-label.tsx
Normal file
21
packages/app/src/Pages/settings/relays/uptime-label.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
export default function UptimeLabel({ avgPing }: { avgPing: number }) {
|
||||||
|
const idealPing = 500;
|
||||||
|
const badPing = idealPing * 2;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames("font-semibold", {
|
||||||
|
"text-error": isNaN(avgPing) || avgPing > badPing,
|
||||||
|
"text-warning": avgPing > idealPing && avgPing < badPing,
|
||||||
|
"text-success": avgPing < idealPing,
|
||||||
|
})}
|
||||||
|
title={`${avgPing.toFixed(0)} ms`}>
|
||||||
|
{isNaN(avgPing) && <FormattedMessage defaultMessage="Dead" />}
|
||||||
|
{avgPing > badPing && <FormattedMessage defaultMessage="Poor" />}
|
||||||
|
{avgPing > idealPing && avgPing < badPing && <FormattedMessage defaultMessage="Good" />}
|
||||||
|
{avgPing < idealPing && <FormattedMessage defaultMessage="Great" />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { FormattedMessage } from "react-intl";
|
|||||||
|
|
||||||
import { findTag } from "@/Utils";
|
import { findTag } from "@/Utils";
|
||||||
import { Day } from "@/Utils/Const";
|
import { Day } from "@/Utils/Const";
|
||||||
|
import UptimeLabel from "./uptime-label";
|
||||||
|
|
||||||
const MonitorRelays = [
|
const MonitorRelays = [
|
||||||
"wss://relaypag.es",
|
"wss://relaypag.es",
|
||||||
@ -45,20 +46,5 @@ export default function RelayUptime({ url }: { url: string }) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const avgPing = ping.total / ping.n;
|
const avgPing = ping.total / ping.n;
|
||||||
const idealPing = 500;
|
return <UptimeLabel avgPing={avgPing} />;
|
||||||
const badPing = idealPing * 2;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames("font-semibold", {
|
|
||||||
"text-error": isNaN(avgPing) || avgPing > badPing,
|
|
||||||
"text-warning": avgPing > idealPing && avgPing < badPing,
|
|
||||||
"text-success": avgPing < idealPing,
|
|
||||||
})}
|
|
||||||
title={`${avgPing.toFixed(0)} ms`}>
|
|
||||||
{isNaN(avgPing) && <FormattedMessage defaultMessage="Dead" />}
|
|
||||||
{avgPing > badPing && <FormattedMessage defaultMessage="Poor" />}
|
|
||||||
{avgPing > idealPing && avgPing < badPing && <FormattedMessage defaultMessage="Good" />}
|
|
||||||
{avgPing < idealPing && <FormattedMessage defaultMessage="Great" />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -989,9 +989,6 @@
|
|||||||
"UxgyeY": {
|
"UxgyeY": {
|
||||||
"defaultMessage": "Your referral code is {code}"
|
"defaultMessage": "Your referral code is {code}"
|
||||||
},
|
},
|
||||||
"VL900k": {
|
|
||||||
"defaultMessage": "Recommended Relays"
|
|
||||||
},
|
|
||||||
"VOjC1i": {
|
"VOjC1i": {
|
||||||
"defaultMessage": "Pick which upload service you want to upload attachments to"
|
"defaultMessage": "Pick which upload service you want to upload attachments to"
|
||||||
},
|
},
|
||||||
@ -1431,6 +1428,9 @@
|
|||||||
"k7sKNy": {
|
"k7sKNy": {
|
||||||
"defaultMessage": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!"
|
"defaultMessage": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!"
|
||||||
},
|
},
|
||||||
|
"k9SQm1": {
|
||||||
|
"defaultMessage": "Relays which you have connected to before and appear to be reliable."
|
||||||
|
},
|
||||||
"kEZUR8": {
|
"kEZUR8": {
|
||||||
"defaultMessage": "Register an Iris username"
|
"defaultMessage": "Register an Iris username"
|
||||||
},
|
},
|
||||||
@ -1557,6 +1557,9 @@
|
|||||||
"p9Ps2l": {
|
"p9Ps2l": {
|
||||||
"defaultMessage": "{x}/{y} have relays ({percent})"
|
"defaultMessage": "{x}/{y} have relays ({percent})"
|
||||||
},
|
},
|
||||||
|
"pEEBFk": {
|
||||||
|
"defaultMessage": "Reliable Relays"
|
||||||
|
},
|
||||||
"pI+77w": {
|
"pI+77w": {
|
||||||
"defaultMessage": "Downloadable backups from Snort relay"
|
"defaultMessage": "Downloadable backups from Snort relay"
|
||||||
},
|
},
|
||||||
@ -1812,6 +1815,9 @@
|
|||||||
"yCLnBC": {
|
"yCLnBC": {
|
||||||
"defaultMessage": "LNURL or Lightning Address"
|
"defaultMessage": "LNURL or Lightning Address"
|
||||||
},
|
},
|
||||||
|
"yLzgxH": {
|
||||||
|
"defaultMessage": "Popular Relays"
|
||||||
|
},
|
||||||
"z3UjXR": {
|
"z3UjXR": {
|
||||||
"defaultMessage": "Debug"
|
"defaultMessage": "Debug"
|
||||||
},
|
},
|
||||||
|
@ -327,7 +327,6 @@
|
|||||||
"Ups2/p": "Your application is pending",
|
"Ups2/p": "Your application is pending",
|
||||||
"UrKTqQ": "You have an active iris.to account",
|
"UrKTqQ": "You have an active iris.to account",
|
||||||
"UxgyeY": "Your referral code is {code}",
|
"UxgyeY": "Your referral code is {code}",
|
||||||
"VL900k": "Recommended Relays",
|
|
||||||
"VOjC1i": "Pick which upload service you want to upload attachments to",
|
"VOjC1i": "Pick which upload service you want to upload attachments to",
|
||||||
"VR5eHw": "Public key (npub/nprofile)",
|
"VR5eHw": "Public key (npub/nprofile)",
|
||||||
"VcwrfF": "Yes please",
|
"VcwrfF": "Yes please",
|
||||||
@ -474,6 +473,7 @@
|
|||||||
"k0kCJp": "Apply Now",
|
"k0kCJp": "Apply Now",
|
||||||
"k2veDA": "Write",
|
"k2veDA": "Write",
|
||||||
"k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!",
|
"k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!",
|
||||||
|
"k9SQm1": "Relays which you have connected to before and appear to be reliable.",
|
||||||
"kEZUR8": "Register an Iris username",
|
"kEZUR8": "Register an Iris username",
|
||||||
"kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}",
|
"kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}",
|
||||||
"kaaf1E": "now",
|
"kaaf1E": "now",
|
||||||
@ -516,6 +516,7 @@
|
|||||||
"p4N05H": "Upload",
|
"p4N05H": "Upload",
|
||||||
"p85Uwy": "Active Subscriptions",
|
"p85Uwy": "Active Subscriptions",
|
||||||
"p9Ps2l": "{x}/{y} have relays ({percent})",
|
"p9Ps2l": "{x}/{y} have relays ({percent})",
|
||||||
|
"pEEBFk": "Reliable Relays",
|
||||||
"pI+77w": "Downloadable backups from Snort relay",
|
"pI+77w": "Downloadable backups from Snort relay",
|
||||||
"pRess9": "ZapPool",
|
"pRess9": "ZapPool",
|
||||||
"puLNUJ": "Pin",
|
"puLNUJ": "Pin",
|
||||||
@ -601,6 +602,7 @@
|
|||||||
"y1Z3or": "Language",
|
"y1Z3or": "Language",
|
||||||
"yAztTU": "{n} eSats",
|
"yAztTU": "{n} eSats",
|
||||||
"yCLnBC": "LNURL or Lightning Address",
|
"yCLnBC": "LNURL or Lightning Address",
|
||||||
|
"yLzgxH": "Popular Relays",
|
||||||
"z3UjXR": "Debug",
|
"z3UjXR": "Debug",
|
||||||
"zCb8fX": "Weight",
|
"zCb8fX": "Weight",
|
||||||
"zFegDD": "Contact",
|
"zFegDD": "Contact",
|
||||||
|
@ -25,10 +25,6 @@ module.exports = {
|
|||||||
"gray-superdark": "var(--gray-superdark)",
|
"gray-superdark": "var(--gray-superdark)",
|
||||||
"gray-ultradark": "var(--gray-ultradark)",
|
"gray-ultradark": "var(--gray-ultradark)",
|
||||||
},
|
},
|
||||||
backgroundColor: {
|
|
||||||
background: "var(--bg-color)",
|
|
||||||
secondary: "var(--bg-secondary)",
|
|
||||||
},
|
|
||||||
textColor: {
|
textColor: {
|
||||||
secondary: "var(--font-secondary-color)",
|
secondary: "var(--font-secondary-color)",
|
||||||
},
|
},
|
||||||
@ -40,6 +36,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
header: "var(--header-bg-color)",
|
header: "var(--header-bg-color)",
|
||||||
|
background: "var(--bg-color)",
|
||||||
|
secondary: "var(--bg-secondary)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user