mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
New: Add a filter bar to the "Add Indexers" modal (#607)
* Add a filter bar to the "Add Indexers" modal * Fix stylelint errors * Hide AddIndexerModal alert on small screens
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
composes: input from '~Components/Form/TextInput.css';
|
composes: input from '~Components/Form/TextInput.css';
|
||||||
|
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert {
|
.alert {
|
||||||
@@ -28,3 +28,46 @@
|
|||||||
.scroller {
|
.scroller {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filterRow {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterContainer:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterLabel {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: $breakpointSmall) {
|
||||||
|
.alert {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterRow {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterContainer {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroller {
|
||||||
|
margin-right: -30px;
|
||||||
|
margin-bottom: -30px;
|
||||||
|
margin-left: -30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Alert from 'Components/Alert';
|
import Alert from 'Components/Alert';
|
||||||
|
import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput';
|
||||||
import TextInput from 'Components/Form/TextInput';
|
import TextInput from 'Components/Form/TextInput';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
@@ -44,6 +45,17 @@ const columns = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const protocols = [
|
||||||
|
{
|
||||||
|
key: 'torrent',
|
||||||
|
value: 'torrent'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'usenet',
|
||||||
|
value: 'nzb'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
class AddIndexerModalContent extends Component {
|
class AddIndexerModalContent extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -53,7 +65,10 @@ class AddIndexerModalContent extends Component {
|
|||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: ''
|
filter: '',
|
||||||
|
filterProtocols: [],
|
||||||
|
filterLanguages: [],
|
||||||
|
filterPrivacyLevels: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,8 +95,35 @@ class AddIndexerModalContent extends Component {
|
|||||||
onModalClose
|
onModalClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const filter = this.state.filter;
|
const languages = Array.from(new Set(indexers.map(({ language }) => language)))
|
||||||
const filterLower = filter.toLowerCase();
|
.sort((a, b) => a.localeCompare(b))
|
||||||
|
.map((language) => ({ key: language, value: language }));
|
||||||
|
|
||||||
|
const privacyLevels = Array.from(new Set(indexers.map(({ privacy }) => privacy)))
|
||||||
|
.sort((a, b) => a.localeCompare(b))
|
||||||
|
.map((privacy) => ({ key: privacy, value: privacy }));
|
||||||
|
|
||||||
|
const filteredIndexers = indexers.filter((indexer) => {
|
||||||
|
const { filter, filterProtocols, filterLanguages, filterPrivacyLevels } = this.state;
|
||||||
|
|
||||||
|
if (!indexer.name.toLowerCase().includes(filter.toLocaleLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterProtocols.length && !filterProtocols.includes(indexer.protocol)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterLanguages.length && !filterLanguages.includes(indexer.language)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterPrivacyLevels.length && !filterPrivacyLevels.includes(indexer.privacy)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const errorMessage = getErrorMessage(error, 'Unable to load indexers');
|
const errorMessage = getErrorMessage(error, 'Unable to load indexers');
|
||||||
|
|
||||||
@@ -99,11 +141,43 @@ class AddIndexerModalContent extends Component {
|
|||||||
className={styles.filterInput}
|
className={styles.filterInput}
|
||||||
placeholder={translate('FilterPlaceHolder')}
|
placeholder={translate('FilterPlaceHolder')}
|
||||||
name="filter"
|
name="filter"
|
||||||
value={filter}
|
value={this.state.filter}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
onChange={this.onFilterChange}
|
onChange={this.onFilterChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className={styles.filterRow}>
|
||||||
|
<div className={styles.filterContainer}>
|
||||||
|
<label className={styles.filterLabel}>Protocol</label>
|
||||||
|
<EnhancedSelectInput
|
||||||
|
name="indexerProtocols"
|
||||||
|
value={this.state.filterProtocols}
|
||||||
|
values={protocols}
|
||||||
|
onChange={({ value }) => this.setState({ filterProtocols: value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.filterContainer}>
|
||||||
|
<label className={styles.filterLabel}>Language</label>
|
||||||
|
<EnhancedSelectInput
|
||||||
|
name="indexerLanguages"
|
||||||
|
value={this.state.filterLanguages}
|
||||||
|
values={languages}
|
||||||
|
onChange={({ value }) => this.setState({ filterLanguages: value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.filterContainer}>
|
||||||
|
<label className={styles.filterLabel}>Privacy</label>
|
||||||
|
<EnhancedSelectInput
|
||||||
|
name="indexerPrivacyLevels"
|
||||||
|
value={this.state.filterPrivacyLevels}
|
||||||
|
values={privacyLevels}
|
||||||
|
onChange={({ value }) => this.setState({ filterPrivacyLevels: value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
kind={kinds.INFO}
|
kind={kinds.INFO}
|
||||||
className={styles.alert}
|
className={styles.alert}
|
||||||
@@ -133,18 +207,14 @@ class AddIndexerModalContent extends Component {
|
|||||||
>
|
>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{
|
{
|
||||||
indexers.map((indexer) => {
|
filteredIndexers.map((indexer) => (
|
||||||
return indexer.name.toLowerCase().includes(filterLower) ?
|
<SelectIndexerRow
|
||||||
(
|
key={indexer.name}
|
||||||
<SelectIndexerRow
|
implementation={indexer.implementation}
|
||||||
key={indexer.name}
|
{...indexer}
|
||||||
implementation={indexer.implementation}
|
onIndexerSelect={onIndexerSelect}
|
||||||
{...indexer}
|
/>
|
||||||
onIndexerSelect={onIndexerSelect}
|
))
|
||||||
/>
|
|
||||||
) :
|
|
||||||
null;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table> :
|
</Table> :
|
||||||
|
Reference in New Issue
Block a user