-
@@ -452,22 +447,13 @@ export const FilterDetails = () => {
{({ values, dirty, resetForm }) => (
diff --git a/web/src/screens/filters/Importer.tsx b/web/src/screens/filters/Importer.tsx
index 1fdf2b7..9db8e2e 100644
--- a/web/src/screens/filters/Importer.tsx
+++ b/web/src/screens/filters/Importer.tsx
@@ -4,9 +4,9 @@ import { useQueryClient } from "@tanstack/react-query";
import toast from "react-hot-toast";
import { APIClient } from "@api/APIClient";
+import { FilterKeys } from "@api/query_keys";
import Toast from "@components/notifications/Toast";
-import { filterKeys } from "./List";
import { AutodlIrssiConfigParser } from "./_configParser";
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
@@ -211,7 +211,7 @@ export const Importer = ({
} finally {
setIsOpen(false);
// Invalidate filter cache, and trigger refresh request
- await queryClient.invalidateQueries({ queryKey: filterKeys.lists() });
+ await queryClient.invalidateQueries({ queryKey: FilterKeys.lists() });
}
};
diff --git a/web/src/screens/filters/List.tsx b/web/src/screens/filters/List.tsx
index a9053bf..f0eced4 100644
--- a/web/src/screens/filters/List.tsx
+++ b/web/src/screens/filters/List.tsx
@@ -3,24 +3,23 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
-import { Dispatch, FC, Fragment, MouseEventHandler, useReducer, useRef, useState, useEffect } from "react";
-import { Link } from "react-router-dom";
+import { Dispatch, FC, Fragment, MouseEventHandler, useCallback, useEffect, useReducer, useRef, useState } from "react";
+import { Link } from '@tanstack/react-router'
import { toast } from "react-hot-toast";
import { Listbox, Menu, Transition } from "@headlessui/react";
-import { useMutation, useQuery, useQueryClient, keepPreviousData, useSuspenseQuery } from "@tanstack/react-query";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { FormikValues } from "formik";
-import { useCallback } from "react";
import {
ArrowsRightLeftIcon,
+ ArrowUpOnSquareIcon,
+ ChatBubbleBottomCenterTextIcon,
CheckIcon,
ChevronDownIcon,
- PlusIcon,
DocumentDuplicateIcon,
EllipsisHorizontalIcon,
PencilSquareIcon,
- ChatBubbleBottomCenterTextIcon,
- TrashIcon,
- ArrowUpOnSquareIcon
+ PlusIcon,
+ TrashIcon
} from "@heroicons/react/24/outline";
import { ArrowDownTrayIcon } from "@heroicons/react/24/solid";
@@ -29,6 +28,8 @@ import { classNames } from "@utils";
import { FilterAddForm } from "@forms";
import { useToggle } from "@hooks/hooks";
import { APIClient } from "@api/APIClient";
+import { FilterKeys } from "@api/query_keys";
+import { FiltersQueryOptions, IndexersOptionsQueryOptions } from "@api/queries";
import Toast from "@components/notifications/Toast";
import { EmptyListState } from "@components/emptystates";
import { DeleteModal } from "@components/modals";
@@ -37,14 +38,6 @@ import { Importer } from "./Importer";
import { Tooltip } from "@components/tooltips/Tooltip";
import { Checkbox } from "@components/Checkbox";
-export const filterKeys = {
- all: ["filters"] as const,
- lists: () => [...filterKeys.all, "list"] as const,
- list: (indexers: string[], sortOrder: string) => [...filterKeys.lists(), { indexers, sortOrder }] as const,
- details: () => [...filterKeys.all, "detail"] as const,
- detail: (id: number) => [...filterKeys.details(), id] as const
-};
-
enum ActionType {
INDEXER_FILTER_CHANGE = "INDEXER_FILTER_CHANGE",
INDEXER_FILTER_RESET = "INDEXER_FILTER_RESET",
@@ -192,11 +185,7 @@ function FilterList({ toggleCreateFilter }: any) {
filterListState
);
- const { data, error } = useSuspenseQuery({
- queryKey: filterKeys.list(indexerFilter, sortOrder),
- queryFn: ({ queryKey }) => APIClient.filters.find(queryKey[2].indexers, queryKey[2].sortOrder),
- refetchOnWindowFocus: false
- });
+ const { data, error } = useQuery(FiltersQueryOptions(indexerFilter, sortOrder));
useEffect(() => {
FilterListContext.set({ indexerFilter, sortOrder, status });
@@ -407,8 +396,8 @@ const FilterItemDropdown = ({ filter, onToggle }: FilterItemDropdownProps) => {
const deleteMutation = useMutation({
mutationFn: (id: number) => APIClient.filters.delete(id),
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: filterKeys.lists() });
- queryClient.invalidateQueries({ queryKey: filterKeys.detail(filter.id) });
+ queryClient.invalidateQueries({ queryKey: FilterKeys.lists() });
+ queryClient.invalidateQueries({ queryKey: FilterKeys.detail(filter.id) });
toast.custom((t) =>
);
}
@@ -417,7 +406,7 @@ const FilterItemDropdown = ({ filter, onToggle }: FilterItemDropdownProps) => {
const duplicateMutation = useMutation({
mutationFn: (id: number) => APIClient.filters.duplicate(id),
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: filterKeys.lists() });
+ queryClient.invalidateQueries({ queryKey: FilterKeys.lists() });
toast.custom((t) =>
);
}
@@ -459,7 +448,11 @@ const FilterItemDropdown = ({ filter, onToggle }: FilterItemDropdownProps) => {
{({ active }) => (
{filter.name}
@@ -645,7 +641,10 @@ function FilterListItem({ filter, values, idx }: FilterListItemProps) {
@@ -666,7 +665,10 @@ function FilterListItem({ filter, values, idx }: FilterListItemProps) {
) : (
@@ -784,12 +786,9 @@ const ListboxFilter = ({
// a unique option from a list
const IndexerSelectFilter = ({ dispatch }: any) => {
- const { data, isSuccess } = useQuery({
- queryKey: ["filters", "indexers_options"],
- queryFn: () => APIClient.indexers.getOptions(),
- placeholderData: keepPreviousData,
- staleTime: Infinity
- });
+ const filterListState = FilterListContext.useValue();
+
+ const { data, isSuccess } = useQuery(IndexersOptionsQueryOptions());
const setFilter = (value: string) => {
if (value == undefined || value == "") {
@@ -804,11 +803,11 @@ const IndexerSelectFilter = ({ dispatch }: any) => {
i.identifier == filterListState.indexerFilter[0])?.name}` : "Indexer"}
+ currentValue={filterListState.indexerFilter[0] ?? ""}
onChange={setFilter}
>
-
+
{isSuccess && data?.map((indexer, idx) => (
))}
@@ -830,7 +829,7 @@ const FilterOption = ({ label, value }: FilterOptionProps) => (
value={value}
>
{({ selected }) => (
- <>
+
(
{label}
{selected ? (
-
+
) : null}
- >
+
)}
);
export const SortSelectFilter = ({ dispatch }: any) => {
+ const filterListState = FilterListContext.useValue();
+
const setFilter = (value: string) => {
if (value == undefined || value == "") {
dispatch({ type: ActionType.SORT_ORDER_RESET, payload: "" });
@@ -870,8 +871,8 @@ export const SortSelectFilter = ({ dispatch }: any) => {
o.value == filterListState.sortOrder)?.label}` : "Sort"}
+ currentValue={filterListState.sortOrder ?? ""}
onChange={setFilter}
>
<>
diff --git a/web/src/screens/filters/NotFound.tsx b/web/src/screens/filters/NotFound.tsx
new file mode 100644
index 0000000..3d89e59
--- /dev/null
+++ b/web/src/screens/filters/NotFound.tsx
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 - 2024, Ludvig Lundgren and the autobrr contributors.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+import { Link } from "@tanstack/react-router";
+import { FilterGetByIdRoute } from "@app/routes";
+import { ExternalLink } from "@components/ExternalLink";
+
+import Logo from "@app/logo.svg?react";
+
+export const FilterNotFound = () => {
+ const { filterId } = FilterGetByIdRoute.useParams()
+
+ return (
+
+
+
+
+
+ Status 404
+
+
+ Filter with id {filterId} not found!
+
+
+ In case you think this is a bug rather than too much brr,
+
+
+ feel free to report this to our
+ {" "}
+
+ GitHub page
+
+ {" or to "}
+
+ our official Discord channel
+
+ .
+
+
+ Otherwise, let us help you to get you back on track for more brr!
+
+
+
+
+
+
+
+ );
+};
diff --git a/web/src/screens/filters/index.ts b/web/src/screens/filters/index.ts
index 5e341ce..a83e411 100644
--- a/web/src/screens/filters/index.ts
+++ b/web/src/screens/filters/index.ts
@@ -5,3 +5,4 @@
export { Filters } from "./List";
export { FilterDetails } from "./Details";
+export { FilterNotFound } from "./NotFound";
\ No newline at end of file
diff --git a/web/src/screens/filters/sections/Actions.tsx b/web/src/screens/filters/sections/Actions.tsx
index 6ff9dac..78cd6f3 100644
--- a/web/src/screens/filters/sections/Actions.tsx
+++ b/web/src/screens/filters/sections/Actions.tsx
@@ -7,7 +7,7 @@ import { useEffect, useRef, useState } from "react";
import { toast } from "react-hot-toast";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Field, FieldArray, useFormikContext } from "formik";
-import type { FieldProps, FieldArrayRenderProps, FormikValues } from "formik";
+import type { FieldProps, FieldArrayRenderProps } from "formik";
import { ChevronRightIcon, BoltIcon } from "@heroicons/react/24/solid";
import { classNames } from "@utils";
@@ -25,18 +25,17 @@ import { TitleSubtitle } from "@components/headings";
import * as FilterSection from "./_components";
import * as FilterActions from "./action_components";
+import { DownloadClientsQueryOptions } from "@api/queries";
-interface FilterActionsProps {
- filter: Filter;
- values: FormikValues;
-}
+// interface FilterActionsProps {
+// filter: Filter;
+// values: FormikValues;
+// }
-export function Actions({ filter, values }: FilterActionsProps) {
- const { data } = useQuery({
- queryKey: ["filters", "download_clients"],
- queryFn: () => APIClient.download_clients.getAll(),
- refetchOnWindowFocus: false
- });
+export function Actions() {
+ const { values } = useFormikContext();
+
+ const { data } = useQuery(DownloadClientsQueryOptions());
const newAction: Action = {
id: 0,
@@ -63,7 +62,7 @@ export function Actions({ filter, values }: FilterActionsProps) {
reannounce_delete: false,
reannounce_interval: 7,
reannounce_max_attempts: 25,
- filter_id: filter.id,
+ filter_id: values.id,
webhook_host: "",
webhook_type: "",
webhook_method: "",
diff --git a/web/src/screens/filters/sections/Advanced.tsx b/web/src/screens/filters/sections/Advanced.tsx
index b357a36..91a277a 100644
--- a/web/src/screens/filters/sections/Advanced.tsx
+++ b/web/src/screens/filters/sections/Advanced.tsx
@@ -1,4 +1,4 @@
-import type { FormikValues } from "formik";
+import { useFormikContext } from "formik";
import { DocsLink } from "@components/ExternalLink";
import { WarningAlert } from "@components/alerts";
@@ -10,493 +10,533 @@ import { CollapsibleSection } from "./_components";
import * as Components from "./_components";
import { classNames } from "@utils";
-type ValueConsumer = {
- values: FormikValues;
-};
+// type ValueConsumer = {
+// values: FormikValues;
+// };
-const Releases = ({ values }: ValueConsumer) => (
-
-
-
-
-
-
+const Releases = () => {
+ const { values } = useFormikContext();
-
-
-
+
+
+
+
+
+
+
+
+
+ This field has full regex support (Golang flavour).
+
+
+
+ Remember to tick Use Regex if using more than *
and ?
.
+
+ }
+ />
+
+
+
+
+ This field has full regex support (Golang flavour).
+
+
+
+ Remember to tick Use Regex below if using more than *
and ?
.
+
+ }
+ />
+
+
+
+
+ {values.match_releases ? (
+