improve relay settings

This commit is contained in:
hzrd149 2023-02-07 17:04:18 -06:00
parent 2a34b370cd
commit b960f8c600
7 changed files with 382 additions and 161 deletions

189
public/relays.json Normal file
View File

@ -0,0 +1,189 @@
{
"_": "Download from https://nostr.watch/",
"relays": [
"wss://nostr.p2sh.co",
"wss://nostr.lnprivate.network",
"wss://relay.nostr.vision",
"wss://nostr-3.orba.ca",
"wss://satstacker.cloud",
"wss://freedom-relay.herokuapp.com/ws",
"wss://nostr-relay.freeberty.net",
"wss://nostr-relay.wlvs.space",
"wss://nostr-relay-dev.wlvs.space",
"wss://nostr.onsats.org",
"wss://nostr-relay.untethr.me",
"wss://nostr-relay.lnmarkets.com",
"wss://nostr.unknown.place",
"wss://nostr.semisol.dev",
"wss://nostr-pub.semisol.dev",
"wss://nostr-verified.wellorder.net",
"wss://nostr.drss.io",
"wss://nostr.rocks",
"wss://nostr.bitcoiner.social",
"wss://nostr.openchain.fr",
"wss://nostr.delo.software",
"wss://relay.nostr.info",
"wss://relay.nostr.pro",
"wss://relay.minds.com/nostr/v1/ws",
"wss://relay.damus.io",
"wss://nostr.zaprite.io",
"wss://nostr.oxtr.dev",
"wss://nostr.ono.re",
"wss://relay.grunch.dev",
"wss://relay.cynsar.foundation",
"wss://nostr-pub.wellorder.net",
"wss://relay.oldcity-bitcoiners.info",
"wss://relay.bitid.nz",
"wss://relay.nostr.xyz",
"wss://relay.futohq.com",
"wss://nostr.rdfriedl.com",
"wss://relay.farscapian.com",
"wss://astral.ninja",
"wss://relay.sovereign-stack.org",
"wss://nostr.sandwich.farm",
"wss://nostr.zebedee.cloud",
"wss://nostr-2.zebedee.cloud",
"wss://nostr.shadownode.org",
"wss://nostr.nymsrelay.com",
"wss://expensive-relay.fiatjaf.com",
"wss://relay.kronkltd.net",
"wss://relay.r3d.red",
"wss://relay.valireum.net",
"wss://nostr.fmt.wiz.biz",
"wss://nostr.v0l.io",
"wss://relay.cryptocculture.com",
"wss://nostr.fly.dev",
"wss://nostr.nordlysln.net",
"wss://nostr-relay.gkbrk.com",
"wss://nostr.zerofeerouting.com",
"wss://no.str.cr",
"wss://relay.nostr.ch",
"wss://rsslay.nostr.net",
"wss://nostr.cercatrova.me",
"wss://public.nostr.swissrouting.com",
"wss://nostr-relay.nonce.academy",
"wss://nostr.rewardsbunny.com",
"wss://nostr.slothy.win",
"wss://nostr-verif.slothy.win",
"wss://nostr.coinos.io",
"wss://relay.nostropolis.xyz/websocket",
"wss://lv01.tater.ninja",
"wss://nostr-2.orba.ca",
"wss://nostr.orba.ca",
"wss://nostr.supremestack.xyz",
"wss://nostrrelay.com",
"wss://nostr-01.bolt.observer",
"wss://nostr.mom",
"wss://relay.nostr.au",
"wss://nostr.swiss-enigma.ch",
"wss://nostr.oooxxx.ml",
"wss://nostr.yael.at",
"wss://nostr-relay.trustbtc.org",
"wss://nostr.namek.link",
"wss://nostr-relay.wolfandcrow.tech",
"wss://nostr.8e23.net",
"wss://nostr.actn.io",
"wss://relay.sendstr.com",
"wss://nostr.satsophone.tk",
"wss://nostr-relay.derekross.me",
"wss://nostr.jiashanlu.synology.me",
"wss://nostr.radixrat.com",
"wss://nostr.shawnyeager.net",
"wss://relay.dev.kronkltd.net",
"wss://nostr2.namek.link",
"wss://nostr-dev.wellorder.net",
"wss://nostr.mwmdev.com",
"wss://relay.21spirits.io",
"wss://nostr-relay.freedomnode.com",
"wss://relay.minds.io/nostr/v1/ws",
"wss://wlvs.space",
"wss://nostr.einundzwanzig.space",
"wss://nostr.d11n.net",
"wss://nostr1.tunnelsats.com",
"wss://nostr.tunnelsats.com",
"wss://nostr.leximaster.com",
"wss://nostr.hugo.md",
"wss://mule.platanito.org",
"wss://relay.ryzizub.com",
"wss://nostr.w3ird.tech",
"wss://nostr.robotechy.com",
"wss://relay.stoner.com",
"wss://relay.nostrmoto.xyz",
"wss://relay.boring.surf",
"wss://nostr.bongbong.com",
"wss://nostr.gruntwerk.org",
"wss://nostr.mado.io",
"wss://nostr.corebreach.com",
"wss://nostr.hyperlingo.com",
"wss://nostr.ethtozero.fr",
"wss://nostr-relay.digitalmob.ro",
"wss://relay.nvote.co",
"wss://jiggytom.ddns.net",
"wss://nostr.sectiontwo.org",
"wss://nostr.roundrockbitcoiners.com",
"wss://nostr.nodeofsven.com",
"wss://nostr.jimc.me",
"wss://nostr.utxo.lol",
"wss://relay.lexingtonbitcoin.org",
"wss://nostr.mikedilger.com",
"wss://nostr.f44.dev",
"wss://relay.nyx.ma",
"wss://nostr.walletofsatoshi.com",
"wss://nostr.shmueli.org",
"wss://wizards.wormrobot.org",
"wss://nostr.orangepill.dev",
"wss://paid.no.str.cr",
"wss://nostr.sovbit.com",
"wss://nostr.datamagik.com",
"wss://relay.nostrid.com",
"wss://nostr1.starbackr.me",
"wss://nostr-relay.schnitzel.world",
"wss://relay.nostr.express",
"wss://sg.qemura.xyz",
"wss://nostr.formigator.eu",
"wss://nostr.xpersona.net",
"wss://relay.n057r.club",
"wss://nostr.digitalreformation.info",
"wss://nostr.gromeul.eu",
"wss://nostr-relay.alekberg.net",
"wss://nostr2.actn.io",
"wss://nostr-relay.usebitcoin.space",
"wss://nostrich.friendship.tw",
"wss://nostr.mustardnodes.com",
"wss://nostr-alpha.gruntwerk.org",
"wss://nostr-relay.australiaeast.cloudapp.azure.com",
"wss://nostr.bch.ninja",
"wss://nostr-relay.smoove.net",
"wss://nostr-relay.j3s7m4n.com",
"wss://nostr.demovement.net",
"wss://nostr.thesimplekid.com",
"wss://nostr.aozing.com",
"wss://nostr.blocs.fr",
"wss://no.str.watch",
"wss://nostr.vulpem.com",
"wss://btc.klendazu.com",
"wss://nostr.hackerman.pro",
"wss://relay.realsearch.cc",
"wss://nostr.mrbits.it",
"wss://nostr.coollamer.com",
"wss://node01.nostress.cc",
"wss://nostr.zenon.wtf",
"wss://nostr.massmux.com",
"wss://no.contry.xyz",
"wss://relay.nostr.bg",
"wss://nostr.uselessshit.co",
"wss://brb.io",
"wss://nostream.gromeul.eu",
"wss://relay.nostr.ro",
"wss://nostr.developer.li",
"wss://nostr.screaminglife.io",
"wss://deschooling.us",
"wss://relay-pub.deschooling.us",
"wss://nostr-pub1.southflorida.ninja",
"wss://nostr.ncsa.illinois.edu",
"wss://nostr.whoop.ph",
"wss://nostr.zoomout.chat",
"wss://nostr.chaker.net",
"wss://nostr-1.nbo.angani.co"
]
}

