From acd4c8ec3f07a2c52fce290d85978dedbd9d9841 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 27 Nov 2023 15:12:45 +0000 Subject: [PATCH] feat: stupid search --- src/element/search.tsx | 23 +- src/element/torrent-list.tsx | 2 +- src/login.tsx | 12 +- src/main.tsx | 7 +- src/page/new.tsx | 438 +++++++++++++++++------------------ src/page/search.tsx | 28 +++ src/page/torrent.tsx | 32 ++- 7 files changed, 303 insertions(+), 239 deletions(-) create mode 100644 src/page/search.tsx diff --git a/src/element/search.tsx b/src/element/search.tsx index 1e17554..a9ca5e5 100644 --- a/src/element/search.tsx +++ b/src/element/search.tsx @@ -1,6 +1,16 @@ +import { useNavigate, useParams } from "react-router-dom"; import { Categories } from "../const"; +import { useEffect, useState } from "react"; export function Search() { + const params = useParams(); + const navigate = useNavigate(); + const [term, setTerm] = useState(""); + + useEffect(() => { + setTerm(params.term ?? ""); + }, [params.term]); + return (
@@ -11,7 +21,18 @@ export function Search() {
))}
- + setTerm(e.target.value)} + onKeyDown={(e) => { + if (e.key == "Enter") { + navigate(`/search/${encodeURIComponent(term)}`); + } + }} + /> ); } diff --git a/src/element/torrent-list.tsx b/src/element/torrent-list.tsx index 09a4048..88e2f21 100644 --- a/src/element/torrent-list.tsx +++ b/src/element/torrent-list.tsx @@ -21,7 +21,7 @@ export function TorrentList({ items }: { items: Array }) { {items.map((a) => ( - + ))} diff --git a/src/login.tsx b/src/login.tsx index 91a57bd..872f0e6 100644 --- a/src/login.tsx +++ b/src/login.tsx @@ -42,9 +42,11 @@ export function useLogin() { () => LoginState.snapshot(), ); const system = useContext(SnortContext); - return session ? { - ...session, - builder: new EventPublisher(new Nip7Signer(), session.publicKey), - system - } : undefined; + return session + ? { + ...session, + builder: new EventPublisher(new Nip7Signer(), session.publicKey), + system, + } + : undefined; } diff --git a/src/main.tsx b/src/main.tsx index 8e5e69d..31159fc 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,10 +10,11 @@ import { ProfilePage } from "./page/profile"; import { NewPage } from "./page/new"; import { TorrentPage } from "./page/torrent"; import { SnortSystemDb } from "@snort/system-web"; +import { SearchPage } from "./page/search"; const db = new SnortSystemDb(); const System = new NostrSystem({ - db + db, }); const Routes = [ { @@ -42,6 +43,10 @@ const Routes = [ path: "/e/:id", element: , }, + { + path: "/search/:term?", + element: , + }, ], }, ] as Array; diff --git a/src/page/new.tsx b/src/page/new.tsx index 367bd69..a4ebb43 100644 --- a/src/page/new.tsx +++ b/src/page/new.tsx @@ -9,234 +9,234 @@ import { bytesToHex } from "@noble/hashes/utils"; import { useNavigate } from "react-router-dom"; async function openFile(): Promise { - return new Promise((resolve) => { - const elm = document.createElement("input"); - let lock = false; - elm.type = "file"; - elm.accept = ".torrent"; - const handleInput = (e: Event) => { - lock = true; - const elm = e.target as HTMLInputElement; - if ((elm.files?.length ?? 0) > 0) { - resolve(elm.files![0]); - } else { - resolve(undefined); - } - }; + return new Promise((resolve) => { + const elm = document.createElement("input"); + let lock = false; + elm.type = "file"; + elm.accept = ".torrent"; + const handleInput = (e: Event) => { + lock = true; + const elm = e.target as HTMLInputElement; + if ((elm.files?.length ?? 0) > 0) { + resolve(elm.files![0]); + } else { + resolve(undefined); + } + }; - elm.onchange = (e) => handleInput(e); - elm.click(); - window.addEventListener( - "focus", - () => { - setTimeout(() => { - if (!lock) { - console.debug("FOCUS WINDOW UPLOAD"); - resolve(undefined); - } - }, 300); - }, - { once: true }, - ); - }); + elm.onchange = (e) => handleInput(e); + elm.click(); + window.addEventListener( + "focus", + () => { + setTimeout(() => { + if (!lock) { + console.debug("FOCUS WINDOW UPLOAD"); + resolve(undefined); + } + }, 300); + }, + { once: true }, + ); + }); } export function NewPage() { - const login = useLogin(); - const navigate = useNavigate(); + const login = useLogin(); + const navigate = useNavigate(); - const [obj, setObj] = useState({ - name: "", - desc: "", - btih: "", - tags: [] as Array, - files: [] as Array<{ - name: string; - size: number; - }>, + const [obj, setObj] = useState({ + name: "", + desc: "", + btih: "", + tags: [] as Array, + files: [] as Array<{ + name: string; + size: number; + }>, + }); + + async function loadTorrent() { + const f = await openFile(); + if (f) { + const buf = await f.arrayBuffer(); + const torrent = bencode.decode(new Uint8Array(buf)) as Record; + const infoBuf = bencode.encode(torrent["info"]); + console.debug(torrent); + const dec = new TextDecoder(); + const info = torrent["info"] as { + files?: Array<{ length: number; path: Array }>; + length: number; + name: Uint8Array; + }; + + setObj({ + name: dec.decode(info.name), + desc: dec.decode(torrent["comment"] as Uint8Array | undefined) ?? "", + btih: bytesToHex(sha1(infoBuf)), + tags: [], + files: (info.files ?? [{ length: info.length, path: [info.name] }]).map((a) => ({ + size: a.length, + name: a.path.map((b) => dec.decode(b)).join("/"), + })), + }); + } + } + + async function publish() { + if (!login) return; + const ev = await login.builder.generic((eb) => { + const v = eb + .kind(TorrentKind) + .content(obj.desc) + .tag(["title", obj.name]) + .tag(["size", String(obj.files.reduce((acc, v) => (acc += v.size), 0))]) + .tag(["btih", obj.btih]); + + obj.tags.forEach((t) => v.tag(["t", t])); + obj.files.forEach((f) => v.tag(["file", f.name, String(f.size)])); + + return v; }); + console.debug(ev); - async function loadTorrent() { - const f = await openFile(); - if (f) { - const buf = await f.arrayBuffer(); - const torrent = bencode.decode(new Uint8Array(buf)) as Record; - const infoBuf = bencode.encode(torrent["info"]); - console.debug(torrent); - const dec = new TextDecoder(); - const info = torrent["info"] as { - files?: Array<{ length: number; path: Array }>; - length: number; - name: Uint8Array; - }; - - setObj({ - name: dec.decode(info.name), - desc: dec.decode(torrent["comment"] as Uint8Array | undefined) ?? "", - btih: bytesToHex(sha1(infoBuf)), - tags: [], - files: (info.files ?? [{ length: info.length, path: [info.name] }]).map((a) => ({ - size: a.length, - name: a.path.map(b => dec.decode(b)).join("/"), - })), - }); - } - } - - async function publish() { - if (!login) return; - const ev = await login.builder.generic((eb) => { - const v = eb - .kind(TorrentKind) - .content(obj.desc) - .tag(["title", obj.name]) - .tag(["size", String(obj.files.reduce((acc, v) => (acc += v.size), 0))]) - .tag(["btih", obj.btih]); - - obj.tags.forEach((t) => v.tag(["t", t])); - obj.files.forEach((f) => v.tag(["file", f.name, String(f.size)])); - - return v; - }); - console.debug(ev); - - if (ev) { - await login.system.BroadcastEvent(ev); - } - navigate("/") - } - - function renderCategories(a: Category, tags: Array): ReactNode { - return ( - <> -
- - setObj((o) => ({ - ...o, - tags: e.target.checked ? dedupe(e.target.value.split(",")) : [], - })) - } - /> - -
- {a.sub_category?.map((b) => renderCategories(b, [...tags, b.tag]))} - - ); + if (ev) { + await login.system.BroadcastEvent(ev); } + navigate("/"); + } + function renderCategories(a: Category, tags: Array): ReactNode { return ( - <> -

New

-
- - -
-

Torrent Info

-
-
-
- - setObj((o) => ({ ...o, name: e.target.value }))} - /> - - setObj((o) => ({ ...o, btih: e.target.value }))} - /> - -
- {Categories.map((a) => ( -
-
{a.name}
-
{renderCategories(a, [a.tag])}
-
- ))} -
-
-
- - -
-
-

Files

-
- {obj.files.map((a, i) => ( -
- - setObj((o) => ({ - ...o, - files: o.files.map((f, ii) => { - if (ii === i) { - return { ...f, name: e.target.value }; - } - return f; - }), - })) - } - /> - - setObj((o) => ({ - ...o, - files: o.files.map((f, ii) => { - if (ii === i) { - return { ...f, size: Number(e.target.value) }; - } - return f; - }), - })) - } - /> - -
- ))} -
- - -
- + <> +
+ + setObj((o) => ({ + ...o, + tags: e.target.checked ? dedupe(e.target.value.split(",")) : [], + })) + } + /> + +
+ {a.sub_category?.map((b) => renderCategories(b, [...tags, b.tag]))} + ); + } + + return ( + <> +

New

+
+ + +
+

Torrent Info

+
+
+
+ + setObj((o) => ({ ...o, name: e.target.value }))} + /> + + setObj((o) => ({ ...o, btih: e.target.value }))} + /> + +
+ {Categories.map((a) => ( +
+
{a.name}
+
{renderCategories(a, [a.tag])}
+
+ ))} +
+
+
+ + +
+
+

