add category filter to torrents

This commit is contained in:
hzrd149 2023-11-28 12:50:14 -06:00
parent c2edced715
commit 4877f08f51
8 changed files with 71 additions and 33 deletions

View File

@ -34,7 +34,7 @@ export default function PeopleListSelection({
};
return (
<Menu>
<Menu isLazy>
<MenuButton as={Button} {...props}>
{listEvent ? getListName(listEvent) : selected === "global" ? "Global" : "Loading..."}
</MenuButton>

View File

@ -4,7 +4,6 @@ import { ButtonGroup, ButtonGroupProps, IconButton } from "@chakra-ui/react";
import { ImageGridTimelineIcon, NoteFeedIcon, TimelineHealthIcon } from "../icons";
import { TimelineViewType } from "./index";
import { searchParamsToJson } from "../../helpers/url";
export default function TimelineViewTypeButtons(props: ButtonGroupProps) {
const [params, setParams] = useSearchParams();
@ -12,7 +11,14 @@ export default function TimelineViewTypeButtons(props: ButtonGroupProps) {
const onChange = useCallback(
(type: TimelineViewType) => {
setParams((p) => ({ ...searchParamsToJson(p), view: type }), { replace: true });
setParams(
(p) => {
const newParams = new URLSearchParams(p);
newParams.set("view", type);
return newParams;
},
{ replace: true },
);
},
[setParams],
);

View File

@ -54,13 +54,3 @@ export function replaceDomain(url: string | URL, replacementUrl: string | URL) {
if (replacementUrl.password) newUrl.password = replacementUrl.password;
return newUrl;
}
export function searchParamsToJson(params: URLSearchParams) {
const json: URLSearchParamsInit = {};
for (const [key, value] of params.entries()) {
json[key] = value;
}
return json;
}

View File

@ -41,11 +41,6 @@ export default function ({ community, onClose, ...props }: Omit<ModalProps, "chi
const communityCoordinate = getEventCoordinate(community);
const readRelays = useReadRelayUrls(getCommunityRelays(community));
const timeline = useTimelineLoader(`${communityCoordinate}-members`, readRelays, [
{
"#a": [communityCoordinate],
"#d": [SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER],
kinds: [NOTE_LIST_KIND],
},
{ "#a": [communityCoordinate], kinds: [COMMUNITIES_LIST_KIND] },
]);

View File

@ -5,7 +5,6 @@ import { useSearchParams, useNavigate } from "react-router-dom";
import { SEARCH_RELAYS } from "../../const";
import { safeDecode } from "../../helpers/nip19";
import { getMatchHashtag } from "../../helpers/regexp";
import { searchParamsToJson } from "../../helpers/url";
import { CommunityIcon, CopyToClipboardIcon, NotesIcon, QrCodeIcon } from "../../components/icons";
import QrScannerModal from "../../components/qr-scanner-modal";
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
@ -26,7 +25,14 @@ export function SearchPage() {
const [searchParams, setSearchParams] = useSearchParams();
const mergeSearchParams = useCallback(
(params: Record<string, any>) => {
setSearchParams((p) => ({ ...searchParamsToJson(p), ...params }), { replace: true });
setSearchParams(
(p) => {
const newParams = new URLSearchParams(p);
for (const [key, value] of Object.entries(params)) newParams.set(key, value);
return newParams;
},
{ replace: true },
);
},
[setSearchParams],
);

View File

@ -0,0 +1,25 @@
import { ReactNode } from "react";
import { Select, SelectProps } from "@chakra-ui/react";
import { Category, torrentCatagories } from "../../../helpers/nostr/torrents";
export default function CategorySelect({ ...props }: Omit<SelectProps, "children">) {
function renderCategory(a: Category, tags: Array<string>): ReactNode {
return (
<>
<option value={tags.join(",")}>{a.name}</option>
{a.sub_category?.map((b) => renderCategory(b, [...tags, b.tag]))}
</>
);
}
return (
<Select {...props}>
<option value="">All</option>
{torrentCatagories.map((category) => (
<optgroup key={category.tag} label={category.name}>
{renderCategory(category, [category.tag])}
</optgroup>
))}
</Select>
);
}

View File

@ -1,4 +1,4 @@
import { useMemo, useRef } from "react";
import { memo, useMemo, useRef } from "react";
import { ButtonGroup, IconButton, Link, Td, Tr } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
@ -14,7 +14,7 @@ import { formatBytes } from "../../../helpers/number";
import NoteZapButton from "../../../components/note/note-zap-button";
import TorrentMenu from "./torrent-menu";
export default function TorrentTableRow({ torrent }: { torrent: NostrEvent }) {
function TorrentTableRow({ torrent }: { torrent: NostrEvent }) {
const ref = useRef<HTMLTableRowElement | null>(null);
useRegisterIntersectionEntity(ref, getEventUID(torrent));
@ -29,7 +29,7 @@ export default function TorrentTableRow({ torrent }: { torrent: NostrEvent }) {
.join(" > ")}
</Td>
<Td>
<Link as={RouterLink} to={`/torrents/${getNeventCodeWithRelays(torrent.id)}`}>
<Link as={RouterLink} to={`/torrents/${getNeventCodeWithRelays(torrent.id)}`} isTruncated maxW="lg">
{getTorrentTitle(torrent)}
</Link>
</Td>
@ -50,3 +50,4 @@ export default function TorrentTableRow({ torrent }: { torrent: NostrEvent }) {
</Tr>
);
}
export default memo(TorrentTableRow);

View File

@ -1,6 +1,6 @@
import { useCallback, useState } from "react";
import { ChangeEventHandler, useCallback, useMemo, useState } from "react";
import { Alert, Button, Flex, Spacer, Table, TableContainer, Tbody, Th, Thead, Tr, useToast } from "@chakra-ui/react";
import { Link as RouterLink, useNavigate } from "react-router-dom";
import { Link as RouterLink, useNavigate, useSearchParams } from "react-router-dom";
import { generatePrivateKey, getPublicKey } from "nostr-tools";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
@ -20,6 +20,7 @@ import useCurrentAccount from "../../hooks/use-current-account";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import accountService from "../../services/account";
import signingService from "../../services/signing";
import CategorySelect from "./components/category-select";
function Warning() {
const navigate = useNavigate();
@ -58,21 +59,34 @@ function Warning() {
function TorrentsPage() {
const { filter, listId } = usePeopleListContext();
const { relays } = useRelaySelectionContext();
const [params, setParams] = useSearchParams();
const tags = params.get("tags")?.split(",") ?? [];
const handleTagsChange = useCallback<ChangeEventHandler<HTMLSelectElement>>(
(e) => {
const newParams = new URLSearchParams(params);
if (e.target.value) newParams.set("tags", e.target.value);
else newParams.delete("tags");
setParams(newParams, { replace: true });
},
[params],
);
const muteFilter = useClientSideMuteFilter();
const eventFilter = useCallback(
(e: NostrEvent) => {
return !muteFilter(e) && validateTorrent(e);
if (muteFilter(e)) return false;
if (!validateTorrent(e)) return false;
if (tags.length > 0 && tags.some((t) => !e.tags.some((e) => e[1] === t))) return false;
return true;
},
[muteFilter],
[muteFilter, tags.join(",")],
);
const timeline = useTimelineLoader(
`${listId}-torrents`,
relays,
{ ...filter, kinds: [TORRENT_KIND] },
{ eventFilter, enabled: !!filter },
const query = useMemo(
() => (tags.length > 0 ? { ...filter, kinds: [TORRENT_KIND], "#t": tags } : { ...filter, kinds: [TORRENT_KIND] }),
[tags.join(",")],
);
const timeline = useTimelineLoader(`${listId}-torrents`, relays, query, { eventFilter, enabled: !!filter });
const torrents = useSubject(timeline.timeline);
const callback = useTimelineCurserIntersectionCallback(timeline);
@ -85,6 +99,7 @@ function TorrentsPage() {
<Flex gap="2">
<RelaySelectionButton />
<PeopleListSelection />
<CategorySelect maxW="xs" value={tags.join(",")} onChange={handleTagsChange} />
<Spacer />
<Button as={RouterLink} to="/torrents/new">
New Torrent