mirror of https://github.com/lidarr/Lidarr
New: Refresh only selected or filtered artists
(cherry picked from commit bf90c3cbddffca4f7ad07c3ae51fa705988cd80b) Closes #3717
This commit is contained in:
parent
0e7b368c3a
commit
cbb3cb78f9
|
@ -0,0 +1,8 @@
|
|||
import { CustomFilter } from './AppState';
|
||||
|
||||
interface ClientSideCollectionAppState {
|
||||
totalItems: number;
|
||||
customFilters: CustomFilter[];
|
||||
}
|
||||
|
||||
export default ClientSideCollectionAppState;
|
|
@ -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<string | null>(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) => {
|
|||
<PageContent>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label={translate('UpdateAll')}
|
||||
iconName={icons.REFRESH}
|
||||
spinningName={icons.REFRESH}
|
||||
isSpinning={isRefreshingArtist}
|
||||
isDisabled={hasNoArtist}
|
||||
onPress={onRefreshArtistPress}
|
||||
<ArtistIndexRefreshArtistsButton
|
||||
isSelectMode={isSelectMode}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useSelect } from 'App/SelectContext';
|
||||
import ArtistAppState, { ArtistIndexAppState } from 'App/State/ArtistAppState';
|
||||
import ClientSideCollectionAppState from 'App/State/ClientSideCollectionAppState';
|
||||
import { REFRESH_ARTIST } from 'Commands/commandNames';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import createArtistClientSideCollectionItemsSelector from 'Store/Selectors/createArtistClientSideCollectionItemsSelector';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
|
||||
interface ArtistIndexRefreshArtistsButtonProps {
|
||||
isSelectMode: boolean;
|
||||
selectedFilterKey: string;
|
||||
}
|
||||
|
||||
function ArtistIndexRefreshArtistsButton(
|
||||
props: ArtistIndexRefreshArtistsButtonProps
|
||||
) {
|
||||
const isRefreshing = useSelector(
|
||||
createCommandExecutingSelector(REFRESH_ARTIST)
|
||||
);
|
||||
const {
|
||||
items,
|
||||
totalItems,
|
||||
}: ArtistAppState & ArtistIndexAppState & ClientSideCollectionAppState =
|
||||
useSelector(createArtistClientSideCollectionItemsSelector('artistIndex'));
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { isSelectMode, selectedFilterKey } = props;
|
||||
const [selectState] = useSelect();
|
||||
const { selectedState } = selectState;
|
||||
|
||||
const selectedArtistIds = useMemo(() => {
|
||||
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 (
|
||||
<PageToolbarButton
|
||||
label={refreshLabel}
|
||||
isSpinning={isRefreshing}
|
||||
isDisabled={!totalItems}
|
||||
iconName={icons.REFRESH}
|
||||
onPress={onPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArtistIndexRefreshArtistsButton;
|
|
@ -119,7 +119,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
GivenAlbumsForRefresh(_albums);
|
||||
AllowArtistUpdate();
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
VerifyEventNotPublished<ArtistUpdatedEvent>();
|
||||
VerifyEventPublished<ArtistRefreshCompleteEvent>();
|
||||
|
@ -140,7 +140,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
GivenAlbumsForRefresh(new List<Album>());
|
||||
AllowArtistUpdate();
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
VerifyEventPublished<ArtistUpdatedEvent>();
|
||||
VerifyEventPublished<ArtistRefreshCompleteEvent>();
|
||||
|
@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
GivenAlbumsForRefresh(_albums);
|
||||
AllowArtistUpdate();
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
Mocker.GetMock<IMonitorNewAlbumService>()
|
||||
.Verify(x => x.ShouldMonitorNewAlbum(newAlbum, _albums, _artist.MonitorNewItems), Times.Once());
|
||||
|
@ -175,7 +175,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
Mocker.GetMock<IArtistService>()
|
||||
.Setup(x => x.DeleteArtist(It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<bool>()));
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
Mocker.GetMock<IArtistService>()
|
||||
.Verify(v => v.UpdateArtist(It.IsAny<Artist>(), It.IsAny<bool>()), Times.Never());
|
||||
|
@ -193,7 +193,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
GivenArtistFiles();
|
||||
GivenAlbumsForRefresh(new List<Album>());
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
Mocker.GetMock<IArtistService>()
|
||||
.Verify(v => v.UpdateArtist(It.IsAny<Artist>(), It.IsAny<bool>()), Times.Never());
|
||||
|
@ -238,7 +238,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
.Setup(x => x.UpdateArtist(It.IsAny<Artist>(), It.IsAny<bool>()))
|
||||
.Returns((Artist a, bool updated) => a);
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
Mocker.GetMock<IArtistService>()
|
||||
.Verify(v => v.UpdateArtist(It.Is<Artist>(s => s.ArtistMetadataId == 100 && s.ForeignArtistId == newArtistInfo.ForeignArtistId), It.IsAny<bool>()),
|
||||
|
@ -298,7 +298,7 @@ namespace NzbDrone.Core.Test.MusicTests
|
|||
.Setup(x => x.UpdateArtist(It.IsAny<Artist>(), It.IsAny<bool>()))
|
||||
.Returns((Artist a, bool updated) => a);
|
||||
|
||||
Subject.Execute(new RefreshArtistCommand(_artist.Id));
|
||||
Subject.Execute(new RefreshArtistCommand(new List<int> { _artist.Id }));
|
||||
|
||||
// the retained artist gets updated
|
||||
Mocker.GetMock<IArtistService>()
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<int> ArtistIds { get; set; }
|
||||
public bool IsNewArtist { get; set; }
|
||||
|
||||
public RefreshArtistCommand()
|
||||
{
|
||||
ArtistIds = new List<int>();
|
||||
}
|
||||
|
||||
public RefreshArtistCommand(int? artistId, bool isNewArtist = false)
|
||||
public RefreshArtistCommand(List<int> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<int> { artist.Id }));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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<int> { message.Artist.Id }, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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<int> { message.Artist.Id }, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<int> { message.ArtistId.Value }, isNew, trigger);
|
||||
RefreshSelectedArtists(message.ArtistIds, isNew, trigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue