From 27736649c27e1b884508f2d075835254ef8da697 Mon Sep 17 00:00:00 2001 From: Qstick Date: Mon, 8 Oct 2018 20:56:05 -0400 Subject: [PATCH] New: Import List Tags (#505) * New: Import List Tags * New: Show ImportLists where Tag is Used in Tag manager * Fixed: SignalR Errors due to handleTag missing * Fixed: Clarify Lidarr Tags, not to be confused with LastFmTags --- frontend/src/Components/SignalRConnector.js | 17 +++++++++++++++-- .../ImportLists/EditImportListModalContent.js | 13 +++++++++++++ .../Tags/Details/TagDetailsModalContent.js | 17 +++++++++++++++++ .../Details/TagDetailsModalContentConnector.js | 12 +++++++++++- frontend/src/Settings/Tags/Tag.js | 12 ++++++++++++ frontend/src/Settings/Tags/TagsConnector.js | 6 +++++- src/Lidarr.Api.V1/Tags/TagDetailsResource.cs | 2 ++ .../Datastore/Migration/022_import_list_tags.cs | 14 ++++++++++++++ src/NzbDrone.Core/Datastore/TableMapping.cs | 3 +-- .../ImportLists/ImportListSyncService.cs | 1 + src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + src/NzbDrone.Core/Tags/TagDetails.cs | 1 + src/NzbDrone.Core/Tags/TagService.cs | 8 ++++++++ 13 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 src/NzbDrone.Core/Datastore/Migration/022_import_list_tags.cs diff --git a/frontend/src/Components/SignalRConnector.js b/frontend/src/Components/SignalRConnector.js index 15f11c5da..b652a7096 100644 --- a/frontend/src/Components/SignalRConnector.js +++ b/frontend/src/Components/SignalRConnector.js @@ -11,6 +11,7 @@ import { setAppValue, setVersion } from 'Store/Actions/appActions'; import { update, updateItem, removeItem } from 'Store/Actions/baseActions'; import { fetchHealth } from 'Store/Actions/systemActions'; import { fetchQueue, fetchQueueDetails } from 'Store/Actions/queueActions'; +import { fetchTags, fetchTagDetails } from 'Store/Actions/tagActions'; function getState(status) { switch (status) { @@ -68,7 +69,9 @@ const mapDispatchToProps = { dispatchRemoveItem: removeItem, dispatchFetchHealth: fetchHealth, dispatchFetchQueue: fetchQueue, - dispatchFetchQueueDetails: fetchQueueDetails + dispatchFetchQueueDetails: fetchQueueDetails, + dispatchFetchTags: fetchTags, + dispatchFetchTagDetails: fetchTagDetails }; class SignalRConnector extends Component { @@ -263,6 +266,14 @@ class SignalRConnector extends Component { // No-op for now, we may want this later } + handleTag = (body) => { + if (body.action === 'sync') { + this.props.dispatchFetchTags(); + this.props.dispatchFetchTagDetails(); + return; + } + } + // // Listeners @@ -362,7 +373,9 @@ SignalRConnector.propTypes = { dispatchRemoveItem: PropTypes.func.isRequired, dispatchFetchHealth: PropTypes.func.isRequired, dispatchFetchQueue: PropTypes.func.isRequired, - dispatchFetchQueueDetails: PropTypes.func.isRequired + dispatchFetchQueueDetails: PropTypes.func.isRequired, + dispatchFetchTags: PropTypes.func.isRequired, + dispatchFetchTagDetails: PropTypes.func.isRequired }; export default connect(createMapStateToProps, mapDispatchToProps)(SignalRConnector); diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js index 83a586c0e..2c6573c9e 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js @@ -44,6 +44,7 @@ function EditImportListModalContent(props) { qualityProfileId, languageProfileId, metadataProfileId, + tags, fields } = item; @@ -152,6 +153,18 @@ function EditImportListModalContent(props) { /> + + Lidarr Tags + + + + { !!fields && !!fields.length &&
diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js index ea6fe8a5e..3d6ec10a2 100644 --- a/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js +++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContent.js @@ -17,6 +17,7 @@ function TagDetailsModalContent(props) { isTagUsed, artist, delayProfiles, + importLists, notifications, restrictions, onModalClose, @@ -80,6 +81,21 @@ function TagDetailsModalContent(props) { } + { + !!importLists.length && +
+ { + importLists.map((item) => { + return ( +
+ {item.name} +
+ ); + }) + } +
+ } + { !!restrictions.length &&
@@ -154,6 +170,7 @@ TagDetailsModalContent.propTypes = { isTagUsed: PropTypes.bool.isRequired, artist: PropTypes.arrayOf(PropTypes.object).isRequired, delayProfiles: PropTypes.arrayOf(PropTypes.object).isRequired, + importLists: PropTypes.arrayOf(PropTypes.object).isRequired, notifications: PropTypes.arrayOf(PropTypes.object).isRequired, restrictions: PropTypes.arrayOf(PropTypes.object).isRequired, onModalClose: PropTypes.func.isRequired, diff --git a/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js b/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js index ead36b433..7d214568f 100644 --- a/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js +++ b/frontend/src/Settings/Tags/Details/TagDetailsModalContentConnector.js @@ -25,6 +25,14 @@ function createMatchingDelayProfilesSelector() { ); } +function createMatchingImportListsSelector() { + return createSelector( + (state, { importListIds }) => importListIds, + (state) => state.settings.importLists.items, + findMatchingItems + ); +} + function createMatchingNotificationsSelector() { return createSelector( (state, { notificationIds }) => notificationIds, @@ -45,12 +53,14 @@ function createMapStateToProps() { return createSelector( createMatchingArtistSelector(), createMatchingDelayProfilesSelector(), + createMatchingImportListsSelector(), createMatchingNotificationsSelector(), createMatchingRestrictionsSelector(), - (artist, delayProfiles, notifications, restrictions) => { + (artist, delayProfiles, importLists, notifications, restrictions) => { return { artist, delayProfiles, + importLists, notifications, restrictions }; diff --git a/frontend/src/Settings/Tags/Tag.js b/frontend/src/Settings/Tags/Tag.js index ebe5460eb..b2aceb47e 100644 --- a/frontend/src/Settings/Tags/Tag.js +++ b/frontend/src/Settings/Tags/Tag.js @@ -53,6 +53,7 @@ class Tag extends Component { const { label, delayProfileIds, + importListIds, notificationIds, restrictionIds, artistIds @@ -65,6 +66,7 @@ class Tag extends Component { const isTagUsed = !!( delayProfileIds.length || + importListIds.length || notificationIds.length || restrictionIds.length || artistIds.length @@ -97,6 +99,13 @@ class Tag extends Component {
} + { + !!importListIds.length && +
+ {importListIds.length} import list{importListIds.length > 1 && 's'} +
+ } + { !!notificationIds.length &&
@@ -125,6 +134,7 @@ class Tag extends Component { isTagUsed={isTagUsed} artistIds={artistIds} delayProfileIds={delayProfileIds} + importListIds={importListIds} notificationIds={notificationIds} restrictionIds={restrictionIds} isOpen={isDetailsModalOpen} @@ -150,6 +160,7 @@ Tag.propTypes = { id: PropTypes.number.isRequired, label: PropTypes.string.isRequired, delayProfileIds: PropTypes.arrayOf(PropTypes.number).isRequired, + importListIds: PropTypes.arrayOf(PropTypes.number).isRequired, notificationIds: PropTypes.arrayOf(PropTypes.number).isRequired, restrictionIds: PropTypes.arrayOf(PropTypes.number).isRequired, artistIds: PropTypes.arrayOf(PropTypes.number).isRequired, @@ -158,6 +169,7 @@ Tag.propTypes = { Tag.defaultProps = { delayProfileIds: [], + importListIds: [], notificationIds: [], restrictionIds: [], artistIds: [] diff --git a/frontend/src/Settings/Tags/TagsConnector.js b/frontend/src/Settings/Tags/TagsConnector.js index dccf9b43d..46946e1f5 100644 --- a/frontend/src/Settings/Tags/TagsConnector.js +++ b/frontend/src/Settings/Tags/TagsConnector.js @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { fetchTagDetails } from 'Store/Actions/tagActions'; -import { fetchDelayProfiles, fetchNotifications, fetchRestrictions } from 'Store/Actions/settingsActions'; +import { fetchDelayProfiles, fetchNotifications, fetchRestrictions, fetchImportLists } from 'Store/Actions/settingsActions'; import Tags from './Tags'; function createMapStateToProps() { @@ -27,6 +27,7 @@ function createMapStateToProps() { const mapDispatchToProps = { dispatchFetchTagDetails: fetchTagDetails, dispatchFetchDelayProfiles: fetchDelayProfiles, + dispatchFetchImportLists: fetchImportLists, dispatchFetchNotifications: fetchNotifications, dispatchFetchRestrictions: fetchRestrictions }; @@ -40,12 +41,14 @@ class MetadatasConnector extends Component { const { dispatchFetchTagDetails, dispatchFetchDelayProfiles, + dispatchFetchImportLists, dispatchFetchNotifications, dispatchFetchRestrictions } = this.props; dispatchFetchTagDetails(); dispatchFetchDelayProfiles(); + dispatchFetchImportLists(); dispatchFetchNotifications(); dispatchFetchRestrictions(); } @@ -65,6 +68,7 @@ class MetadatasConnector extends Component { MetadatasConnector.propTypes = { dispatchFetchTagDetails: PropTypes.func.isRequired, dispatchFetchDelayProfiles: PropTypes.func.isRequired, + dispatchFetchImportLists: PropTypes.func.isRequired, dispatchFetchNotifications: PropTypes.func.isRequired, dispatchFetchRestrictions: PropTypes.func.isRequired }; diff --git a/src/Lidarr.Api.V1/Tags/TagDetailsResource.cs b/src/Lidarr.Api.V1/Tags/TagDetailsResource.cs index a27420526..4bd6b6619 100644 --- a/src/Lidarr.Api.V1/Tags/TagDetailsResource.cs +++ b/src/Lidarr.Api.V1/Tags/TagDetailsResource.cs @@ -9,6 +9,7 @@ public class TagDetailsResource : RestResource { public string Label { get; set; } public List DelayProfileIds { get; set; } + public List ImportListIds { get; set; } public List NotificationIds { get; set; } public List RestrictionIds { get; set; } public List ArtistIds { get; set; } @@ -25,6 +26,7 @@ public static TagDetailsResource ToResource(this TagDetails model) Id = model.Id, Label = model.Label, DelayProfileIds = model.DelayProfileIds, + ImportListIds = model.ImportListIds, NotificationIds = model.NotificationIds, RestrictionIds = model.RestrictionIds, ArtistIds = model.ArtistIds diff --git a/src/NzbDrone.Core/Datastore/Migration/022_import_list_tags.cs b/src/NzbDrone.Core/Datastore/Migration/022_import_list_tags.cs new file mode 100644 index 000000000..85739cbd2 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/022_import_list_tags.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(22)] + public class import_list_tags : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("ImportLists").AddColumn("Tags").AsString().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index d10f04896..361c62967 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -66,8 +66,7 @@ public static void Map() .Ignore(d => d.Tags); Mapper.Entity().RegisterDefinition("ImportLists") - .Ignore(i => i.Enable) - .Ignore(d => d.Tags); + .Ignore(i => i.Enable); Mapper.Entity().RegisterDefinition("Notifications") .Ignore(i => i.SupportsOnGrab) diff --git a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs index 65211ec15..fa2234be8 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs @@ -125,6 +125,7 @@ private List ProcessReports(List reports) ProfileId = importList.ProfileId, LanguageProfileId = importList.LanguageProfileId, MetadataProfileId = importList.MetadataProfileId, + Tags = importList.Tags, AlbumFolder = true, AddOptions = new AddArtistOptions { SearchForMissingAlbums = true, Monitored = importList.ShouldMonitor, SelectedOption = 0 } }); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 3d951e32f..a88368760 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -196,6 +196,7 @@ + diff --git a/src/NzbDrone.Core/Tags/TagDetails.cs b/src/NzbDrone.Core/Tags/TagDetails.cs index e35bb44e9..ce433d1c5 100644 --- a/src/NzbDrone.Core/Tags/TagDetails.cs +++ b/src/NzbDrone.Core/Tags/TagDetails.cs @@ -10,5 +10,6 @@ public class TagDetails : ModelBase public List NotificationIds { get; set; } public List RestrictionIds { get; set; } public List DelayProfileIds { get; set; } + public List ImportListIds { get; set; } } } diff --git a/src/NzbDrone.Core/Tags/TagService.cs b/src/NzbDrone.Core/Tags/TagService.cs index f5a52977c..ff18502a2 100644 --- a/src/NzbDrone.Core/Tags/TagService.cs +++ b/src/NzbDrone.Core/Tags/TagService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using NzbDrone.Core.ImportLists; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Notifications; using NzbDrone.Core.Profiles.Delay; @@ -25,6 +26,7 @@ public class TagService : ITagService private readonly ITagRepository _repo; private readonly IEventAggregator _eventAggregator; private readonly IDelayProfileService _delayProfileService; + private readonly IImportListFactory _importListFactory; private readonly INotificationFactory _notificationFactory; private readonly IRestrictionService _restrictionService; private readonly IArtistService _artistService; @@ -32,6 +34,7 @@ public class TagService : ITagService public TagService(ITagRepository repo, IEventAggregator eventAggregator, IDelayProfileService delayProfileService, + ImportListFactory importListFactory, INotificationFactory notificationFactory, IRestrictionService restrictionService, IArtistService artistService) @@ -39,6 +42,7 @@ public TagService(ITagRepository repo, _repo = repo; _eventAggregator = eventAggregator; _delayProfileService = delayProfileService; + _importListFactory = importListFactory; _notificationFactory = notificationFactory; _restrictionService = restrictionService; _artistService = artistService; @@ -65,6 +69,7 @@ public TagDetails Details(int tagId) { var tag = GetTag(tagId); var delayProfiles = _delayProfileService.AllForTag(tagId); + var importLists = _importListFactory.AllForTag(tagId); var notifications = _notificationFactory.AllForTag(tagId); var restrictions = _restrictionService.AllForTag(tagId); var artist = _artistService.AllForTag(tagId); @@ -74,6 +79,7 @@ public TagDetails Details(int tagId) Id = tagId, Label = tag.Label, DelayProfileIds = delayProfiles.Select(c => c.Id).ToList(), + ImportListIds = importLists.Select(c => c.Id).ToList(), NotificationIds = notifications.Select(c => c.Id).ToList(), RestrictionIds = restrictions.Select(c => c.Id).ToList(), ArtistIds = artist.Select(c => c.Id).ToList() @@ -84,6 +90,7 @@ public List Details() { var tags = All(); var delayProfiles = _delayProfileService.All(); + var importLists = _importListFactory.All(); var notifications = _notificationFactory.All(); var restrictions = _restrictionService.All(); var artists = _artistService.GetAllArtists(); @@ -97,6 +104,7 @@ public List Details() Id = tag.Id, Label = tag.Label, DelayProfileIds = delayProfiles.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), + ImportListIds = importLists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), RestrictionIds = restrictions.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), ArtistIds = artists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList()