From a20a81f424998750a7406948f740aa0424232bb9 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Tue, 23 Jan 2024 07:58:35 +0200 Subject: [PATCH] New: Category filter for Indexers --- frontend/src/App/State/SettingsAppState.ts | 7 ++++ .../Builder/CategoryFilterBuilderRowValue.tsx | 41 +++++++++++++++++++ .../Filter/Builder/FilterBuilderRow.js | 4 ++ .../Helpers/Props/filterBuilderValueTypes.js | 2 +- frontend/src/Store/Actions/indexerActions.js | 24 ++++++++++- .../src/Store/Actions/indexerIndexActions.js | 6 +++ 6 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 frontend/src/Components/Filter/Builder/CategoryFilterBuilderRowValue.tsx diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index 9dc6dfa2c..d4152431c 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -3,6 +3,7 @@ import AppSectionState, { AppSectionItemState, AppSectionSaveState, } from 'App/State/AppSectionState'; +import { IndexerCategory } from 'Indexer/Indexer'; import Application from 'typings/Application'; import DownloadClient from 'typings/DownloadClient'; import Notification from 'typings/Notification'; @@ -25,6 +26,11 @@ export interface DownloadClientAppState AppSectionDeleteState, AppSectionSaveState {} +export interface IndexerCategoryAppState + extends AppSectionState, + AppSectionDeleteState, + AppSectionSaveState {} + export interface NotificationAppState extends AppSectionState, AppSectionDeleteState {} @@ -35,6 +41,7 @@ interface SettingsAppState { appProfiles: AppProfileAppState; applications: ApplicationAppState; downloadClients: DownloadClientAppState; + indexerCategories: IndexerCategoryAppState; notifications: NotificationAppState; ui: UiSettingsAppState; } diff --git a/frontend/src/Components/Filter/Builder/CategoryFilterBuilderRowValue.tsx b/frontend/src/Components/Filter/Builder/CategoryFilterBuilderRowValue.tsx new file mode 100644 index 000000000..6a7dddcfc --- /dev/null +++ b/frontend/src/Components/Filter/Builder/CategoryFilterBuilderRowValue.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; +import { IndexerCategory } from 'Indexer/Indexer'; +import FilterBuilderRowValue from './FilterBuilderRowValue'; +import FilterBuilderRowValueProps from './FilterBuilderRowValueProps'; + +const indexerCategoriesSelector = createSelector( + (state: AppState) => state.settings.indexerCategories, + (categories) => categories.items +); + +function CategoryFilterBuilderRowValue(props: FilterBuilderRowValueProps) { + const categories: IndexerCategory[] = useSelector(indexerCategoriesSelector); + + const tagList = categories.reduce( + (acc: { id: number; name: string }[], element) => { + acc.push({ + id: element.id, + name: `${element.name} (${element.id})`, + }); + + if (element.subCategories && element.subCategories.length > 0) { + element.subCategories.forEach((subCat) => { + acc.push({ + id: subCat.id, + name: `${subCat.name} (${subCat.id})`, + }); + }); + } + + return acc; + }, + [] + ); + + return ; +} + +export default CategoryFilterBuilderRowValue; diff --git a/frontend/src/Components/Filter/Builder/FilterBuilderRow.js b/frontend/src/Components/Filter/Builder/FilterBuilderRow.js index 8899eaf5b..51622509b 100644 --- a/frontend/src/Components/Filter/Builder/FilterBuilderRow.js +++ b/frontend/src/Components/Filter/Builder/FilterBuilderRow.js @@ -5,6 +5,7 @@ import IconButton from 'Components/Link/IconButton'; import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Props'; import AppProfileFilterBuilderRowValueConnector from './AppProfileFilterBuilderRowValueConnector'; import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue'; +import CategoryFilterBuilderRowValue from './CategoryFilterBuilderRowValue'; import DateFilterBuilderRowValue from './DateFilterBuilderRowValue'; import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector'; import HistoryEventTypeFilterBuilderRowValue from './HistoryEventTypeFilterBuilderRowValue'; @@ -56,6 +57,9 @@ function getRowValueConnector(selectedFilterBuilderProp) { case filterBuilderValueTypes.BOOL: return BoolFilterBuilderRowValue; + case filterBuilderValueTypes.CATEGORY: + return CategoryFilterBuilderRowValue; + case filterBuilderValueTypes.DATE: return DateFilterBuilderRowValue; diff --git a/frontend/src/Helpers/Props/filterBuilderValueTypes.js b/frontend/src/Helpers/Props/filterBuilderValueTypes.js index 19c8ccd9c..73ef41956 100644 --- a/frontend/src/Helpers/Props/filterBuilderValueTypes.js +++ b/frontend/src/Helpers/Props/filterBuilderValueTypes.js @@ -7,5 +7,5 @@ export const INDEXER = 'indexer'; export const PROTOCOL = 'protocol'; export const PRIVACY = 'privacy'; export const APP_PROFILE = 'appProfile'; -export const MOVIE_STATUS = 'movieStatus'; +export const CATEGORY = 'category'; export const TAG = 'tag'; diff --git a/frontend/src/Store/Actions/indexerActions.js b/frontend/src/Store/Actions/indexerActions.js index ff0cfe5d4..5a4261c29 100644 --- a/frontend/src/Store/Actions/indexerActions.js +++ b/frontend/src/Store/Actions/indexerActions.js @@ -1,6 +1,6 @@ import _ from 'lodash'; import { createAction } from 'redux-actions'; -import { sortDirections } from 'Helpers/Props'; +import { filterTypePredicates, sortDirections } from 'Helpers/Props'; import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler'; import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler'; @@ -69,6 +69,28 @@ export const filterPredicates = { item.fields.find((field) => field.name === 'vipExpiration')?.value ?? null; return dateFilterPredicate(vipExpiration, filterValue, type); + }, + + categories: function(item, filterValue, type) { + const predicate = filterTypePredicates[type]; + + const { categories = [] } = item.capabilities || {}; + + const categoryList = categories + .filter((category) => category.id < 100000) + .reduce((acc, element) => { + acc.push(element.id); + + if (element.subCategories && element.subCategories.length > 0) { + element.subCategories.forEach((subCat) => { + acc.push(subCat.id); + }); + } + + return acc; + }, []); + + return predicate(categoryList, filterValue); } }; diff --git a/frontend/src/Store/Actions/indexerIndexActions.js b/frontend/src/Store/Actions/indexerIndexActions.js index a09af6228..fa4bc3a15 100644 --- a/frontend/src/Store/Actions/indexerIndexActions.js +++ b/frontend/src/Store/Actions/indexerIndexActions.js @@ -186,6 +186,12 @@ export const defaultState = { type: filterBuilderTypes.EXACT, valueType: filterBuilderValueTypes.APP_PROFILE }, + { + name: 'categories', + label: () => translate('Categories'), + type: filterBuilderTypes.ARRAY, + valueType: filterBuilderValueTypes.CATEGORY + }, { name: 'tags', label: () => translate('Tags'),