diff --git a/src/main.tsx b/src/main.tsx index 7589738..c955781 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -12,8 +12,9 @@ import { NewPage } from "./page/new"; import { TorrentPage } from "./page/torrent"; import { SearchPage } from "./page/search"; import { System, initSystem } from "./system"; +import { RelaysPage } from "./page/relays"; -const Routes = [ +const routes = [ { element: , loader: async () => { @@ -41,15 +42,19 @@ const Routes = [ path: "/search/:term?", element: , }, + { + path: "/relays", + element: , + }, ], }, ] as Array; -const Router = createBrowserRouter(Routes); +const router = createBrowserRouter(routes); ReactDOM.createRoot(document.getElementById("root")!).render( - + , ); diff --git a/src/page/layout.tsx b/src/page/layout.tsx index d786350..709c94f 100644 --- a/src/page/layout.tsx +++ b/src/page/layout.tsx @@ -3,9 +3,35 @@ import { Button } from "../element/button"; import { LoginSession, LoginState, useLogin } from "../login"; import { ProfileImage } from "../element/profile-image"; import { Search } from "../element/search"; +import { useRelays } from "../relays"; +import { useContext, useEffect } from "react"; +import { SnortContext } from "@snort/system-react"; +import { RelaySettings, SystemInterface } from "@snort/system"; export function Layout() { const login = useLogin(); + const system = useContext(SnortContext); + const { relays } = useRelays(); + + async function updateRelayConnections(system: SystemInterface, relays: Record) { + if (import.meta.env.VITE_SINGLE_RELAY) { + system.ConnectToRelay(import.meta.env.VITE_SINGLE_RELAY, { read: true, write: true }); + } else { + for (const [k, v] of Object.entries(relays)) { + // note: don't awit this, causes race condition with sending requests to relays + system.ConnectToRelay(k, v); + } + for (const [k, v] of system.pool) { + if (!relays[k] && !v.ephemeral) { + system.DisconnectRelay(k); + } + } + } + } + + useEffect(() => { + updateRelayConnections(system, Object.fromEntries(relays.map((a) => [a, { read: true, write: true }]))); + }, [system, relays]); async function DoLogin() { if ("nostr" in window) { @@ -18,14 +44,18 @@ export function Layout() { return (
-
+

dtan.xyz

-
+
+
+ + + {login ? ( ) : ( diff --git a/src/page/relays.tsx b/src/page/relays.tsx new file mode 100644 index 0000000..f469542 --- /dev/null +++ b/src/page/relays.tsx @@ -0,0 +1,47 @@ +import { useState } from "react"; +import { Button } from "../element/button"; +import { useRelays } from "../relays"; +import { sanitizeRelayUrl } from "@snort/shared"; + +export function RelaysPage() { + const relays = useRelays(); + const [newRelay, setNewRelay] = useState(""); + return ( + <> +

Relays

+
+
+ {relays.relays.map((a) => ( +
+ {a} + +
+ ))} +
+
+
+ setNewRelay(e.target.value)} + className="px-4 py-2 rounded-xl bg-neutral-800 focus-visible:outline-none" + placeholder="wss://myrelay.com" + /> + +
+ + ); +} diff --git a/src/relays.tsx b/src/relays.tsx new file mode 100644 index 0000000..d07dfec --- /dev/null +++ b/src/relays.tsx @@ -0,0 +1,58 @@ +import { ExternalStore, appendDedupe, sanitizeRelayUrl } from "@snort/shared"; +import { useSyncExternalStore } from "react"; + +const storageKey = "relays"; +class RelaysStore extends ExternalStore> { + #relays: Array = []; + + constructor() { + super(); + const loaded = localStorage.getItem(storageKey); + if (loaded) { + this.#relays = JSON.parse(loaded); + } else { + this.#relays = ["wss://nos.lol/", "wss://relay.damus.io/", "wss://relay.nostr.band/"]; + this.#save(); + } + } + + add(u: string) { + const url = sanitizeRelayUrl(u); + if (url) { + this.#relays = appendDedupe(this.#relays, [url]); + this.#save(); + } + } + + remove(u: string) { + const url = sanitizeRelayUrl(u); + if (url) { + this.#relays = this.#relays.filter((a) => a !== url); + this.#save(); + } + } + + #save() { + localStorage.setItem(storageKey, JSON.stringify(this.#relays)); + this.notifyChange(); + } + + takeSnapshot(): string[] { + return [...this.#relays]; + } +} + +const relayStore = new RelaysStore(); + +export function useRelays() { + const relays = useSyncExternalStore( + (s) => relayStore.hook(s), + () => relayStore.snapshot(), + ); + + return { + relays, + add: (a: string) => relayStore.add(a), + remove: (a: string) => relayStore.remove(a), + }; +} diff --git a/src/system.ts b/src/system.ts index 6dc3a8b..48ec60e 100644 --- a/src/system.ts +++ b/src/system.ts @@ -62,8 +62,5 @@ export async function initSystem() { System.Init(), ]; - for (const r of ["wss://nos.lol", "wss://relay.damus.io", "wss://relay.nostr.band"]) { - System.ConnectToRelay(r, { read: true, write: true }); - } await Promise.all(tasks); }