diff --git a/frontend/src/Activity/Blacklist/BlacklistConnector.js b/frontend/src/Activity/Blacklist/BlacklistConnector.js index 0528dffaa..1f5b6cd05 100644 --- a/frontend/src/Activity/Blacklist/BlacklistConnector.js +++ b/frontend/src/Activity/Blacklist/BlacklistConnector.js @@ -1,10 +1,9 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import * as blacklistActions from 'Store/Actions/blacklistActions'; import { executeCommand } from 'Store/Actions/commandActions'; import * as commandNames from 'Commands/commandNames'; @@ -13,10 +12,8 @@ import Blacklist from './Blacklist'; function createMapStateToProps() { return createSelector( (state) => state.blacklist, - createCommandsSelector(), - (blacklist, commands) => { - const isClearingBlacklistExecuting = _.some(commands, { name: commandNames.CLEAR_BLACKLIST }); - + createCommandExecutingSelector(commandNames.CLEAR_BLACKLIST), + (blacklist, isClearingBlacklistExecuting) => { return { isClearingBlacklistExecuting, ...blacklist diff --git a/frontend/src/Activity/Queue/QueueConnector.js b/frontend/src/Activity/Queue/QueueConnector.js index d9de161f2..00f57ba30 100644 --- a/frontend/src/Activity/Queue/QueueConnector.js +++ b/frontend/src/Activity/Queue/QueueConnector.js @@ -6,7 +6,7 @@ import { createSelector } from 'reselect'; import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import selectUniqueIds from 'Utilities/Object/selectUniqueIds'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import { executeCommand } from 'Store/Actions/commandActions'; import * as queueActions from 'Store/Actions/queueActions'; import { fetchAlbums, clearAlbums } from 'Store/Actions/albumActions'; @@ -17,10 +17,8 @@ function createMapStateToProps() { return createSelector( (state) => state.albums, (state) => state.queue.paged, - createCommandsSelector(), - (albums, queue, commands) => { - const isCheckForFinishedDownloadExecuting = _.some(commands, { name: commandNames.CHECK_FOR_FINISHED_DOWNLOAD }); - + createCommandExecutingSelector(commandNames.CHECK_FOR_FINISHED_DOWNLOAD), + (albums, queue, isCheckForFinishedDownloadExecuting) => { return { isAlbumsFetching: albums.isFetching, isAlbumsPopulated: albums.isPopulated, diff --git a/frontend/src/Album/AlbumSearchCellConnector.js b/frontend/src/Album/AlbumSearchCellConnector.js index f5f1f7693..2774db752 100644 --- a/frontend/src/Album/AlbumSearchCellConnector.js +++ b/frontend/src/Album/AlbumSearchCellConnector.js @@ -1,6 +1,6 @@ -import _ from 'lodash'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; +import { isCommandExecuting } from 'Utilities/Command'; import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import { executeCommand } from 'Store/Actions/commandActions'; @@ -13,14 +13,17 @@ function createMapStateToProps() { createArtistSelector(), createCommandsSelector(), (albumId, artist, commands) => { - const isSearching = _.some(commands, (command) => { + const isSearching = commands.some((command) => { const albumSearch = command.name === commandNames.ALBUM_SEARCH; if (!albumSearch) { return false; } - return command.body.albumIds.indexOf(albumId) > -1; + return ( + isCommandExecuting(command) && + command.body.albumIds.indexOf(albumId) > -1 + ); }); return { diff --git a/frontend/src/Artist/Details/AlbumRowConnector.js b/frontend/src/Artist/Details/AlbumRowConnector.js index 05ae5042c..1c48af992 100644 --- a/frontend/src/Artist/Details/AlbumRowConnector.js +++ b/frontend/src/Artist/Details/AlbumRowConnector.js @@ -3,17 +3,13 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createTrackFileSelector from 'Store/Selectors/createTrackFileSelector'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import AlbumRow from './AlbumRow'; function createMapStateToProps() { return createSelector( - (state, { id }) => id, - (state, { sceneSeasonNumber }) => sceneSeasonNumber, createArtistSelector(), createTrackFileSelector(), - createCommandsSelector(), - (id, sceneSeasonNumber, artist, trackFile, commands) => { + (artist, trackFile) => { return { foreignArtistId: artist.foreignArtistId, artistMonitored: artist.monitored, diff --git a/frontend/src/Artist/Index/ArtistIndexItemConnector.js b/frontend/src/Artist/Index/ArtistIndexItemConnector.js index afee85424..a7e093733 100644 --- a/frontend/src/Artist/Index/ArtistIndexItemConnector.js +++ b/frontend/src/Artist/Index/ArtistIndexItemConnector.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; +import { isCommandExecuting } from 'Utilities/Command'; import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createQualityProfileSelector from 'Store/Selectors/createQualityProfileSelector'; @@ -20,9 +21,12 @@ function createMapStateToProps() { createMetadataProfileSelector(), createCommandsSelector(), (artist, qualityProfile, languageProfile, metadataProfile, commands) => { - const isRefreshingArtist = _.some(commands, (command) => { - return command.name === commandNames.REFRESH_ARTIST && - command.body.artistId === artist.id; + const isRefreshingArtist = commands.some((command) => { + return ( + command.name === commandNames.REFRESH_ARTIST && + command.body.artistId === artist.id && + isCommandExecuting(command) + ); }); const latestAlbum = _.maxBy(artist.albums, (album) => album.releaseDate); diff --git a/frontend/src/Components/Icon.css b/frontend/src/Components/Icon.css index 4298ea38f..8c6c10d0f 100644 --- a/frontend/src/Components/Icon.css +++ b/frontend/src/Components/Icon.css @@ -6,6 +6,10 @@ color: inherit; } +.info { + color: $infoColor; +} + .success { color: $successColor; } diff --git a/frontend/src/Settings/General/GeneralSettingsConnector.js b/frontend/src/Settings/General/GeneralSettingsConnector.js index 78f3b0943..804fdfde7 100644 --- a/frontend/src/Settings/General/GeneralSettingsConnector.js +++ b/frontend/src/Settings/General/GeneralSettingsConnector.js @@ -1,10 +1,9 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector'; import { setGeneralSettingsValue, saveGeneralSettings, fetchGeneralSettings } from 'Store/Actions/settingsActions'; import { clearPendingChanges } from 'Store/Actions/baseActions'; @@ -19,11 +18,9 @@ function createMapStateToProps() { return createSelector( (state) => state.settings.advancedSettings, createSettingsSectionSelector(SECTION), - createCommandsSelector(), + createCommandExecutingSelector(commandNames.RESET_API_KEY), createSystemStatusSelector(), - (advancedSettings, sectionSettings, commands, systemStatus) => { - const isResettingApiKey = _.some(commands, { name: commandNames.RESET_API_KEY }); - + (advancedSettings, sectionSettings, isResettingApiKey, systemStatus) => { return { advancedSettings, isResettingApiKey, diff --git a/frontend/src/Store/Selectors/createCommandExecutingSelector.js b/frontend/src/Store/Selectors/createCommandExecutingSelector.js index c3beb9a4d..6037d5820 100644 --- a/frontend/src/Store/Selectors/createCommandExecutingSelector.js +++ b/frontend/src/Store/Selectors/createCommandExecutingSelector.js @@ -1,12 +1,11 @@ import { createSelector } from 'reselect'; -import { findCommand, isCommandExecuting } from 'Utilities/Command'; -import createCommandsSelector from './createCommandsSelector'; +import { isCommandExecuting } from 'Utilities/Command'; +import createCommandSelector from './createCommandSelector'; function createCommandExecutingSelector(name, contraints = {}) { return createSelector( - createCommandsSelector(), - (commands) => { - const command = findCommand(commands, { name, ...contraints }); + createCommandSelector(name, contraints), + (command) => { return isCommandExecuting(command); } ); diff --git a/frontend/src/Store/Selectors/createCommandSelector.js b/frontend/src/Store/Selectors/createCommandSelector.js index 5f69ab005..709dfebaf 100644 --- a/frontend/src/Store/Selectors/createCommandSelector.js +++ b/frontend/src/Store/Selectors/createCommandSelector.js @@ -6,7 +6,7 @@ function createCommandSelector(name, contraints = {}) { return createSelector( createCommandsSelector(), (commands) => { - return !!findCommand(commands, { name, ...contraints }); + return findCommand(commands, { name, ...contraints }); } ); } diff --git a/frontend/src/System/Backup/BackupsConnector.js b/frontend/src/System/Backup/BackupsConnector.js index efefa9e70..434354f5b 100644 --- a/frontend/src/System/Backup/BackupsConnector.js +++ b/frontend/src/System/Backup/BackupsConnector.js @@ -1,9 +1,8 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import { fetchBackups, deleteBackup } from 'Store/Actions/systemActions'; import { executeCommand } from 'Store/Actions/commandActions'; import * as commandNames from 'Commands/commandNames'; @@ -12,8 +11,8 @@ import Backups from './Backups'; function createMapStateToProps() { return createSelector( (state) => state.system.backups, - createCommandsSelector(), - (backups, commands) => { + createCommandExecutingSelector(commandNames.BACKUP), + (backups, backupExecuting) => { const { isFetching, isPopulated, @@ -21,8 +20,6 @@ function createMapStateToProps() { items } = backups; - const backupExecuting = _.some(commands, { name: commandNames.BACKUP }); - return { isFetching, isPopulated, diff --git a/frontend/src/System/Events/LogsTableConnector.js b/frontend/src/System/Events/LogsTableConnector.js index 649e33f67..c02d67650 100644 --- a/frontend/src/System/Events/LogsTableConnector.js +++ b/frontend/src/System/Events/LogsTableConnector.js @@ -1,9 +1,8 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import { executeCommand } from 'Store/Actions/commandActions'; import * as systemActions from 'Store/Actions/systemActions'; import * as commandNames from 'Commands/commandNames'; @@ -12,10 +11,8 @@ import LogsTable from './LogsTable'; function createMapStateToProps() { return createSelector( (state) => state.system.logs, - createCommandsSelector(), - (logs, commands) => { - const clearLogExecuting = _.some(commands, { name: commandNames.CLEAR_LOGS }); - + createCommandExecutingSelector(commandNames.CLEAR_LOGS), + (logs, clearLogExecuting) => { return { clearLogExecuting, ...logs diff --git a/frontend/src/System/Logs/Files/LogFilesConnector.js b/frontend/src/System/Logs/Files/LogFilesConnector.js index 16d8d23b9..628bb571c 100644 --- a/frontend/src/System/Logs/Files/LogFilesConnector.js +++ b/frontend/src/System/Logs/Files/LogFilesConnector.js @@ -1,10 +1,9 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import combinePath from 'Utilities/String/combinePath'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import { executeCommand } from 'Store/Actions/commandActions'; import { fetchLogFiles } from 'Store/Actions/systemActions'; import * as commandNames from 'Commands/commandNames'; @@ -14,8 +13,8 @@ function createMapStateToProps() { return createSelector( (state) => state.system.logFiles, (state) => state.system.status.item, - createCommandsSelector(), - (logFiles, status, commands) => { + createCommandExecutingSelector(commandNames.DELETE_LOG_FILES), + (logFiles, status, deleteFilesExecuting) => { const { isFetching, items @@ -26,8 +25,6 @@ function createMapStateToProps() { isWindows } = status; - const deleteFilesExecuting = _.some(commands, { name: commandNames.DELETE_LOG_FILES }); - return { isFetching, items, diff --git a/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js b/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js index d49b82152..3030c12ce 100644 --- a/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js +++ b/frontend/src/System/Logs/Updates/UpdateLogFilesConnector.js @@ -1,10 +1,9 @@ -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import combinePath from 'Utilities/String/combinePath'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import { executeCommand } from 'Store/Actions/commandActions'; import { fetchUpdateLogFiles } from 'Store/Actions/systemActions'; import * as commandNames from 'Commands/commandNames'; @@ -14,15 +13,13 @@ function createMapStateToProps() { return createSelector( (state) => state.system.updateLogFiles, (state) => state.system.status.item, - createCommandsSelector(), - (updateLogFiles, status, commands) => { + createCommandExecutingSelector(commandNames.DELETE_UPDATE_LOG_FILES), + (updateLogFiles, status, deleteFilesExecuting) => { const { isFetching, items } = updateLogFiles; - const deleteFilesExecuting = _.some(commands, { name: commandNames.DELETE_UPDATE_LOG_FILES }); - const { appData, isWindows diff --git a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js index 6e7149b0c..fc32ae629 100644 --- a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js +++ b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js @@ -7,7 +7,7 @@ import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePo import getFilterValue from 'Utilities/Filter/getFilterValue'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import selectUniqueIds from 'Utilities/Object/selectUniqueIds'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import * as wantedActions from 'Store/Actions/wantedActions'; import { executeCommand } from 'Store/Actions/commandActions'; import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions'; @@ -18,9 +18,8 @@ import CutoffUnmet from './CutoffUnmet'; function createMapStateToProps() { return createSelector( (state) => state.wanted.cutoffUnmet, - createCommandsSelector(), - (cutoffUnmet, commands) => { - const isSearchingForCutoffUnmetAlbums = _.some(commands, { name: commandNames.CUTOFF_UNMET_ALBUM_SEARCH }); + createCommandExecutingSelector(commandNames.CUTOFF_UNMET_ALBUM_SEARCH), + (cutoffUnmet, isSearchingForCutoffUnmetAlbums) => { return { isSearchingForCutoffUnmetAlbums, diff --git a/frontend/src/Wanted/Missing/MissingConnector.js b/frontend/src/Wanted/Missing/MissingConnector.js index 05f6128c0..50ee918e1 100644 --- a/frontend/src/Wanted/Missing/MissingConnector.js +++ b/frontend/src/Wanted/Missing/MissingConnector.js @@ -7,7 +7,7 @@ import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePo import getFilterValue from 'Utilities/Filter/getFilterValue'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import selectUniqueIds from 'Utilities/Object/selectUniqueIds'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; import * as wantedActions from 'Store/Actions/wantedActions'; import { executeCommand } from 'Store/Actions/commandActions'; import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions'; @@ -17,9 +17,8 @@ import Missing from './Missing'; function createMapStateToProps() { return createSelector( (state) => state.wanted.missing, - createCommandsSelector(), - (missing, commands) => { - const isSearchingForMissingAlbums = _.some(commands, { name: commandNames.MISSING_ALBUM_SEARCH }); + createCommandExecutingSelector(commandNames.MISSING_ALBUM_SEARCH), + (missing, isSearchingForMissingAlbums) => { return { isSearchingForMissingAlbums, diff --git a/src/Lidarr.Api.V1/Commands/CommandModule.cs b/src/Lidarr.Api.V1/Commands/CommandModule.cs index 7f614f99e..22c794332 100644 --- a/src/Lidarr.Api.V1/Commands/CommandModule.cs +++ b/src/Lidarr.Api.V1/Commands/CommandModule.cs @@ -79,15 +79,9 @@ namespace Lidarr.Api.V1.Commands { _pendingUpdates[message.Command.Id] = message.Command.ToResource(); } - _debouncer.Execute(); - } - if (message.Command.Name == typeof(MessagingCleanupCommand).Name.Replace("Command", "") && - message.Command.Status == CommandStatus.Completed) - { - BroadcastResourceChange(ModelAction.Sync); + _debouncer.Execute(); } - } private void SendUpdates() @@ -100,6 +94,12 @@ namespace Lidarr.Api.V1.Commands foreach (var pendingUpdate in pendingUpdates) { BroadcastResourceChange(ModelAction.Updated, pendingUpdate); + + if (pendingUpdate.Name == typeof(MessagingCleanupCommand).Name.Replace("Command", "") && + pendingUpdate.Status == CommandStatus.Completed) + { + BroadcastResourceChange(ModelAction.Sync); + } } } }