diff --git a/frontend/src/Components/Form/FormInputGroup.js b/frontend/src/Components/Form/FormInputGroup.js
index e38d7970e..93d625277 100644
--- a/frontend/src/Components/Form/FormInputGroup.js
+++ b/frontend/src/Components/Form/FormInputGroup.js
@@ -26,6 +26,7 @@ import PathInputConnector from './PathInputConnector';
import QualityProfileSelectInputConnector from './QualityProfileSelectInputConnector';
import RootFolderSelectInputConnector from './RootFolderSelectInputConnector';
import TagInputConnector from './TagInputConnector';
+import TagSelectInputConnector from './TagSelectInputConnector';
import TextArea from './TextArea';
import TextInput from './TextInput';
import TextTagInputConnector from './TextTagInputConnector';
@@ -103,6 +104,9 @@ function getComponent(type) {
case inputTypes.TEXT_TAG:
return TextTagInputConnector;
+ case inputTypes.TAG_SELECT:
+ return TagSelectInputConnector;
+
case inputTypes.UMASK:
return UMaskInput;
diff --git a/frontend/src/Components/Form/ProviderFieldFormGroup.js b/frontend/src/Components/Form/ProviderFieldFormGroup.js
index 043cc432b..9f2967fb5 100644
--- a/frontend/src/Components/Form/ProviderFieldFormGroup.js
+++ b/frontend/src/Components/Form/ProviderFieldFormGroup.js
@@ -31,6 +31,8 @@ function getType({ type, selectOptionsProviderAction }) {
return inputTypes.SELECT;
case 'tag':
return inputTypes.TEXT_TAG;
+ case 'tagSelect':
+ return inputTypes.TAG_SELECT;
case 'textbox':
return inputTypes.TEXT;
case 'oAuth':
diff --git a/frontend/src/Components/Form/TagSelectInputConnector.js b/frontend/src/Components/Form/TagSelectInputConnector.js
new file mode 100644
index 000000000..23afe6da1
--- /dev/null
+++ b/frontend/src/Components/Form/TagSelectInputConnector.js
@@ -0,0 +1,102 @@
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
+import TagInput from './TagInput';
+
+function createMapStateToProps() {
+ return createSelector(
+ (state, { value }) => value,
+ (state, { values }) => values,
+ (tags, tagList) => {
+ const sortedTags = _.sortBy(tagList, 'value');
+
+ return {
+ tags: tags.reduce((acc, tag) => {
+ const matchingTag = _.find(tagList, { key: tag });
+
+ if (matchingTag) {
+ acc.push({
+ id: tag,
+ name: matchingTag.value
+ });
+ }
+
+ return acc;
+ }, []),
+
+ tagList: sortedTags.map(({ key: id, value: name }) => {
+ return {
+ id,
+ name
+ };
+ }),
+
+ allTags: sortedTags
+ };
+ }
+ );
+}
+
+class TagSelectInputConnector extends Component {
+
+ //
+ // Listeners
+
+ onTagAdd = (tag) => {
+ const {
+ name,
+ value,
+ allTags
+ } = this.props;
+
+ const existingTag =_.some(allTags, { key: tag.id });
+
+ const newValue = value.slice();
+
+ if (existingTag) {
+ newValue.push(tag.id);
+ }
+
+ this.props.onChange({ name, value: newValue });
+ };
+
+ onTagDelete = ({ index }) => {
+ const {
+ name,
+ value
+ } = this.props;
+
+ const newValue = value.slice();
+ newValue.splice(index, 1);
+
+ this.props.onChange({
+ name,
+ value: newValue
+ });
+ };
+
+ //
+ // Render
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+TagSelectInputConnector.propTypes = {
+ name: PropTypes.string.isRequired,
+ value: PropTypes.arrayOf(PropTypes.number).isRequired,
+ values: PropTypes.arrayOf(PropTypes.object).isRequired,
+ allTags: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onChange: PropTypes.func.isRequired
+};
+
+export default connect(createMapStateToProps)(TagSelectInputConnector);
diff --git a/frontend/src/Helpers/Props/inputTypes.js b/frontend/src/Helpers/Props/inputTypes.js
index c4cfb7ecf..73ba65ce2 100644
--- a/frontend/src/Helpers/Props/inputTypes.js
+++ b/frontend/src/Helpers/Props/inputTypes.js
@@ -22,6 +22,7 @@ export const TAG = 'tag';
export const TEXT = 'text';
export const TEXT_AREA = 'textArea';
export const TEXT_TAG = 'textTag';
+export const TAG_SELECT = 'tagSelect';
export const UMASK = 'umask';
export const all = [
@@ -49,5 +50,6 @@ export const all = [
TEXT,
TEXT_AREA,
TEXT_TAG,
+ TAG_SELECT,
UMASK
];
diff --git a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs
index 67f78ad87..e15791153 100644
--- a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs
+++ b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs
@@ -65,7 +65,8 @@ public enum FieldType
Captcha,
OAuth,
Device,
- Bookshelf
+ Bookshelf,
+ TagSelect
}
public enum HiddenType
diff --git a/src/Readarr.Http/ClientSchema/SchemaBuilder.cs b/src/Readarr.Http/ClientSchema/SchemaBuilder.cs
index 6a25e8cd8..bfcd9bbb3 100644
--- a/src/Readarr.Http/ClientSchema/SchemaBuilder.cs
+++ b/src/Readarr.Http/ClientSchema/SchemaBuilder.cs
@@ -107,7 +107,7 @@ private static FieldMapping[] GetFieldMapping(Type type, string prefix, Func