diff --git a/src/components/modals/daemon.tsx b/src/components/modals/daemon.tsx index 7ce584b..9f701fb 100644 --- a/src/components/modals/daemon.tsx +++ b/src/components/modals/daemon.tsx @@ -597,7 +597,7 @@ export function DaemonSettingsModal(props: ModalState) { > - + Polling Download diff --git a/src/components/modals/interfacepanel.tsx b/src/components/modals/interfacepanel.tsx index 08d4655..bc2b2cd 100644 --- a/src/components/modals/interfacepanel.tsx +++ b/src/components/modals/interfacepanel.tsx @@ -18,7 +18,7 @@ import React, { useCallback, useEffect, useState } from "react"; import type { ColorScheme } from "@mantine/core"; -import { Checkbox, Grid, MultiSelect, NativeSelect, NumberInput, Textarea, useMantineTheme } from "@mantine/core"; +import { Box, Checkbox, Grid, HoverCard, MultiSelect, NativeSelect, NumberInput, Text, Textarea, useMantineTheme } from "@mantine/core"; import type { UseFormReturnType } from "@mantine/form"; import ColorChooser from "components/colorchooser"; import { useGlobalStyleOverrides } from "themehooks"; @@ -26,6 +26,7 @@ import { DeleteTorrentDataOptions } from "config"; import type { ColorSetting, DeleteTorrentDataOption, StyleOverrides } from "config"; import { ColorSchemeToggle } from "components/miscbuttons"; import { Label } from "./common"; +import * as Icon from "react-bootstrap-icons"; const { TAURI, invoke } = await import(/* webpackChunkName: "taurishim" */"taurishim"); export interface InterfaceFormValues { @@ -39,6 +40,7 @@ export interface InterfaceFormValues { numLastSaveDirs: number, sortLastSaveDirs: boolean, preconfiguredLabels: string[], + ignoredTrackerPrefixes: string[], defaultTrackers: string[], }, } @@ -59,7 +61,7 @@ export function InterfaceSettigsPanel(props: { fo } }, []); - const { setFieldValue } = props.form as unknown as UseFormReturnType; + const { setFieldValue, setFieldError, clearFieldError } = props.form as unknown as UseFormReturnType; useEffect(() => { setFieldValue("interface.theme", theme.colorScheme); @@ -101,6 +103,17 @@ export function InterfaceSettigsPanel(props: { fo setFieldValue("interface.preconfiguredLabels", labels); }, [setFieldValue]); + const setIgnoredTrackerPrefixes = useCallback((prefixes: string[]) => { + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _ = new RegExp(`^(?(${prefixes.join("|")})\\d*)\\.[^.]+\\.[^.]+$`, "i"); + setFieldValue("interface.ignoredTrackerPrefixes", prefixes); + clearFieldError("interface.ignoredTrackerPrefixes"); + } catch (SyntaxError) { + setFieldError("interface.ignoredTrackerPrefixes", "Invalid regex"); + } + }, [setFieldValue, setFieldError, clearFieldError]); + return ( @@ -161,7 +174,20 @@ export function InterfaceSettigsPanel(props: { fo data={props.form.values.interface.preconfiguredLabels} value={props.form.values.interface.preconfiguredLabels} onChange={setPreconfiguredLabels} - label="Preconfigured labels" + label={ + Preconfigured labels + + + + + + + These labels will always be present in the suggestions list + and filters even if no existing torrents have them. + + + + } withinPortal searchable creatable @@ -173,6 +199,39 @@ export function InterfaceSettigsPanel(props: { fo valueComponent={Label} /> + + + Ignored tracker prefixes + + + + + + + When subdomain of the tracker looks like one of these strings + (optional) digits, + it will be omitted. This affects grouping in filters and display in table columns. + You can use regex here for more advanced filtering, the list will be combined + using "|". + + + + } + withinPortal + searchable + creatable + error={props.form.errors["interface.ignoredTrackerPrefixes"]} + getCreateLabel={(query) => `+ Add ${query}`} + onCreate={(query) => { + setIgnoredTrackerPrefixes([...props.form.values.interface.ignoredTrackerPrefixes, query]); + return query; + }} + valueComponent={Label} + /> +