+
\\^$.|?*+()[{ have special meanings and need escaping with a \\
' }} />
+ {'More details'} {'Here'}
+
+
+ {'Regular expressions can be tested '}
+ Here
+
+
+ }
+
+
+
+ Name
+
+
+
+
+
+ {
+ fields && fields.map((field) => {
+ return (
+
+ );
+ })
+ }
+
+
+
+ Negate
+
+
+
+
+
+
+
+ Required
+
+
+
+
+
+
+
+ {
+ id ?
+ :
+ null
+ }
+
+
+
+
+ Save
+
+
+
+ );
+}
+
+EditSpecificationModalContent.propTypes = {
+ id: PropTypes.number,
+ onDeleteSpecificationPress: PropTypes.func,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default EditSpecificationModalContent;
diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContentConnector.js b/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContentConnector.js
new file mode 100644
index 000000000..8f27b74e0
--- /dev/null
+++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContentConnector.js
@@ -0,0 +1,78 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
+import { clearAutoTaggingSpecificationPending, saveAutoTaggingSpecification, setAutoTaggingSpecificationFieldValue, setAutoTaggingSpecificationValue } from 'Store/Actions/settingsActions';
+import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
+import EditSpecificationModalContent from './EditSpecificationModalContent';
+
+function createMapStateToProps() {
+ return createSelector(
+ (state) => state.settings.advancedSettings,
+ createProviderSettingsSelector('autoTaggingSpecifications'),
+ (advancedSettings, specification) => {
+ return {
+ advancedSettings,
+ ...specification
+ };
+ }
+ );
+}
+
+const mapDispatchToProps = {
+ setAutoTaggingSpecificationValue,
+ setAutoTaggingSpecificationFieldValue,
+ saveAutoTaggingSpecification,
+ clearAutoTaggingSpecificationPending
+};
+
+class EditSpecificationModalContentConnector extends Component {
+
+ //
+ // Listeners
+
+ onInputChange = ({ name, value }) => {
+ this.props.setAutoTaggingSpecificationValue({ name, value });
+ };
+
+ onFieldChange = ({ name, value }) => {
+ this.props.setAutoTaggingSpecificationFieldValue({ name, value });
+ };
+
+ onCancelPress = () => {
+ this.props.clearAutoTaggingSpecificationPending();
+ this.props.onModalClose();
+ };
+
+ onSavePress = () => {
+ this.props.saveAutoTaggingSpecification({ id: this.props.id });
+ this.props.onModalClose();
+ };
+
+ //
+ // Render
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+EditSpecificationModalContentConnector.propTypes = {
+ id: PropTypes.number,
+ item: PropTypes.object.isRequired,
+ setAutoTaggingSpecificationValue: PropTypes.func.isRequired,
+ setAutoTaggingSpecificationFieldValue: PropTypes.func.isRequired,
+ clearAutoTaggingSpecificationPending: PropTypes.func.isRequired,
+ saveAutoTaggingSpecification: PropTypes.func.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default connect(createMapStateToProps, mapDispatchToProps)(EditSpecificationModalContentConnector);
diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.css b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.css
new file mode 100644
index 000000000..e329fc313
--- /dev/null
+++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.css
@@ -0,0 +1,38 @@
+.autoTagging {
+ composes: card from '~Components/Card.css';
+
+ width: 300px;
+}
+
+.nameContainer {
+ display: flex;
+ justify-content: space-between;
+}
+
+.name {
+ @add-mixin truncate;
+
+ margin-bottom: 20px;
+ font-weight: 300;
+ font-size: 24px;
+}
+
+.cloneButton {
+ composes: button from '~Components/Link/IconButton.css';
+
+ height: 36px;
+}
+
+.labels {
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: 5px;
+ pointer-events: all;
+}
+
+.tooltipLabel {
+ composes: label from '~Components/Label.css';
+
+ margin: 0;
+ border: none;
+}
diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.css.d.ts b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.css.d.ts
new file mode 100644
index 000000000..b3229d715
--- /dev/null
+++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.css.d.ts
@@ -0,0 +1,12 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'autoTagging': string;
+ 'cloneButton': string;
+ 'labels': string;
+ 'name': string;
+ 'nameContainer': string;
+ 'tooltipLabel': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js
new file mode 100644
index 000000000..b53bc74b6
--- /dev/null
+++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/Specification.js
@@ -0,0 +1,121 @@
+import PropTypes from 'prop-types';
+import React, { useCallback, useState } from 'react';
+import Card from 'Components/Card';
+import Label from 'Components/Label';
+import IconButton from 'Components/Link/IconButton';
+import ConfirmModal from 'Components/Modal/ConfirmModal';
+import { icons, kinds } from 'Helpers/Props';
+import EditSpecificationModal from './EditSpecificationModal';
+import styles from './Specification.css';
+
+export default function Specification(props) {
+ const {
+ id,
+ implementationName,
+ name,
+ required,
+ negate,
+ onConfirmDeleteSpecification,
+ onCloneSpecificationPress
+ } = props;
+
+ const [isEditModalOpen, setIsEditModalOpen] = useState(false);
+ const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
+
+ const onEditPress = useCallback(() => {
+ setIsEditModalOpen(true);
+ }, [setIsEditModalOpen]);
+
+ const onEditModalClose = useCallback(() => {
+ setIsEditModalOpen(false);
+ }, [setIsEditModalOpen]);
+
+ const onDeletePress = useCallback(() => {
+ setIsEditModalOpen(false);
+ setIsDeleteModalOpen(true);
+ }, [setIsEditModalOpen, setIsDeleteModalOpen]);
+
+ const onDeleteModalClose = useCallback(() => {
+ setIsDeleteModalOpen(false);
+ }, [setIsDeleteModalOpen]);
+
+ const onConfirmDelete = useCallback(() => {
+ onConfirmDeleteSpecification(id);
+ }, [id, onConfirmDeleteSpecification]);
+
+ const onClonePress = useCallback(() => {
+ onCloneSpecificationPress(id);
+ }, [id, onCloneSpecificationPress]);
+
+ return (
+
+
+
+
+
+
+ {
+ negate ?
+ :
+ null
+ }
+
+ {
+ required ?
+ :
+ null
+ }
+
+
+
+
+
+
+ );
+}
+
+Specification.propTypes = {
+ id: PropTypes.number.isRequired,
+ implementation: PropTypes.string.isRequired,
+ implementationName: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ negate: PropTypes.bool.isRequired,
+ required: PropTypes.bool.isRequired,
+ fields: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onConfirmDeleteSpecification: PropTypes.func.isRequired,
+ onCloneSpecificationPress: PropTypes.func.isRequired
+};
diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js
index e946afdf4..3ede0b016 100644
--- a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js
+++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js
@@ -24,6 +24,7 @@ function TagDetailsModalContent(props) {
importLists,
indexers,
downloadClients,
+ autoTags,
onModalClose,
onDeleteTagPress
} = props;
@@ -198,6 +199,22 @@ function TagDetailsModalContent(props) {
:
null
}
+
+ {
+ autoTags.length ?
+
:
+ null
+ }
@@ -233,6 +250,7 @@ TagDetailsModalContent.propTypes = {
importLists: PropTypes.arrayOf(PropTypes.object).isRequired,
indexers: PropTypes.arrayOf(PropTypes.object).isRequired,
downloadClients: PropTypes.arrayOf(PropTypes.object).isRequired,
+ autoTags: PropTypes.arrayOf(PropTypes.object).isRequired,
onModalClose: PropTypes.func.isRequired,
onDeleteTagPress: PropTypes.func.isRequired
};
diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js
index a8d1e8ffc..156e554a1 100644
--- a/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js
+++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js
@@ -85,6 +85,14 @@ function createMatchingDownloadClientsSelector() {
);
}
+function createMatchingAutoTagsSelector() {
+ return createSelector(
+ (state, { autoTagIds }) => autoTagIds,
+ (state) => state.settings.autoTaggings.items,
+ findMatchingItems
+ );
+}
+
function createMapStateToProps() {
return createSelector(
createMatchingMoviesSelector(),
@@ -94,7 +102,8 @@ function createMapStateToProps() {
createMatchingImportListsSelector(),
createMatchingIndexersSelector(),
createMatchingDownloadClientsSelector(),
- (movies, delayProfiles, notifications, restrictions, importLists, indexers, downloadClients) => {
+ createMatchingAutoTagsSelector(),
+ (movies, delayProfiles, notifications, restrictions, importLists, indexers, downloadClients, autoTags) => {
return {
movies,
delayProfiles,
@@ -102,7 +111,8 @@ function createMapStateToProps() {
restrictions,
importLists,
indexers,
- downloadClients
+ downloadClients,
+ autoTags
};
}
);
diff --git a/frontend/src/Settings/Tags/Tag.js b/frontend/src/Settings/Tags/Tag.js
index 1d4f419a0..661e632b9 100644
--- a/frontend/src/Settings/Tags/Tag.js
+++ b/frontend/src/Settings/Tags/Tag.js
@@ -5,6 +5,7 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import TagDetailsModal from './Details/TagDetailsModal';
+import TagInUse from './TagInUse';
import styles from './Tag.css';
class Tag extends Component {
@@ -54,12 +55,13 @@ class Tag extends Component {
const {
label,
delayProfileIds,
+ importListIds,
notificationIds,
restrictionIds,
- importListIds,
- movieIds,
indexerIds,
- downloadClientIds
+ downloadClientIds,
+ autoTagIds,
+ movieIds
} = this.props;
const {
@@ -69,12 +71,13 @@ class Tag extends Component {
const isTagUsed = !!(
delayProfileIds.length ||
+ importListIds.length ||
notificationIds.length ||
restrictionIds.length ||
- importListIds.length ||
- movieIds.length ||
indexerIds.length ||
- downloadClientIds.length
+ downloadClientIds.length ||
+ autoTagIds.length ||
+ movieIds.length
);
return (
@@ -88,59 +91,50 @@ class Tag extends Component {
{
- isTagUsed &&
+ isTagUsed ?
- {
- !!movieIds.length &&
-
- {movieIds.length} movies
-
- }
+
- {
- !!delayProfileIds.length &&
-
- {delayProfileIds.length} delay profile{delayProfileIds.length > 1 && 's'}
-
- }
+
- {
- !!notificationIds.length &&
-
- {notificationIds.length} connection{notificationIds.length > 1 && 's'}
-
- }
+
- {
- !!restrictionIds.length &&
-
- {restrictionIds.length} restriction{restrictionIds.length > 1 && 's'}
-
- }
+
- {
- !!importListIds.length &&
-
- {importListIds.length} list{importListIds.length > 1 && 's'}
-
- }
+
- {
- indexerIds.length ?
-
- {indexerIds.length} indexer{indexerIds.length > 1 && 's'}
-
:
- null
- }
+
- {
- downloadClientIds.length ?
-
- {downloadClientIds.length} download client{indexerIds.length > 1 && 's'}
-
:
- null
- }
-
+