New: Add tag support to indexers

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
This commit is contained in:
Qstick 2022-09-26 14:20:20 -05:00
commit a26cbdf61f
29 changed files with 351 additions and 578 deletions

View file

@ -43,6 +43,7 @@ function EditIndexerModalContent(props) {
enableInteractiveSearch,
supportsRss,
supportsSearch,
tags,
fields,
priority,
protocol,
@ -168,18 +169,30 @@ function EditIndexerModalContent(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>DownloadClient</FormLabel>
<FormLabel>{translate('DownloadClient')}</FormLabel>
<FormInputGroup
type={inputTypes.DOWNLOAD_CLIENT_SELECT}
name="downloadClientId"
helpText={'Specify which download client is used for grabs from this indexer'}
helpText={translate('IndexerDownloadClientHelpText')}
{...downloadClientId}
includeAny={true}
protocol={protocol.value}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Tags')}</FormLabel>
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText={translate('IndexerTagHelpText')}
{...tags}
onChange={onInputChange}
/>
</FormGroup>
</Form>
}
</ModalBody>

View file

@ -4,6 +4,7 @@ import Card from 'Components/Card';
import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import TagList from 'Components/TagList';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import EditIndexerModalConnector from './EditIndexerModalConnector';
@ -68,6 +69,8 @@ class Indexer extends Component {
enableRss,
enableAutomaticSearch,
enableInteractiveSearch,
tags,
tagList,
supportsRss,
supportsSearch,
priority,
@ -133,6 +136,11 @@ class Indexer extends Component {
}
</div>
<TagList
tags={tags}
tagList={tagList}
/>
<EditIndexerModalConnector
id={id}
isOpen={this.state.isEditIndexerModalOpen}
@ -161,6 +169,8 @@ Indexer.propTypes = {
enableRss: PropTypes.bool.isRequired,
enableAutomaticSearch: PropTypes.bool.isRequired,
enableInteractiveSearch: PropTypes.bool.isRequired,
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
supportsRss: PropTypes.bool.isRequired,
supportsSearch: PropTypes.bool.isRequired,
showPriority: PropTypes.bool.isRequired,

View file

@ -54,6 +54,7 @@ class Indexers extends Component {
render() {
const {
items,
tagList,
dispatchCloneIndexer,
onConfirmDeleteIndexer,
...otherProps
@ -79,6 +80,7 @@ class Indexers extends Component {
<Indexer
key={item.id}
{...item}
tagList={tagList}
showPriority={showPriority}
onCloneIndexerPress={this.onCloneIndexerPress}
onConfirmDeleteIndexer={onConfirmDeleteIndexer}
@ -119,6 +121,7 @@ Indexers.propTypes = {
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchCloneIndexer: PropTypes.func.isRequired,
onConfirmDeleteIndexer: PropTypes.func.isRequired
};

View file

@ -4,13 +4,20 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { cloneIndexer, deleteIndexer, fetchIndexers } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import createTagsSelector from 'Store/Selectors/createTagsSelector';
import sortByName from 'Utilities/Array/sortByName';
import Indexers from './Indexers';
function createMapStateToProps() {
return createSelector(
createSortedSectionSelector('settings.indexers', sortByName),
(indexers) => indexers
createTagsSelector(),
(indexers, tagList) => {
return {
...indexers,
tagList
};
}
);
}

View file

@ -22,6 +22,7 @@ function TagDetailsModalContent(props) {
importLists,
notifications,
releaseProfiles,
indexers,
onModalClose,
onDeleteTagPress
} = props;
@ -41,7 +42,7 @@ function TagDetailsModalContent(props) {
}
{
!!artist.length &&
artist.length ?
<FieldSet legend={translate('Artists')}>
{
artist.map((item) => {
@ -52,11 +53,12 @@ function TagDetailsModalContent(props) {
);
})
}
</FieldSet>
</FieldSet> :
null
}
{
!!delayProfiles.length &&
delayProfiles.length ?
<FieldSet legend={translate('DelayProfile')}>
{
delayProfiles.map((item) => {
@ -81,11 +83,12 @@ function TagDetailsModalContent(props) {
);
})
}
</FieldSet>
</FieldSet> :
null
}
{
!!notifications.length &&
notifications.length ?
<FieldSet legend={translate('Connections')}>
{
notifications.map((item) => {
@ -96,11 +99,12 @@ function TagDetailsModalContent(props) {
);
})
}
</FieldSet>
</FieldSet> :
null
}
{
!!importLists.length &&
importLists.length ?
<FieldSet legend={translate('ImportLists')}>
{
importLists.map((item) => {
@ -111,11 +115,12 @@ function TagDetailsModalContent(props) {
);
})
}
</FieldSet>
</FieldSet> :
null
}
{
!!releaseProfiles.length &&
releaseProfiles.length ?
<FieldSet legend={translate('ReleaseProfiles')}>
{
releaseProfiles.map((item) => {
@ -157,7 +162,24 @@ function TagDetailsModalContent(props) {
);
})
}
</FieldSet>
</FieldSet> :
null
}
{
indexers.length ?
<FieldSet legend="Indexers">
{
indexers.map((item) => {
return (
<div key={item.id}>
{item.name}
</div>
);
})
}
</FieldSet> :
null
}
</ModalBody>
@ -192,6 +214,7 @@ TagDetailsModalContent.propTypes = {
importLists: PropTypes.arrayOf(PropTypes.object).isRequired,
notifications: PropTypes.arrayOf(PropTypes.object).isRequired,
releaseProfiles: PropTypes.arrayOf(PropTypes.object).isRequired,
indexers: PropTypes.arrayOf(PropTypes.object).isRequired,
onModalClose: PropTypes.func.isRequired,
onDeleteTagPress: PropTypes.func.isRequired
};

View file

@ -69,6 +69,14 @@ function createMatchingReleaseProfilesSelector() {
);
}
function createMatchingIndexersSelector() {
return createSelector(
(state, { indexerIds }) => indexerIds,
(state) => state.settings.indexers.items,
findMatchingItems
);
}
function createMapStateToProps() {
return createSelector(
createMatchingArtistSelector(),
@ -76,13 +84,15 @@ function createMapStateToProps() {
createMatchingImportListsSelector(),
createMatchingNotificationsSelector(),
createMatchingReleaseProfilesSelector(),
(artist, delayProfiles, importLists, notifications, releaseProfiles) => {
createMatchingIndexersSelector(),
(artist, delayProfiles, importLists, notifications, releaseProfiles, indexers) => {
return {
artist,
delayProfiles,
importLists,
notifications,
releaseProfiles
releaseProfiles,
indexers
};
}
);

View file

@ -57,6 +57,7 @@ class Tag extends Component {
importListIds,
notificationIds,
restrictionIds,
indexerIds,
artistIds
} = this.props;
@ -70,6 +71,7 @@ class Tag extends Component {
importListIds.length ||
notificationIds.length ||
restrictionIds.length ||
indexerIds.length ||
artistIds.length
);
@ -87,38 +89,50 @@ class Tag extends Component {
isTagUsed &&
<div>
{
!!artistIds.length &&
artistIds.length ?
<div>
{artistIds.length} artists
</div>
</div> :
null
}
{
!!delayProfileIds.length &&
delayProfileIds.length ?
<div>
{delayProfileIds.length} delay profile{delayProfileIds.length > 1 && 's'}
</div>
</div> :
null
}
{
!!importListIds.length &&
importListIds.length ?
<div>
{importListIds.length} import list{importListIds.length > 1 && 's'}
</div>
</div> :
null
}
{
!!notificationIds.length &&
notificationIds.length ?
<div>
{notificationIds.length} connection{notificationIds.length > 1 && 's'}
</div>
</div> :
null
}
{
!!restrictionIds.length &&
restrictionIds.length ?
<div>
{restrictionIds.length} restriction{restrictionIds.length > 1 && 's'}
</div>
</div> :
null
}
{
indexerIds.length ?
<div>
{indexerIds.length} indexer{indexerIds.length > 1 && 's'}
</div> :
null
}
</div>
}
@ -138,6 +152,7 @@ class Tag extends Component {
importListIds={importListIds}
notificationIds={notificationIds}
restrictionIds={restrictionIds}
indexerIds={indexerIds}
isOpen={isDetailsModalOpen}
onModalClose={this.onDetailsModalClose}
onDeleteTagPress={this.onDeleteTagPress}
@ -164,6 +179,7 @@ Tag.propTypes = {
importListIds: PropTypes.arrayOf(PropTypes.number).isRequired,
notificationIds: PropTypes.arrayOf(PropTypes.number).isRequired,
restrictionIds: PropTypes.arrayOf(PropTypes.number).isRequired,
indexerIds: PropTypes.arrayOf(PropTypes.number).isRequired,
artistIds: PropTypes.arrayOf(PropTypes.number).isRequired,
onConfirmDeleteTag: PropTypes.func.isRequired
};
@ -173,6 +189,7 @@ Tag.defaultProps = {
importListIds: [],
notificationIds: [],
restrictionIds: [],
indexerIds: [],
artistIds: []
};

View file

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchDelayProfiles, fetchImportLists, fetchNotifications, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
import { fetchDelayProfiles, fetchImportLists, fetchIndexers, fetchNotifications, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
import { fetchTagDetails } from 'Store/Actions/tagActions';
import Tags from './Tags';
@ -29,7 +29,8 @@ const mapDispatchToProps = {
dispatchFetchDelayProfiles: fetchDelayProfiles,
dispatchFetchImportLists: fetchImportLists,
dispatchFetchNotifications: fetchNotifications,
dispatchFetchReleaseProfiles: fetchReleaseProfiles
dispatchFetchReleaseProfiles: fetchReleaseProfiles,
dispatchFetchIndexers: fetchIndexers
};
class MetadatasConnector extends Component {
@ -43,7 +44,8 @@ class MetadatasConnector extends Component {
dispatchFetchDelayProfiles,
dispatchFetchImportLists,
dispatchFetchNotifications,
dispatchFetchReleaseProfiles
dispatchFetchReleaseProfiles,
dispatchFetchIndexers
} = this.props;
dispatchFetchTagDetails();
@ -51,6 +53,7 @@ class MetadatasConnector extends Component {
dispatchFetchImportLists();
dispatchFetchNotifications();
dispatchFetchReleaseProfiles();
dispatchFetchIndexers();
}
//
@ -70,7 +73,8 @@ MetadatasConnector.propTypes = {
dispatchFetchDelayProfiles: PropTypes.func.isRequired,
dispatchFetchImportLists: PropTypes.func.isRequired,
dispatchFetchNotifications: PropTypes.func.isRequired,
dispatchFetchReleaseProfiles: PropTypes.func.isRequired
dispatchFetchReleaseProfiles: PropTypes.func.isRequired,
dispatchFetchIndexers: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(MetadatasConnector);