From 20ef22be94f4bdb5633ddfb080e91c8d5b0229da Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 27 Oct 2024 00:17:46 +0300 Subject: [PATCH] New: Real time UI updates for provider changes --- frontend/src/Components/SignalRConnector.js | 50 ++++++++++++++++++- .../DownloadClientController.cs | 5 +- .../ImportLists/ImportListController.cs | 8 ++- .../Indexers/IndexerController.cs | 7 ++- .../Metadata/MetadataController.cs | 5 +- .../Notifications/NotificationController.cs | 5 +- src/Sonarr.Api.V3/ProviderControllerBase.cs | 31 +++++++++++- 7 files changed, 98 insertions(+), 13 deletions(-) diff --git a/frontend/src/Components/SignalRConnector.js b/frontend/src/Components/SignalRConnector.js index 918f53fa5..e59669883 100644 --- a/frontend/src/Components/SignalRConnector.js +++ b/frontend/src/Components/SignalRConnector.js @@ -168,7 +168,7 @@ class SignalRConnector extends Component { const status = resource.status; // Both successful and failed commands need to be - // completed, otherwise they spin until they timeout. + // completed, otherwise they spin until they time out. if (status === 'completed' || status === 'failed') { this.props.dispatchFinishCommand(resource); @@ -202,10 +202,58 @@ class SignalRConnector extends Component { } }; + handleDownloadclient = ({ action, resource }) => { + const section = 'settings.downloadClients'; + + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } else if (action === 'deleted') { + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + handleHealth = () => { this.props.dispatchFetchHealth(); }; + handleImportlist = ({ action, resource }) => { + const section = 'settings.importLists'; + + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } else if (action === 'deleted') { + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + + handleIndexer = ({ action, resource }) => { + const section = 'settings.indexers'; + + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } else if (action === 'deleted') { + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + + handleMetadata = ({ action, resource }) => { + const section = 'settings.metadata'; + + if (action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } + }; + + handleNotification = ({ action, resource }) => { + const section = 'settings.notifications'; + + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } else if (action === 'deleted') { + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + handleSeries = (body) => { const action = body.action; const section = 'series'; diff --git a/src/Sonarr.Api.V3/DownloadClient/DownloadClientController.cs b/src/Sonarr.Api.V3/DownloadClient/DownloadClientController.cs index 0f79a83c6..1c0c53605 100644 --- a/src/Sonarr.Api.V3/DownloadClient/DownloadClientController.cs +++ b/src/Sonarr.Api.V3/DownloadClient/DownloadClientController.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.Download; +using NzbDrone.SignalR; using Sonarr.Http; namespace Sonarr.Api.V3.DownloadClient @@ -9,8 +10,8 @@ public class DownloadClientController : ProviderControllerBase c.RootFolderPath).Cascade(CascadeMode.Stop) .IsValidPath() diff --git a/src/Sonarr.Api.V3/Indexers/IndexerController.cs b/src/Sonarr.Api.V3/Indexers/IndexerController.cs index fffbc4d96..90d4d6b60 100644 --- a/src/Sonarr.Api.V3/Indexers/IndexerController.cs +++ b/src/Sonarr.Api.V3/Indexers/IndexerController.cs @@ -1,5 +1,6 @@ using NzbDrone.Core.Indexers; using NzbDrone.Core.Validation; +using NzbDrone.SignalR; using Sonarr.Http; namespace Sonarr.Api.V3.Indexers @@ -10,8 +11,10 @@ public class IndexerController : ProviderControllerBase c.DownloadClientId).SetValidator(downloadClientExistsValidator); } diff --git a/src/Sonarr.Api.V3/Metadata/MetadataController.cs b/src/Sonarr.Api.V3/Metadata/MetadataController.cs index 006cab8ba..08f248284 100644 --- a/src/Sonarr.Api.V3/Metadata/MetadataController.cs +++ b/src/Sonarr.Api.V3/Metadata/MetadataController.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Extras.Metadata; +using NzbDrone.SignalR; using Sonarr.Http; namespace Sonarr.Api.V3.Metadata @@ -11,8 +12,8 @@ public class MetadataController : ProviderControllerBase : RestController + public abstract class ProviderControllerBase : RestControllerWithSignalR, + IHandle>, + IHandle>, + IHandle> where TProviderDefinition : ProviderDefinition, new() where TProvider : IProvider where TProviderResource : ProviderResource, new() @@ -22,11 +29,13 @@ public abstract class ProviderControllerBase _resourceMapper; private readonly ProviderBulkResourceMapper _bulkResourceMapper; - protected ProviderControllerBase(IProviderFactory providerFactory, string resource, ProviderResourceMapper resourceMapper, ProviderBulkResourceMapper bulkResourceMapper) + : base(signalRBroadcaster) { _providerFactory = providerFactory; _resourceMapper = resourceMapper; @@ -263,6 +272,24 @@ public IActionResult RequestAction([FromRoute] string name, [FromBody] TProvider return Content(data.ToJson(), "application/json"); } + [NonAction] + public virtual void Handle(ProviderAddedEvent message) + { + BroadcastResourceChange(ModelAction.Created, message.Definition.Id); + } + + [NonAction] + public virtual void Handle(ProviderUpdatedEvent message) + { + BroadcastResourceChange(ModelAction.Updated, message.Definition.Id); + } + + [NonAction] + public virtual void Handle(ProviderDeletedEvent message) + { + BroadcastResourceChange(ModelAction.Deleted, message.ProviderId); + } + private void Validate(TProviderDefinition definition, bool includeWarnings) { var validationResult = definition.Settings.Validate();