New: Bulk Enable/Disable

This commit is contained in:
Qstick
2021-02-22 20:56:36 -05:00
parent a0445e1abe
commit 0f27837246
8 changed files with 107 additions and 13 deletions

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import SelectInput from 'Components/Form/SelectInput';
import SpinnerButton from 'Components/Link/SpinnerButton'; import SpinnerButton from 'Components/Link/SpinnerButton';
import PageContentFooter from 'Components/Page/PageContentFooter'; import PageContentFooter from 'Components/Page/PageContentFooter';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
@@ -20,6 +21,7 @@ class IndexerEditorFooter extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
enable: NO_CHANGE,
savingTags: false, savingTags: false,
isDeleteMovieModalOpen: false, isDeleteMovieModalOpen: false,
isTagsModalOpen: false isTagsModalOpen: false
@@ -34,6 +36,7 @@ class IndexerEditorFooter extends Component {
if (prevProps.isSaving && !isSaving && !saveError) { if (prevProps.isSaving && !isSaving && !saveError) {
this.setState({ this.setState({
enable: NO_CHANGE,
savingTags: false savingTags: false
}); });
} }
@@ -95,13 +98,35 @@ class IndexerEditorFooter extends Component {
} = this.props; } = this.props;
const { const {
enable,
savingTags, savingTags,
isTagsModalOpen, isTagsModalOpen,
isDeleteMovieModalOpen isDeleteMovieModalOpen
} = this.state; } = this.state;
const enableOptions = [
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
{ key: 'true', value: translate('Enabled') },
{ key: 'false', value: translate('Disabled') }
];
return ( return (
<PageContentFooter> <PageContentFooter>
<div className={styles.inputContainer}>
<IndexerEditorFooterLabel
label={translate('EnableIndexer')}
isSaving={isSaving && enable !== NO_CHANGE}
/>
<SelectInput
name="enable"
value={enable}
values={enableOptions}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
<div className={styles.buttonContainer}> <div className={styles.buttonContainer}>
<div className={styles.buttonContainerContent}> <div className={styles.buttonContainerContent}>
<IndexerEditorFooterLabel <IndexerEditorFooterLabel

View File

@@ -246,7 +246,7 @@ class IndexerIndex extends Component {
onSaveSelected = (changes) => { onSaveSelected = (changes) => {
this.props.onSaveSelected({ this.props.onSaveSelected({
movieIds: this.getSelectedIds(), indexerIds: this.getSelectedIds(),
...changes ...changes
}); });
} }
@@ -290,7 +290,7 @@ class IndexerIndex extends Component {
allUnselected allUnselected
} = this.state; } = this.state;
const selectedMovieIds = this.getSelectedIds(); const selectedIndexerIds = this.getSelectedIds();
const ViewComponent = getViewComponent(); const ViewComponent = getViewComponent();
const isLoaded = !!(!error && isPopulated && items.length && scroller); const isLoaded = !!(!error && isPopulated && items.length && scroller);
@@ -448,8 +448,8 @@ class IndexerIndex extends Component {
{ {
isLoaded && isMovieEditorActive && isLoaded && isMovieEditorActive &&
<IndexerEditorFooter <IndexerEditorFooter
indexerIds={selectedMovieIds} indexerIds={selectedIndexerIds}
selectedCount={selectedMovieIds.length} selectedCount={selectedIndexerIds.length}
isSaving={isSaving} isSaving={isSaving}
saveError={saveError} saveError={saveError}
isDeleting={isDeleting} isDeleting={isDeleting}

View File

@@ -182,7 +182,7 @@ export const actionHandlers = handleThunks({
})); }));
const promise = createAjaxRequest({ const promise = createAjaxRequest({
url: '/movie/editor', url: '/indexer/editor',
method: 'PUT', method: 'PUT',
data: JSON.stringify(payload), data: JSON.stringify(payload),
dataType: 'json' dataType: 'json'
@@ -190,11 +190,11 @@ export const actionHandlers = handleThunks({
promise.done((data) => { promise.done((data) => {
dispatch(batchActions([ dispatch(batchActions([
...data.map((movie) => { ...data.map((indexer) => {
return updateItem({ return updateItem({
id: movie.id, id: indexer.id,
section: 'movies', section: 'indexers',
...movie ...indexer
}); });
}), }),

View File

@@ -12,6 +12,7 @@ namespace NzbDrone.Core.ThingiProvider
TProviderDefinition Get(int id); TProviderDefinition Get(int id);
TProviderDefinition Create(TProviderDefinition definition); TProviderDefinition Create(TProviderDefinition definition);
void Update(TProviderDefinition definition); void Update(TProviderDefinition definition);
void Update(IEnumerable<TProviderDefinition> definitions);
void Delete(int id); void Delete(int id);
IEnumerable<TProviderDefinition> GetDefaultDefinitions(); IEnumerable<TProviderDefinition> GetDefaultDefinitions();
IEnumerable<TProviderDefinition> GetPresetDefinitions(TProviderDefinition providerDefinition); IEnumerable<TProviderDefinition> GetPresetDefinitions(TProviderDefinition providerDefinition);

View File

@@ -109,6 +109,11 @@ namespace NzbDrone.Core.ThingiProvider
_eventAggregator.PublishEvent(new ProviderUpdatedEvent<TProvider>(definition)); _eventAggregator.PublishEvent(new ProviderUpdatedEvent<TProvider>(definition));
} }
public virtual void Update(IEnumerable<TProviderDefinition> definitions)
{
_providerRepository.UpdateMany(definitions.ToList());
}
public void Delete(int id) public void Delete(int id)
{ {
_providerRepository.Delete(id); _providerRepository.Delete(id);

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Nancy; using Nancy;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
@@ -7,22 +9,68 @@ namespace Prowlarr.Api.V1.Indexers
{ {
public class IndexerEditorModule : ProwlarrV1Module public class IndexerEditorModule : ProwlarrV1Module
{ {
private readonly IIndexerFactory _movieService; private readonly IIndexerFactory _indexerService;
private readonly IManageCommandQueue _commandQueueManager; private readonly IManageCommandQueue _commandQueueManager;
public static readonly IndexerResourceMapper ResourceMapper = new IndexerResourceMapper();
public IndexerEditorModule(IIndexerFactory movieService, IManageCommandQueue commandQueueManager) public IndexerEditorModule(IIndexerFactory indexerService, IManageCommandQueue commandQueueManager)
: base("/indexer/editor") : base("/indexer/editor")
{ {
_movieService = movieService; _indexerService = indexerService;
_commandQueueManager = commandQueueManager; _commandQueueManager = commandQueueManager;
Put("/", movie => SaveAll());
Delete("/", movie => DeleteIndexers()); Delete("/", movie => DeleteIndexers());
} }
private object SaveAll()
{
var resource = Request.Body.FromJson<IndexerEditorResource>();
var indexersToUpdate = _indexerService.All().Where(x => resource.IndexerIds.Contains(x.Id));
foreach (var indexer in indexersToUpdate)
{
if (resource.Enable.HasValue)
{
indexer.Enable = resource.Enable.Value;
}
if (resource.Tags != null)
{
var newTags = resource.Tags;
var applyTags = resource.ApplyTags;
switch (applyTags)
{
case ApplyTags.Add:
newTags.ForEach(t => indexer.Tags.Add(t));
break;
case ApplyTags.Remove:
newTags.ForEach(t => indexer.Tags.Remove(t));
break;
case ApplyTags.Replace:
indexer.Tags = new HashSet<int>(newTags);
break;
}
}
}
_indexerService.Update(indexersToUpdate);
var indexers = _indexerService.All();
foreach (var definition in indexers)
{
_indexerService.SetProviderCharacteristics(definition);
}
return ResponseWithCode(ResourceMapper.ToResource(indexers), HttpStatusCode.Accepted);
}
private object DeleteIndexers() private object DeleteIndexers()
{ {
var resource = Request.Body.FromJson<IndexerEditorResource>(); var resource = Request.Body.FromJson<IndexerEditorResource>();
_movieService.DeleteIndexers(resource.IndexerIds); _indexerService.DeleteIndexers(resource.IndexerIds);
return new object(); return new object();
} }

View File

@@ -5,5 +5,15 @@ namespace Prowlarr.Api.V1.Indexers
public class IndexerEditorResource public class IndexerEditorResource
{ {
public List<int> IndexerIds { get; set; } public List<int> IndexerIds { get; set; }
public bool? Enable { get; set; }
public List<int> Tags { get; set; }
public ApplyTags ApplyTags { get; set; }
}
public enum ApplyTags
{
Add,
Remove,
Replace
} }
} }

View File

@@ -134,5 +134,10 @@ namespace Prowlarr.Api.V1.Indexers
return field; return field;
} }
public List<IndexerResource> ToResource(IEnumerable<IndexerDefinition> models)
{
return models.Select(ToResource).ToList();
}
} }
} }