Files

+
+ {obj.files.map((a, i) => ( +
+ + setObj((o) => ({ + ...o, + files: o.files.map((f, ii) => { + if (ii === i) { + return { ...f, name: e.target.value }; + } + return f; + }), + })) + } + /> + + setObj((o) => ({ + ...o, + files: o.files.map((f, ii) => { + if (ii === i) { + return { ...f, size: Number(e.target.value) }; + } + return f; + }), + })) + } + /> + +
+ ))} +
+ + +
+ + ); } diff --git a/src/page/search.tsx b/src/page/search.tsx new file mode 100644 index 0000000..a568c74 --- /dev/null +++ b/src/page/search.tsx @@ -0,0 +1,28 @@ +import { NoteCollection, RequestBuilder } from "@snort/system"; +import { useParams } from "react-router-dom"; +import { TorrentKind } from "../const"; +import { useRequestBuilder } from "@snort/system-react"; +import { TorrentList } from "../element/torrent-list"; +import { Search } from "../element/search"; + +export function SearchPage() { + const params = useParams(); + const term = params.term as string | undefined; + + const rb = new RequestBuilder(`search:${term}`); + rb.withFilter() + .kinds([TorrentKind]) + .search(term) + .limit(100) + .relay(["wss://relay.nostr.band", "wss://relay.noswhere.com"]); + + const data = useRequestBuilder(NoteCollection, rb); + + return ( + <> + +

Search Results:

+ + + ); +} diff --git a/src/page/torrent.tsx b/src/page/torrent.tsx index c87a714..8563751 100644 --- a/src/page/torrent.tsx +++ b/src/page/torrent.tsx @@ -30,8 +30,8 @@ export function TorrentDetail({ item }: { item: TaggedNostrEvent }) { const navigate = useNavigate(); const name = item.tags.find((a) => a[0] === "title")?.at(1); const size = Number(item.tags.find((a) => a[0] === "size")?.at(1)); - const files = item.tags.filter(a => a[0] === "file"); - const tags = item.tags.filter(a => a[0] === "t").map(a => a[1]); + const files = item.tags.filter((a) => a[0] === "file"); + const tags = item.tags.filter((a) => a[0] === "t").map((a) => a[1]); async function deleteTorrent() { const ev = await login?.builder?.delete(item.id); @@ -50,9 +50,13 @@ export function TorrentDetail({ item }: { item: TaggedNostrEvent }) {
Size: {FormatBytes(size)}
Uploaded: {new Date(item.created_at * 1000).toLocaleDateString()}
-
Tags:
- {tags.map(a =>
#{a}
)} -
+
+ Tags:{" "} +
+ {tags.map((a) => ( +
#{a}
+ ))} +
@@ -64,14 +68,18 @@ export function TorrentDetail({ item }: { item: TaggedNostrEvent }) {
{item.content}

Files

- {files.map(a =>
- {a[1]} - {FormatBytes(Number(a[2]))} -
)} + {files.map((a) => ( +
+ {a[1]} + {FormatBytes(Number(a[2]))} +
+ ))}
- {item.pubkey == login?.publicKey && } + {item.pubkey == login?.publicKey && ( + + )}
); }