diff --git a/frontend/src/App/State/ClientSideCollectionAppState.ts b/frontend/src/App/State/ClientSideCollectionAppState.ts new file mode 100644 index 000000000..f4110ef73 --- /dev/null +++ b/frontend/src/App/State/ClientSideCollectionAppState.ts @@ -0,0 +1,8 @@ +import { CustomFilter } from './AppState'; + +interface ClientSideCollectionAppState { + totalItems: number; + customFilters: CustomFilter[]; +} + +export default ClientSideCollectionAppState; diff --git a/frontend/src/Artist/Index/ArtistIndex.tsx b/frontend/src/Artist/Index/ArtistIndex.tsx index c58339d99..a5c7b8d48 100644 --- a/frontend/src/Artist/Index/ArtistIndex.tsx +++ b/frontend/src/Artist/Index/ArtistIndex.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { SelectProvider } from 'App/SelectContext'; import NoArtist from 'Artist/NoArtist'; -import { REFRESH_ARTIST, RSS_SYNC } from 'Commands/commandNames'; +import { RSS_SYNC } from 'Commands/commandNames'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; @@ -29,6 +29,7 @@ import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import getErrorMessage from 'Utilities/Object/getErrorMessage'; import translate from 'Utilities/String/translate'; import ArtistIndexFooter from './ArtistIndexFooter'; +import ArtistIndexRefreshArtistsButton from './ArtistIndexRefreshArtistsButton'; import ArtistIndexBanners from './Banners/ArtistIndexBanners'; import ArtistIndexBannerOptionsModal from './Banners/Options/ArtistIndexBannerOptionsModal'; import ArtistIndexFilterMenu from './Menus/ArtistIndexFilterMenu'; @@ -83,9 +84,6 @@ const ArtistIndex = withScrollPosition((props: ArtistIndexProps) => { view, } = useSelector(createArtistClientSideCollectionItemsSelector('artistIndex')); - const isRefreshingArtist = useSelector( - createCommandExecutingSelector(REFRESH_ARTIST) - ); const isRssSyncExecuting = useSelector( createCommandExecutingSelector(RSS_SYNC) ); @@ -96,14 +94,6 @@ const ArtistIndex = withScrollPosition((props: ArtistIndexProps) => { const [jumpToCharacter, setJumpToCharacter] = useState(null); const [isSelectMode, setIsSelectMode] = useState(false); - const onRefreshArtistPress = useCallback(() => { - dispatch( - executeCommand({ - name: REFRESH_ARTIST, - }) - ); - }, [dispatch]); - const onRssSyncPress = useCallback(() => { dispatch( executeCommand({ @@ -217,13 +207,9 @@ const ArtistIndex = withScrollPosition((props: ArtistIndexProps) => { - { + return getSelectedIds(selectedState); + }, [selectedState]); + + const artistsToRefresh = + isSelectMode && selectedArtistIds.length > 0 + ? selectedArtistIds + : items.map((m) => m.id); + + let refreshLabel = translate('UpdateAll'); + + if (selectedArtistIds.length > 0) { + refreshLabel = translate('UpdateSelected'); + } else if (selectedFilterKey !== 'all') { + refreshLabel = translate('UpdateFiltered'); + } + + const onPress = useCallback(() => { + dispatch( + executeCommand({ + name: REFRESH_ARTIST, + artistIds: artistsToRefresh, + }) + ); + }, [dispatch, artistsToRefresh]); + + return ( + + ); +} + +export default ArtistIndexRefreshArtistsButton; diff --git a/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs index aacbde747..b89f4910e 100644 --- a/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs @@ -119,7 +119,7 @@ namespace NzbDrone.Core.Test.MusicTests GivenAlbumsForRefresh(_albums); AllowArtistUpdate(); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); VerifyEventNotPublished(); VerifyEventPublished(); @@ -140,7 +140,7 @@ namespace NzbDrone.Core.Test.MusicTests GivenAlbumsForRefresh(new List()); AllowArtistUpdate(); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); VerifyEventPublished(); VerifyEventPublished(); @@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.MusicTests GivenAlbumsForRefresh(_albums); AllowArtistUpdate(); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); Mocker.GetMock() .Verify(x => x.ShouldMonitorNewAlbum(newAlbum, _albums, _artist.MonitorNewItems), Times.Once()); @@ -175,7 +175,7 @@ namespace NzbDrone.Core.Test.MusicTests Mocker.GetMock() .Setup(x => x.DeleteArtist(It.IsAny(), It.IsAny(), It.IsAny())); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); Mocker.GetMock() .Verify(v => v.UpdateArtist(It.IsAny(), It.IsAny()), Times.Never()); @@ -193,7 +193,7 @@ namespace NzbDrone.Core.Test.MusicTests GivenArtistFiles(); GivenAlbumsForRefresh(new List()); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); Mocker.GetMock() .Verify(v => v.UpdateArtist(It.IsAny(), It.IsAny()), Times.Never()); @@ -238,7 +238,7 @@ namespace NzbDrone.Core.Test.MusicTests .Setup(x => x.UpdateArtist(It.IsAny(), It.IsAny())) .Returns((Artist a, bool updated) => a); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); Mocker.GetMock() .Verify(v => v.UpdateArtist(It.Is(s => s.ArtistMetadataId == 100 && s.ForeignArtistId == newArtistInfo.ForeignArtistId), It.IsAny()), @@ -298,7 +298,7 @@ namespace NzbDrone.Core.Test.MusicTests .Setup(x => x.UpdateArtist(It.IsAny(), It.IsAny())) .Returns((Artist a, bool updated) => a); - Subject.Execute(new RefreshArtistCommand(_artist.Id)); + Subject.Execute(new RefreshArtistCommand(new List { _artist.Id })); // the retained artist gets updated Mocker.GetMock() diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 312b22db2..2f13bafaa 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -1089,12 +1089,13 @@ "Unmonitored": "Unmonitored", "UnmonitoredHelpText": "Include unmonitored albums in the iCal feed", "UnmonitoredOnly": "Unmonitored Only", - "UpdateAll": "Update all", + "UpdateAll": "Update All", "UpdateAutomaticallyHelpText": "Automatically download and install updates. You will still be able to install from System: Updates", "UpdateAvailable": "New update is available", "UpdateCheckStartupNotWritableMessage": "Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", "UpdateCheckStartupTranslocationMessage": "Cannot install update because startup folder '{0}' is in an App Translocation folder.", "UpdateCheckUINotWritableMessage": "Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", + "UpdateFiltered": "Update Filtered", "UpdateMechanismHelpText": "Use Lidarr's built-in updater or a script", "UpdateMonitoring": "Update Monitoring", "UpdateScriptPathHelpText": "Path to a custom script that takes an extracted update package and handle the remainder of the update process", diff --git a/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs b/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs index abd1dacd9..8a6cd60f2 100644 --- a/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs +++ b/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs @@ -1,24 +1,41 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Music.Commands { public class RefreshArtistCommand : Command { - public int? ArtistId { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int ArtistId + { + get => 0; + set + { + if (ArtistIds.Empty()) + { + ArtistIds.Add(value); + } + } + } + + public List ArtistIds { get; set; } public bool IsNewArtist { get; set; } public RefreshArtistCommand() { + ArtistIds = new List(); } - public RefreshArtistCommand(int? artistId, bool isNewArtist = false) + public RefreshArtistCommand(List artistIds, bool isNewArtist = false) { - ArtistId = artistId; + ArtistIds = artistIds; IsNewArtist = isNewArtist; } public override bool SendUpdatesToClient => true; - public override bool UpdateScheduledTask => !ArtistId.HasValue; + public override bool UpdateScheduledTask => ArtistIds.Empty(); } } diff --git a/src/NzbDrone.Core/Music/Handlers/AlbumAddedHandler.cs b/src/NzbDrone.Core/Music/Handlers/AlbumAddedHandler.cs index 4b95ffa5a..95c1fa4d6 100644 --- a/src/NzbDrone.Core/Music/Handlers/AlbumAddedHandler.cs +++ b/src/NzbDrone.Core/Music/Handlers/AlbumAddedHandler.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Music.Commands; @@ -25,7 +26,7 @@ namespace NzbDrone.Core.Music if (_checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)) { - _commandQueueManager.Push(new RefreshArtistCommand(artist.Id)); + _commandQueueManager.Push(new RefreshArtistCommand(new List { artist.Id })); } else { diff --git a/src/NzbDrone.Core/Music/Handlers/ArtistAddedHandler.cs b/src/NzbDrone.Core/Music/Handlers/ArtistAddedHandler.cs index 82397793c..bb46f9cf1 100644 --- a/src/NzbDrone.Core/Music/Handlers/ArtistAddedHandler.cs +++ b/src/NzbDrone.Core/Music/Handlers/ArtistAddedHandler.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; @@ -22,7 +23,7 @@ namespace NzbDrone.Core.Music { if (message.DoRefresh) { - _commandQueueManager.Push(new RefreshArtistCommand(message.Artist.Id, true)); + _commandQueueManager.Push(new RefreshArtistCommand(new List { message.Artist.Id }, true)); } else { diff --git a/src/NzbDrone.Core/Music/Services/ArtistEditedService.cs b/src/NzbDrone.Core/Music/Services/ArtistEditedService.cs index a01e2cfe8..c053b7af1 100644 --- a/src/NzbDrone.Core/Music/Services/ArtistEditedService.cs +++ b/src/NzbDrone.Core/Music/Services/ArtistEditedService.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Music.Commands; @@ -19,7 +20,7 @@ namespace NzbDrone.Core.Music // Refresh Artist is we change AlbumType Preferences if (message.Artist.MetadataProfileId != message.OldArtist.MetadataProfileId) { - _commandQueueManager.Push(new RefreshArtistCommand(message.Artist.Id, false)); + _commandQueueManager.Push(new RefreshArtistCommand(new List { message.Artist.Id }, false)); } } } diff --git a/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs b/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs index 433a17bae..e30bc96ae 100644 --- a/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs +++ b/src/NzbDrone.Core/Music/Services/RefreshArtistService.cs @@ -352,9 +352,9 @@ namespace NzbDrone.Core.Music var trigger = message.Trigger; var isNew = message.IsNewArtist; - if (message.ArtistId.HasValue) + if (message.ArtistIds.Any()) { - RefreshSelectedArtists(new List { message.ArtistId.Value }, isNew, trigger); + RefreshSelectedArtists(message.ArtistIds, isNew, trigger); } else {