mirror of
https://git.v0l.io/Kieran/dtan.git
synced 2025-01-06 00:40:08 +00:00
feat: relay controls
This commit is contained in:
parent
85151ac008
commit
5c8cb7d359
11
src/main.tsx
11
src/main.tsx
@ -12,8 +12,9 @@ import { NewPage } from "./page/new";
|
|||||||
import { TorrentPage } from "./page/torrent";
|
import { TorrentPage } from "./page/torrent";
|
||||||
import { SearchPage } from "./page/search";
|
import { SearchPage } from "./page/search";
|
||||||
import { System, initSystem } from "./system";
|
import { System, initSystem } from "./system";
|
||||||
|
import { RelaysPage } from "./page/relays";
|
||||||
|
|
||||||
const Routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
element: <Layout />,
|
element: <Layout />,
|
||||||
loader: async () => {
|
loader: async () => {
|
||||||
@ -41,15 +42,19 @@ const Routes = [
|
|||||||
path: "/search/:term?",
|
path: "/search/:term?",
|
||||||
element: <SearchPage />,
|
element: <SearchPage />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/relays",
|
||||||
|
element: <RelaysPage />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] as Array<RouteObject>;
|
] as Array<RouteObject>;
|
||||||
|
|
||||||
const Router = createBrowserRouter(Routes);
|
const router = createBrowserRouter(routes);
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<SnortContext.Provider value={System}>
|
<SnortContext.Provider value={System}>
|
||||||
<RouterProvider router={Router} />
|
<RouterProvider router={router} />
|
||||||
</SnortContext.Provider>
|
</SnortContext.Provider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
@ -3,9 +3,35 @@ import { Button } from "../element/button";
|
|||||||
import { LoginSession, LoginState, useLogin } from "../login";
|
import { LoginSession, LoginState, useLogin } from "../login";
|
||||||
import { ProfileImage } from "../element/profile-image";
|
import { ProfileImage } from "../element/profile-image";
|
||||||
import { Search } from "../element/search";
|
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() {
|
export function Layout() {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
|
const system = useContext(SnortContext);
|
||||||
|
const { relays } = useRelays();
|
||||||
|
|
||||||
|
async function updateRelayConnections(system: SystemInterface, relays: Record<string, RelaySettings>) {
|
||||||
|
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() {
|
async function DoLogin() {
|
||||||
if ("nostr" in window) {
|
if ("nostr" in window) {
|
||||||
@ -18,14 +44,18 @@ export function Layout() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto">
|
<div className="container mx-auto">
|
||||||
<header className="flex justify-between items-center pt-4 pb-6">
|
<header className="flex gap-4 items-center pt-4 pb-6">
|
||||||
<Link to={"/"} className="flex gap-2 items-center">
|
<Link to={"/"} className="flex gap-2 items-center">
|
||||||
<img src="/logo_256.jpg" className="rounded-full" height={40} width={40} />
|
<img src="/logo_256.jpg" className="rounded-full" height={40} width={40} />
|
||||||
<h1 className="font-bold uppercase">dtan.xyz</h1>
|
<h1 className="font-bold uppercase">dtan.xyz</h1>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="w-1/2">
|
<div className="w-1/3">
|
||||||
<Search />
|
<Search />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grow"></div>
|
||||||
|
<Link to="/relays">
|
||||||
|
<Button type="secondary">Relays</Button>
|
||||||
|
</Link>
|
||||||
{login ? (
|
{login ? (
|
||||||
<LoggedInHeader login={login} />
|
<LoggedInHeader login={login} />
|
||||||
) : (
|
) : (
|
||||||
|
47
src/page/relays.tsx
Normal file
47
src/page/relays.tsx
Normal file
@ -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 (
|
||||||
|
<>
|
||||||
|
<h2>Relays</h2>
|
||||||
|
<br />
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{relays.relays.map((a) => (
|
||||||
|
<div key={a} className="bg-neutral-800 px-3 py-2 rounded-xl flex justify-between items-center">
|
||||||
|
{a}
|
||||||
|
<Button type="danger" onClick={() => relays.remove(a)}>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={newRelay}
|
||||||
|
onChange={(e) => setNewRelay(e.target.value)}
|
||||||
|
className="px-4 py-2 rounded-xl bg-neutral-800 focus-visible:outline-none"
|
||||||
|
placeholder="wss://myrelay.com"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
const url = sanitizeRelayUrl(newRelay);
|
||||||
|
if (url) {
|
||||||
|
relays.add(url);
|
||||||
|
setNewRelay("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
58
src/relays.tsx
Normal file
58
src/relays.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { ExternalStore, appendDedupe, sanitizeRelayUrl } from "@snort/shared";
|
||||||
|
import { useSyncExternalStore } from "react";
|
||||||
|
|
||||||
|
const storageKey = "relays";
|
||||||
|
class RelaysStore extends ExternalStore<Array<string>> {
|
||||||
|
#relays: Array<string> = [];
|
||||||
|
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
}
|
@ -62,8 +62,5 @@ export async function initSystem() {
|
|||||||
System.Init(),
|
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);
|
await Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user