From 8cb59c35fbc76db1b3b9f713041e77c512b03c8a Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sat, 12 Oct 2024 01:43:51 +0300 Subject: [PATCH] New: Sync UI updates for providers --- frontend/src/Components/SignalRConnector.js | 53 ++++++++++++++++--- .../Applications/ApplicationController.cs | 5 +- .../DownloadClientController.cs | 5 +- .../IndexerProxies/IndexerProxyController.cs | 5 +- .../Indexers/IndexerController.cs | 6 ++- .../Notifications/NotificationController.cs | 5 +- src/Prowlarr.Api.V1/ProviderControllerBase.cs | 31 ++++++++++- 7 files changed, 91 insertions(+), 19 deletions(-) diff --git a/frontend/src/Components/SignalRConnector.js b/frontend/src/Components/SignalRConnector.js index 28c12df12..d39c05e10 100644 --- a/frontend/src/Components/SignalRConnector.js +++ b/frontend/src/Components/SignalRConnector.js @@ -141,6 +141,16 @@ class SignalRConnector extends Component { console.error(`signalR: Unable to find handler for ${name}`); }; + handleApplications = ({ action, resource }) => { + const section = 'settings.applications'; + + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } else if (action === 'deleted') { + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + handleCommand = (body) => { if (body.action === 'sync') { this.props.dispatchFetchCommands(); @@ -150,8 +160,8 @@ class SignalRConnector extends Component { const resource = body.resource; const status = resource.status; - // Both sucessful and failed commands need to be - // completed, otherwise they spin until they timeout. + // Both successful and failed commands need to be + // completed, otherwise they spin until they time out. if (status === 'completed' || status === 'failed') { this.props.dispatchFinishCommand(resource); @@ -160,6 +170,16 @@ 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(); }; @@ -168,14 +188,33 @@ class SignalRConnector extends Component { this.props.dispatchFetchIndexerStatus(); }; - handleIndexer = (body) => { - const action = body.action; + handleIndexer = ({ action, resource }) => { const section = 'indexers'; - if (action === 'updated') { - this.props.dispatchUpdateItem({ section, ...body.resource }); + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); } else if (action === 'deleted') { - this.props.dispatchRemoveItem({ section, id: body.resource.id }); + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + + handleIndexerproxy = ({ action, resource }) => { + const section = 'settings.indexerProxies'; + + if (action === 'created' || action === 'updated') { + this.props.dispatchUpdateItem({ section, ...resource }); + } else if (action === 'deleted') { + this.props.dispatchRemoveItem({ section, id: resource.id }); + } + }; + + 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 }); } }; diff --git a/src/Prowlarr.Api.V1/Applications/ApplicationController.cs b/src/Prowlarr.Api.V1/Applications/ApplicationController.cs index c3819d8f9..a63beedcf 100644 --- a/src/Prowlarr.Api.V1/Applications/ApplicationController.cs +++ b/src/Prowlarr.Api.V1/Applications/ApplicationController.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.Applications; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.Applications @@ -9,8 +10,8 @@ namespace Prowlarr.Api.V1.Applications public static readonly ApplicationResourceMapper ResourceMapper = new (); public static readonly ApplicationBulkResourceMapper BulkResourceMapper = new (); - public ApplicationController(ApplicationFactory applicationsFactory) - : base(applicationsFactory, "applications", ResourceMapper, BulkResourceMapper) + public ApplicationController(IBroadcastSignalRMessage signalRBroadcaster, ApplicationFactory applicationsFactory) + : base(signalRBroadcaster, applicationsFactory, "applications", ResourceMapper, BulkResourceMapper) { } } diff --git a/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs b/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs index 5dd43ea7d..dfaf24a9e 100644 --- a/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs +++ b/src/Prowlarr.Api.V1/DownloadClient/DownloadClientController.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.Download; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.DownloadClient @@ -9,8 +10,8 @@ namespace Prowlarr.Api.V1.DownloadClient public static readonly DownloadClientResourceMapper ResourceMapper = new (); public static readonly DownloadClientBulkResourceMapper BulkResourceMapper = new (); - public DownloadClientController(IDownloadClientFactory downloadClientFactory) - : base(downloadClientFactory, "downloadclient", ResourceMapper, BulkResourceMapper) + public DownloadClientController(IBroadcastSignalRMessage signalRBroadcaster, IDownloadClientFactory downloadClientFactory) + : base(signalRBroadcaster, downloadClientFactory, "downloadclient", ResourceMapper, BulkResourceMapper) { } } diff --git a/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs index ba6cbfbe7..d1be85292 100644 --- a/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs +++ b/src/Prowlarr.Api.V1/IndexerProxies/IndexerProxyController.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.IndexerProxies; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.IndexerProxies @@ -11,8 +12,8 @@ namespace Prowlarr.Api.V1.IndexerProxies public static readonly IndexerProxyResourceMapper ResourceMapper = new (); public static readonly IndexerProxyBulkResourceMapper BulkResourceMapper = new (); - public IndexerProxyController(IndexerProxyFactory notificationFactory) - : base(notificationFactory, "indexerProxy", ResourceMapper, BulkResourceMapper) + public IndexerProxyController(IBroadcastSignalRMessage signalRBroadcaster, IndexerProxyFactory notificationFactory) + : base(signalRBroadcaster, notificationFactory, "indexerProxy", ResourceMapper, BulkResourceMapper) { } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerController.cs b/src/Prowlarr.Api.V1/Indexers/IndexerController.cs index 0047acbca..6f360a574 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerController.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerController.cs @@ -1,6 +1,7 @@ using FluentValidation; using NzbDrone.Core.Indexers; using NzbDrone.Core.Validation; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.Indexers @@ -8,12 +9,13 @@ namespace Prowlarr.Api.V1.Indexers [V1ApiController] public class IndexerController : ProviderControllerBase { - public IndexerController(IndexerFactory indexerFactory, + public IndexerController(IBroadcastSignalRMessage signalRBroadcaster, + IndexerFactory indexerFactory, IndexerResourceMapper resourceMapper, IndexerBulkResourceMapper bulkResourceMapper, AppProfileExistsValidator appProfileExistsValidator, DownloadClientExistsValidator downloadClientExistsValidator) - : base(indexerFactory, "indexer", resourceMapper, bulkResourceMapper) + : base(signalRBroadcaster, indexerFactory, "indexer", resourceMapper, bulkResourceMapper) { SharedValidator.RuleFor(c => c.AppProfileId).Cascade(CascadeMode.Stop) .ValidId() diff --git a/src/Prowlarr.Api.V1/Notifications/NotificationController.cs b/src/Prowlarr.Api.V1/Notifications/NotificationController.cs index b6aa8d99e..1520a8dd4 100644 --- a/src/Prowlarr.Api.V1/Notifications/NotificationController.cs +++ b/src/Prowlarr.Api.V1/Notifications/NotificationController.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Notifications; +using NzbDrone.SignalR; using Prowlarr.Http; namespace Prowlarr.Api.V1.Notifications @@ -11,8 +12,8 @@ namespace Prowlarr.Api.V1.Notifications public static readonly NotificationResourceMapper ResourceMapper = new (); public static readonly NotificationBulkResourceMapper BulkResourceMapper = new (); - public NotificationController(NotificationFactory notificationFactory) - : base(notificationFactory, "notification", ResourceMapper, BulkResourceMapper) + public NotificationController(IBroadcastSignalRMessage signalRBroadcaster, NotificationFactory notificationFactory) + : base(signalRBroadcaster, notificationFactory, "notification", ResourceMapper, BulkResourceMapper) { } diff --git a/src/Prowlarr.Api.V1/ProviderControllerBase.cs b/src/Prowlarr.Api.V1/ProviderControllerBase.cs index ba1ccf553..725f6e58b 100644 --- a/src/Prowlarr.Api.V1/ProviderControllerBase.cs +++ b/src/Prowlarr.Api.V1/ProviderControllerBase.cs @@ -4,14 +4,21 @@ using FluentValidation; using FluentValidation.Results; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; +using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.Core.Validation; using NzbDrone.Http.REST.Attributes; +using NzbDrone.SignalR; using Prowlarr.Http.REST; namespace Prowlarr.Api.V1 { - public abstract class ProviderControllerBase : RestController + public abstract class ProviderControllerBase : RestControllerWithSignalR, + IHandle>, + IHandle>, + IHandle> where TProviderDefinition : ProviderDefinition, new() where TBulkProviderResource : ProviderBulkResource, new() where TProvider : IProvider @@ -21,11 +28,13 @@ namespace Prowlarr.Api.V1 protected readonly ProviderResourceMapper _resourceMapper; private readonly ProviderBulkResourceMapper _bulkResourceMapper; - protected ProviderControllerBase(IProviderFactory providerFactory, string resource, ProviderResourceMapper resourceMapper, ProviderBulkResourceMapper bulkResourceMapper) + : base(signalRBroadcaster) { _providerFactory = providerFactory; _resourceMapper = resourceMapper; @@ -244,6 +253,24 @@ namespace Prowlarr.Api.V1 return Json(data); } + [NonAction] + public void Handle(ProviderAddedEvent message) + { + BroadcastResourceChange(ModelAction.Created, message.Definition.Id); + } + + [NonAction] + public void Handle(ProviderUpdatedEvent message) + { + BroadcastResourceChange(ModelAction.Updated, message.Definition.Id); + } + + [NonAction] + public void Handle(ProviderDeletedEvent message) + { + BroadcastResourceChange(ModelAction.Deleted, message.ProviderId); + } + private void Validate(TProviderDefinition definition, bool includeWarnings) { var validationResult = definition.Settings.Validate();