From 5b2e2e3c28812cd7f9b02090f0f0e0f65fa28534 Mon Sep 17 00:00:00 2001 From: qu1ck Date: Wed, 25 Oct 2023 21:51:47 -0700 Subject: [PATCH] Split trackers from properties dialog, allow multi edit Issue #76 --- src/components/modals/add.tsx | 8 +- src/components/modals/common.tsx | 34 +++--- src/components/modals/edittorrent.tsx | 65 +++--------- src/components/modals/edittrackers.tsx | 137 +++++++++++++++++++++++++ src/components/modals/servermodals.tsx | 6 ++ src/components/tables/torrenttable.tsx | 10 +- 6 files changed, 188 insertions(+), 72 deletions(-) create mode 100644 src/components/modals/edittrackers.tsx diff --git a/src/components/modals/add.tsx b/src/components/modals/add.tsx index c6d0f67..a8d4f5e 100644 --- a/src/components/modals/add.tsx +++ b/src/components/modals/add.tsx @@ -19,7 +19,7 @@ import { Box, Button, Checkbox, Divider, Flex, Group, Menu, SegmentedControl, Text, TextInput } from "@mantine/core"; import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import type { ModalState, LocationData } from "./common"; -import { HkModal, TorrentLabels, TorrentLocation, limitTorrentNames, useTorrentLocation } from "./common"; +import { HkModal, LimitedNamesList, TorrentLabels, TorrentLocation, useTorrentLocation } from "./common"; import type { PriorityNumberType } from "rpc/transmission"; import { PriorityColors, PriorityStrings } from "rpc/transmission"; import type { Torrent } from "rpc/torrent"; @@ -558,9 +558,7 @@ export function AddTorrent(props: AddCommonModalProps) { const names = useMemo(() => { if (torrentData === undefined) return []; - const names = torrentData.map((td) => td.name); - - return limitTorrentNames(names, 1); + return torrentData.map((td) => td.name); }, [torrentData]); const torrentExists = existingTorrent !== undefined; @@ -578,7 +576,7 @@ export function AddTorrent(props: AddCommonModalProps) { {torrentExists ? Torrent already exists - : names.map((name, i) => {name})} + : }
{(torrentData.length > 1 || torrentData[0].files == null) diff --git a/src/components/modals/common.tsx b/src/components/modals/common.tsx index c5b008b..1013698 100644 --- a/src/components/modals/common.tsx +++ b/src/components/modals/common.tsx @@ -68,12 +68,21 @@ export function SaveCancelModal({ onSave, onClose, children, saveLoading, ...oth ); } -export function limitTorrentNames(allNames: string[], limit: number = 5) { - const names: string[] = allNames.slice(0, limit); +export function LimitedNamesList({ names, limit }: { names: string[], limit?: number }) { + limit = limit ?? 5; + const t = names.slice(0, limit); - if (allNames.length > limit) names.push(`... and ${allNames.length - limit} more`); - - return names; + return <> + {t.map((s, i) => + {s} + )} + {names.length > limit && {`... and ${names.length - limit} more`}} + ; } export function TorrentsNames() { @@ -84,20 +93,11 @@ export function TorrentsNames() { if (serverData.current == null || serverSelected.size === 0) { return ["No torrent selected"]; } - - const selected = serverData.torrents.filter( - (t) => serverSelected.has(t.id)); - - const allNames: string[] = []; - selected.forEach((t) => allNames.push(t.name)); - return allNames; + return serverData.torrents.filter( + (t) => serverSelected.has(t.id)).map((t) => t.name); }, [serverData, serverSelected]); - const names = limitTorrentNames(allNames); - - return <> - {names.map((s, i) => {s})} - ; + return ; } export interface LocationData { diff --git a/src/components/modals/edittorrent.tsx b/src/components/modals/edittorrent.tsx index e32aaac..53c092a 100644 --- a/src/components/modals/edittorrent.tsx +++ b/src/components/modals/edittorrent.tsx @@ -16,16 +16,14 @@ * along with this program. If not, see . */ -import React, { useCallback, useContext, useEffect } from "react"; +import React, { useCallback, useEffect, useMemo } from "react"; import type { ModalState } from "./common"; -import { SaveCancelModal } from "./common"; +import { SaveCancelModal, TorrentsNames } from "./common"; import { useForm } from "@mantine/form"; import { useMutateTorrent, useTorrentDetails } from "queries"; import { notifications } from "@mantine/notifications"; -import { Button, Checkbox, Grid, LoadingOverlay, NumberInput, Text, Textarea } from "@mantine/core"; -import { ConfigContext } from "config"; -import type { TrackerStats } from "rpc/torrent"; -import { useServerRpcVersion, useServerTorrentData } from "rpc/torrent"; +import { Checkbox, Grid, LoadingOverlay, NumberInput } from "@mantine/core"; +import { useServerSelectedTorrents, useServerTorrentData } from "rpc/torrent"; interface FormValues { downloadLimited?: boolean, @@ -37,16 +35,20 @@ interface FormValues { seedRatioLimit: number, seedIdleMode: number, seedIdleLimit: number, - trackerList: string, honorsSessionLimits: boolean, sequentialDownload: boolean, } export function EditTorrent(props: ModalState) { - const config = useContext(ConfigContext); const serverData = useServerTorrentData(); - const torrentId = serverData.current; - const rpcVersion = useServerRpcVersion(); + const selected = useServerSelectedTorrents(); + + const torrentId = useMemo(() => { + if (serverData.current === undefined || !selected.has(serverData.current)) { + return [...selected][0]; + } + return serverData.current; + }, [selected, serverData]); const { data: torrent, isLoading } = useTorrentDetails( torrentId ?? -1, torrentId !== undefined && props.opened, false, true); @@ -66,40 +68,22 @@ export function EditTorrent(props: ModalState) { seedRatioLimit: torrent.seedRatioLimit, seedIdleMode: torrent.seedIdleMode, seedIdleLimit: torrent.seedIdleLimit, - trackerList: rpcVersion >= 17 - ? torrent.trackerList - : torrent.trackerStats.map((s: TrackerStats) => s.announce).join("\n"), honorsSessionLimits: torrent.honorsSessionLimits, sequentialDownload: torrent.sequentialDownload, }); - }, [rpcVersion, setValues, torrent]); + }, [setValues, torrent]); const mutation = useMutateTorrent(); const onSave = useCallback(() => { if (torrentId === undefined || torrent === undefined) return; - let toAdd; - let toRemove; - if (rpcVersion < 17) { - const trackers = form.values.trackerList.split("\n").filter((s) => s !== ""); - const currentTrackers = Object.fromEntries( - torrent.trackerStats.map((s: TrackerStats) => [s.announce, s.id])); - toAdd = trackers.filter((t) => !Object.hasOwn(currentTrackers, t)); - toRemove = (torrent.trackerStats as TrackerStats[]) - .filter((s: TrackerStats) => !trackers.includes(s.announce)) - .map((s: TrackerStats) => s.id as number); - if (toAdd.length === 0) toAdd = undefined; - if (toRemove.length === 0) toRemove = undefined; - } mutation.mutate( { - torrentIds: [torrentId], + torrentIds: [...selected], fields: { ...form.values, "peer-limit": form.values.peerLimit, - trackerAdd: toAdd, - trackerRemove: toRemove, }, }, { @@ -113,14 +97,7 @@ export function EditTorrent(props: ModalState) { }, ); props.close(); - }, [form.values, mutation, torrent, props, rpcVersion, torrentId]); - - const addDefaultTrackers = useCallback(() => { - let list = form.values.trackerList; - if (!list.endsWith("\n")) list += "\n"; - list += config.values.interface.defaultTrackers.join("\n"); - form.setFieldValue("trackerList", list); - }, [config, form]); + }, [torrentId, torrent, mutation, selected, form.values, props]); return <>{props.opened && - Torrent: {torrent?.name} + minutes - - Tracker list, one per line, empty line between tiers - - - - - -