View File

@ -1,99 +1,22 @@
import React, { useState } from "react";
import {
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableCaption,
TableContainer,
useDisclosure,
Badge,
} from "@chakra-ui/react";
import { useState } from "react";
import { Text } from "@chakra-ui/react";
import { useInterval } from "react-use";
import { Relay } from "../services/relays";
import relayPool from "../services/relays/relay-pool";
import useSubject from "../hooks/use-subject";
import settings from "../services/settings";
const getRelayStatusText = (relay: Relay) => {
if (relay.connecting) return "Connecting...";
if (relay.connected) return "Connected";
if (relay.closing) return "Disconnecting...";
if (relay.closed) return "Disconnected";
};
export const ConnectedRelays = () => {
const relayUrls = useSubject(settings.relays);
const { isOpen, onOpen, onClose } = useDisclosure();
const [relays, setRelays] = useState<Relay[]>([]);
const [relays, setRelays] = useState<Relay[]>(relayPool.getRelays());
useInterval(() => {
setRelays(relayPool.getRelays());
}, 1000);
useInterval(() => {
for (const url of relayUrls) {
// ask the pool to reconnect if disconnected
relayPool.requestRelay(url);
}
}, 1000 * 30);
const connected = relays.filter((relay) => relay.okay);
const disconnected = relays.filter((relay) => !relay.okay);
return (
<>
<Button variant="link" onClick={onOpen}>
{connected.length}/{relays.length} of relays connected
</Button>
<Modal isOpen={isOpen} onClose={onClose} size="4xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Relay Status</ModalHeader>
<ModalCloseButton />
<ModalBody>
<TableContainer>
<Table variant="simple">
<Thead>
<Tr>
<Th>Url</Th>
<Th>status</Th>
</Tr>
</Thead>
<Tbody>
{relays.map((relay) => (
<Tr key={relay.url}>
<Td>{relay.url}</Td>
<Td>
<Badge colorScheme={relay.okay ? "green" : "red"}>
{getRelayStatusText(relay)}
</Badge>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
<Text textAlign="center">
{connected.length}/{relays.length} of relays connected
</Text>
);
};

View File

@ -49,3 +49,8 @@ export const ClipboardIcon = createIcon({
displayName: "clipboard-line",
d: "M7 4V2h10v2h3.007c.548 0 .993.445.993.993v16.014a.994.994 0 0 1-.993.993H3.993A.994.994 0 0 1 3 21.007V4.993C3 4.445 3.445 4 3.993 4H7zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z",
});
export const TrashIcon = createIcon({
displayName: "delete-bin-line",
d: "M17 6h5v2h-2v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8H2V6h5V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v3zm1 2H6v12h12V8zm-9 3h2v6H9v-6zm4 0h2v6h-2v-6zM9 4v2h6V4H9z",
});

View File

@ -1,77 +0,0 @@
import {
Button,
Divider,
Flex,
FormControl,
FormHelperText,
FormLabel,
Heading,
Input,
Stack,
Switch,
Textarea,
useColorMode,
} from "@chakra-ui/react";
import { SyntheticEvent, useState } from "react";
import useSubject from "../hooks/use-subject";
import settings from "../services/settings";
export const SettingsView = () => {
const relays = useSubject(settings.relays);
const [relayUrls, setRelayUrls] = useState(relays.join("\n"));
const { colorMode, setColorMode } = useColorMode();
const handleSubmit = async (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
const newRelays = relayUrls
.split("\n")
.filter(Boolean)
.map((url) => url.trim());
if (newRelays.length > 0) {
settings.relays.next(newRelays);
}
};
const resetForm = async () => {
setRelayUrls(relays.join("\n"));
};
return (
<Flex direction="column" gap="4" pt="2" pb="2" overflow="auto">
<Heading>Settings</Heading>
<FormControl display="flex" alignItems="center">
<FormLabel htmlFor="use-dark-theme" mb="0">
Use dark theme
</FormLabel>
<Switch
id="use-dark-theme"
isChecked={colorMode === "dark"}
onChange={(v) => setColorMode(v.target.checked ? "dark" : "light")}
/>
</FormControl>
<Divider />
<form onSubmit={handleSubmit}>
<FormControl>
<FormLabel>Relays</FormLabel>
<Textarea
value={relayUrls}
onChange={(e) => setRelayUrls(e.target.value)}
required
size="md"
rows={10}
resize="vertical"
/>
<FormHelperText>One relay per line</FormHelperText>
</FormControl>
<Stack direction="row" spacing={4}>
<Button onClick={resetForm}>Reset</Button>
<Button type="submit" colorScheme="teal">
Save
</Button>
</Stack>
</form>
</Flex>
);
};

View File

@ -0,0 +1,152 @@
import {
Button,
Flex,
FormControl,
FormLabel,
Input,
Switch,
useColorMode,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableContainer,
IconButton,
AccordionItem,
Accordion,
AccordionPanel,
AccordionButton,
Box,
AccordionIcon,
} from "@chakra-ui/react";
import { SyntheticEvent, useState } from "react";
import { useAsync } from "react-use";
import { TrashIcon } from "../../components/icons";
import { RelayStatus } from "./relay-status";
import useSubject from "../../hooks/use-subject";
import settings from "../../services/settings";
export const SettingsView = () => {
const relays = useSubject(settings.relays);
const [relayInputValue, setRelayInputValue] = useState("");
const { value: relaysJson, loading: loadingRelaysJson } = useAsync(async () =>
fetch("/relays.json").then(
(res) => res.json() as Promise<{ relays: string[] }>
)
);
const relaySuggestions =
relaysJson?.relays.filter((url) => !relays.includes(url)) ?? [];
const { colorMode, setColorMode } = useColorMode();
const handleRemoveRelay = (url: string) => {
settings.relays.next(relays.filter((v) => v !== url));
};
const handleAddRelay = (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
settings.relays.next([...relays, relayInputValue]);
setRelayInputValue("");
};
return (
<Flex direction="column" pt="2" pb="2" overflow="auto">
<Accordion defaultIndex={[0]} allowMultiple>
<AccordionItem>
<h2>
<AccordionButton>
<Box as="span" flex="1" textAlign="left">
Relays
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel>
<TableContainer mb="4">
<Table variant="simple" size="sm">
<Thead>
<Tr>
<Th>Url</Th>
<Th>Status</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{relays.map((url) => (
<Tr key={url}>
<Td>{url}</Td>
<Td>
<RelayStatus url={url} />
</Td>
<Td isNumeric>
<IconButton
icon={<TrashIcon />}
title="Remove Relay"
aria-label="Remove Relay"
size="sm"
onClick={() => handleRemoveRelay(url)}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<form onSubmit={handleAddRelay}>
<FormControl>
<FormLabel htmlFor="relay-url-input">Add Relay</FormLabel>
<Flex gap="2">
<Input
id="relay-url-input"
value={relayInputValue}
onChange={(e) => setRelayInputValue(e.target.value)}
required
list="relay-suggestions"
type="url"
isDisabled={loadingRelaysJson}
/>
<datalist id="relay-suggestions">
{relaySuggestions.map((url) => (
<option key={url} value={url}>
{url}
</option>
))}
</datalist>
<Button type="submit">Add</Button>
</Flex>
</FormControl>
</form>
</AccordionPanel>
</AccordionItem>
<AccordionItem>
<h2>
<AccordionButton>
<Box as="span" flex="1" textAlign="left">
Display
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel>
<FormControl display="flex" alignItems="center">
<FormLabel htmlFor="use-dark-theme" mb="0">
Use dark theme
</FormLabel>
<Switch
id="use-dark-theme"
isChecked={colorMode === "dark"}
onChange={(v) =>
setColorMode(v.target.checked ? "dark" : "light")
}
/>
</FormControl>
</AccordionPanel>
</AccordionItem>
</Accordion>
</Flex>
);
};

View File

@ -0,0 +1,30 @@
import { Badge, useForceUpdate } from "@chakra-ui/react";
import { useInterval } from "react-use";
import { Relay, relayPool } from "../../services/relays";
const getStatusText = (relay: Relay) => {
if (relay.connecting) return "Connecting...";
if (relay.connected) return "Connected";
if (relay.closing) return "Disconnecting...";
if (relay.closed) return "Disconnected";
return "Unused";
};
const getStatusColor = (relay: Relay) => {
if (relay.connecting) return "yellow";
if (relay.connected) return "green";
if (relay.closing) return "yellow";
if (relay.closed) return "red";
return "gray";
};
export const RelayStatus = ({ url }: { url: string }) => {
const update = useForceUpdate();
const relay = relayPool.requestRelay(url, false);
useInterval(() => update(), 500);
return (
<Badge colorScheme={getStatusColor(relay)}>{getStatusText(relay)}</Badge>
);
};

View File

@ -5,7 +5,6 @@ import react from "@vitejs/plugin-react";
export default defineConfig({
build: {
target: ["chrome89", "edge89", "firefox89", "safari15"],
minify: false,
},
plugins: [react()],
});