This commit is contained in:
TheXamlGuy
2024-02-10 20:19:01 +00:00
parent ecfac99868
commit 565c6866d8
60 changed files with 445 additions and 381 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ public sealed class NavigateAction :
{
if (frameworkElement.DataContext is IObservableViewModel observableViewModel)
{
observableViewModel.Mediator.PublishAsync(new Navigate(Path))
observableViewModel.Publisher.PublishAsync(new Navigate(Path))
.GetAwaiter().GetResult();
}
}
+10 -1
View File
@@ -4,6 +4,15 @@ using Microsoft.UI.Xaml.Markup;
namespace Hyperbar.UI.Windows;
public class ViewModelBinder
{
public void Bind(object viewModel,
FrameworkElement view)
{
view.DataContext ??= viewModel;
}
}
public class ViewModelTemplate(IViewModelTemplateDescriptorProvider descriptors) :
DataTemplateSelector,
IViewModelTemplate
@@ -19,7 +28,7 @@ public class ViewModelTemplate(IViewModelTemplateDescriptorProvider descriptors)
DependencyObject container) =>
SelectTemplateCore(item);
private DataTemplate CreateDataTemplate(IViewModelTemplateDescriptor descriptor)
private static DataTemplate CreateDataTemplate(IViewModelTemplateDescriptor descriptor)
{
string xamlString = @$"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
-6
View File
@@ -1,17 +1,11 @@
using Hyperbar.Interop.Windows;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Graphics;
using WinRT.Interop;
namespace Hyperbar.UI.Windows;
public class NavigationItemTemplateSelector :
DataTemplateSelector
{
}
public static class WindowExtensions
{
public static WindowMessageListener CreateMessageListener(this Window window) =>
+2 -2
View File
@@ -20,7 +20,7 @@ public class WindowHandler :
public Task Handle(Navigate<Window> args,
CancellationToken cancellationToken)
{
if (args.Template is Window window)
if (args.View is Window window)
{
if (window.Content is FrameworkElement content)
{
@@ -33,8 +33,8 @@ public class WindowHandler :
}
}
//ViewModelBinder.Bind(args.ViewModel, content);
window.Closed += HandleClosed;
content.DataContext = args.Content;
}
window.Activate();
@@ -5,10 +5,11 @@ namespace Hyperbar.Widget.MediaController.Windows;
public partial class MediaButtonViewModel<TMediaButton>(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IRelayCommand invokeCommand) :
WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer),
WidgetComponentViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer),
INotificationHandler<Changed<MediaButton<TMediaButton>>>,
IMediaButtonViewModel
{
@@ -29,5 +30,5 @@ public partial class MediaButtonViewModel<TMediaButton>(IServiceProvider service
}
public override async Task InitializeAsync() =>
await Mediator.PublishAsync<Request<TMediaButton>>();
await Publisher.PublishAsync<Request<TMediaButton>>();
}
@@ -14,23 +14,24 @@ public class MediaController :
IDisposable
{
private readonly IDisposer disposer;
private readonly IMediator mediator;
private readonly IPublisher publisher;
private readonly GlobalSystemMediaTransportControlsSession session;
private bool isNextEnabled;
private bool isPreviousEnabled;
private GlobalSystemMediaTransportControlsSessionPlaybackStatus playbackStatus;
public MediaController(IMediator mediator,
public MediaController(IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
GlobalSystemMediaTransportControlsSession session)
{
this.mediator = mediator;
this.publisher = publisher;
this.disposer = disposer;
this.session = session;
disposer.Add(this);
mediator.Subscribe(this);
subscriber.Add(this);
session.MediaPropertiesChanged += OnMediaPropertiesChanged;
session.PlaybackInfoChanged += OnPlaybackInfoChanged;
@@ -117,7 +118,7 @@ public class MediaController :
buffer = memoryStream.ToArray();
}
await mediator.PublishAsync(new Changed<MediaInformation>(new MediaInformation(mediaProperties.Title,
await publisher.PublishAsync(new Changed<MediaInformation>(new MediaInformation(mediaProperties.Title,
mediaProperties.Artist, buffer)));
}
catch
@@ -133,7 +134,7 @@ public class MediaController :
GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo =
session.GetPlaybackInfo();
await mediator.PublishAsync(new Changed<MediaButton<MediaPlayPauseButton>>(new
await publisher.PublishAsync(new Changed<MediaButton<MediaPlayPauseButton>>(new
MediaButton<MediaPlayPauseButton>(playbackInfo.PlaybackStatus is
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing ?
new MediaButtonPlaying() :
@@ -142,7 +143,7 @@ public class MediaController :
bool isPreviousEnabled = playbackInfo.Controls.IsPreviousEnabled;
if (this.isPreviousEnabled != isPreviousEnabled)
{
await mediator.PublishAsync(new Changed<MediaButton<MediaPreviousButton>>(new
await publisher.PublishAsync(new Changed<MediaButton<MediaPreviousButton>>(new
MediaButton<MediaPreviousButton>(isPreviousEnabled ? new MediaButtonEnabled() :
new MediaButtonDisabled())));
@@ -152,7 +153,7 @@ public class MediaController :
bool isNextEnabled = playbackInfo.Controls.IsNextEnabled;
if (this.isNextEnabled != isNextEnabled)
{
await mediator.PublishAsync(new Changed<MediaButton<MediaNextButton>>(new
await publisher.PublishAsync(new Changed<MediaButton<MediaNextButton>>(new
MediaButton<MediaNextButton>(isNextEnabled ? new MediaButtonEnabled() :
new MediaButtonDisabled())));
@@ -2,7 +2,7 @@
namespace Hyperbar.Widget.MediaController.Windows;
public class MediaControllerHandler(IMediator mediator,
public class MediaControllerHandler(IPublisher publisher,
IServiceScopeProvider<MediaController> scopeProvider,
ICache<MediaController, MediaControllerViewModel> cache) :
INotificationHandler<Create<MediaController>>,
@@ -18,7 +18,7 @@ public class MediaControllerHandler(IMediator mediator,
factory.Create(mediaController) is MediaControllerViewModel viewModel)
{
cache.Add(mediaController, viewModel);
await mediator.PublishAsync(new Create<MediaControllerViewModel>(viewModel), cancellationToken);
await publisher.PublishAsync(new Create<MediaControllerViewModel>(viewModel), cancellationToken);
}
}
@@ -28,7 +28,7 @@ public class MediaControllerHandler(IMediator mediator,
cache.TryGetValue(mediaController, out MediaControllerViewModel? viewModel) &&
viewModel is not null)
{
await mediator.PublishAsync(new Remove<MediaControllerViewModel>(viewModel), cancellationToken);
await publisher.PublishAsync(new Remove<MediaControllerViewModel>(viewModel), cancellationToken);
cache.Remove(mediaController);
}
}
@@ -3,7 +3,7 @@ using Windows.Media.Control;
namespace Hyperbar.Widget.MediaController.Windows;
public class MediaControllerService(IMediator mediator,
public class MediaControllerService(IPublisher publisher,
IFactory<GlobalSystemMediaTransportControlsSession, MediaController> factory) :
IHostedService
{
@@ -34,7 +34,7 @@ public class MediaControllerService(IMediator mediator,
{
if (factory.Create(session) is MediaController mediaController)
{
await mediator.PublishAsync(new Create<MediaController>(mediaController));
await publisher.PublishAsync(new Create<MediaController>(mediaController));
cache.Add(new KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController>(session,
mediaController));
}
@@ -53,7 +53,7 @@ public class MediaControllerService(IMediator mediator,
{
if (!sessions.Any(x => x.SourceAppUserModelId == session.Key.SourceAppUserModelId))
{
await mediator.PublishAsync(new Remove<MediaController>(session.Value));
await publisher.PublishAsync(new Remove<MediaController>(session.Value));
cache.Remove(session);
}
}
@@ -10,21 +10,22 @@ public class MediaControllerViewModel :
public MediaControllerViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
Template = template;
Add<MediaInformationViewModel>();
Add<MediaButtonViewModel<MediaPreviousButton>>(new RelayCommand(async () =>
await mediator.PublishAsync<Request<MediaPrevious>>()));
await publisher.PublishAsync<Request<MediaPrevious>>()));
Add<MediaButtonViewModel<MediaPlayPauseButton>>(new RelayCommand(async () =>
await mediator.PublishAsync<Request<MediaPlayPause>>()));
await publisher.PublishAsync<Request<MediaPlayPause>>()));
Add<MediaButtonViewModel<MediaNextButton>>(new RelayCommand(async () =>
await mediator.PublishAsync<Request<MediaNext>>()));
await publisher.PublishAsync<Request<MediaNext>>()));
}
public IViewModelTemplate Template { get; }
@@ -5,10 +5,11 @@ namespace Hyperbar.Widget.MediaController.Windows;
public class MediaControllerWidgetViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IEnumerable<MediaControllerViewModel> items) :
ObservableCollectionViewModel<MediaControllerViewModel>(serviceProvider, serviceFactory, mediator, disposer, items),
ObservableCollectionViewModel<MediaControllerViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer, items),
IWidgetViewModel
{
public IViewModelTemplate Template => template;
@@ -4,9 +4,10 @@ namespace Hyperbar.Widget.MediaController.Windows;
public partial class MediaInformationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) :
WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer),
WidgetComponentViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer),
INotificationHandler<Changed<MediaInformation>>
{
[ObservableProperty]
@@ -32,5 +33,5 @@ public partial class MediaInformationViewModel(IServiceProvider serviceProvider,
}
public override async Task InitializeAsync() =>
await Mediator.PublishAsync<Request<MediaInformation>>();
await Publisher.PublishAsync<Request<MediaInformation>>();
}
@@ -1,8 +1,6 @@
using Hyperbar.Widget;
namespace Hyperbar.Widget.Primary.Windows;
namespace Hyperbar.Widget.Primary.Windows;
public class PrimaryWidgetConfigurationHandler(IMediator mediator,
public class PrimaryWidgetConfigurationHandler(IPublisher publisher,
PrimaryWidgetConfiguration configuration,
IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory,
IProvider<PrimaryCommandConfiguration, IWidgetComponentViewModel?> provider,
@@ -44,7 +42,7 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator,
if (moved.Value is PrimaryCommandConfiguration configuration &&
provider.Get(configuration) is IWidgetComponentViewModel viewModel)
{
await mediator.PublishAsync(new Move<IWidgetComponentViewModel>(configuration.Order, viewModel),
await publisher.PublishAsync(new Move<IWidgetComponentViewModel>(configuration.Order, viewModel),
moved.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : moved.Key.ParentId,
cancellationToken);
@@ -61,8 +59,7 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator,
if (added.Value is PrimaryCommandConfiguration configuration &&
factory.Create(configuration) is IWidgetComponentViewModel viewModel)
{
await mediator.PublishAsync(
new Insert<IWidgetComponentViewModel>(configuration.Order, viewModel),
await publisher.PublishAsync(new Insert<IWidgetComponentViewModel>(configuration.Order, viewModel),
added.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : added.Key.ParentId,
cancellationToken);
@@ -78,8 +75,7 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator,
if (removed.Value is PrimaryCommandConfiguration configuration &&
provider.Get(configuration) is IWidgetComponentViewModel viewModel)
{
await mediator.PublishAsync(
new Remove<IWidgetComponentViewModel>(viewModel),
await publisher.PublishAsync(new Remove<IWidgetComponentViewModel>(viewModel),
removed.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : removed.Key.ParentId,
cancellationToken);
@@ -5,7 +5,8 @@ namespace Hyperbar.Widget.Primary.Windows;
[NotificationHandler(nameof(PrimaryWidgetViewModel))]
public class PrimaryWidgetViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) :
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceProvider, serviceFactory, mediator, disposer),
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer),
IWidgetViewModel;
@@ -1,14 +1,13 @@
using Hyperbar.Widget;
namespace Hyperbar.Widget.Primary.Windows;
namespace Hyperbar.Widget.Primary.Windows;
public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration,
IMediator mediator,
IPublisher publisher,
IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?> factory,
ICache<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> cache) :
INotificationHandler<Enumerate<IWidgetComponentViewModel>>
{
public async Task Handle(Enumerate<IWidgetComponentViewModel> notification, CancellationToken cancellationToken)
public async Task Handle(Enumerate<IWidgetComponentViewModel> notification,
CancellationToken cancellationToken)
{
Stack<(Guid, List<PrimaryCommandConfiguration>)> stack = new();
stack.Push((Guid.Empty, configuration.Commands));
@@ -30,7 +29,7 @@ public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration confi
{
if (factory.Create(item) is IWidgetComponentViewModel viewModel)
{
await mediator.PublishAsync(new Create<IWidgetComponentViewModel>(viewModel), nameof(PrimaryWidgetViewModel),
await publisher.PublishAsync(new Create<IWidgetComponentViewModel>(viewModel), nameof(PrimaryWidgetViewModel),
cancellationToken);
}
}
+3 -2
View File
@@ -6,12 +6,13 @@ namespace Hyperbar.Widget;
[NotificationHandler(nameof(Id))]
public partial class WidgetButtonViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
Guid id,
string? text = null,
string? icon = null,
RelayCommand? invokeCommand = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer)
RelayCommand? invokeCommand = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
[ObservableProperty]
private string? icon = icon;
+6 -4
View File
@@ -6,17 +6,19 @@ public partial class WidgetComponentViewModel :
{
public WidgetComponentViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IEnumerable<IWidgetComponentViewModel> items) : base(serviceProvider, serviceFactory, mediator, disposer, items)
IEnumerable<IWidgetComponentViewModel> items) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer, items)
{
}
public WidgetComponentViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
}
+2 -2
View File
@@ -6,7 +6,7 @@ namespace Hyperbar.Widget;
public class WidgetExtensionEnumerator(IFactory<Type, IWidget> factory,
IHostEnvironment hostEnvironment,
IMediator mediator) :
IPublisher publisher) :
INotificationHandler<Enumerate<WidgetExtension>>
{
public Task Handle(Enumerate<WidgetExtension> args,
@@ -29,7 +29,7 @@ public class WidgetExtensionEnumerator(IFactory<Type, IWidget> factory,
{
if (factory.Create(widgetType) is IWidget widget)
{
await mediator.PublishAsync(new Create<WidgetExtension>(new WidgetExtension(widget,
await publisher.PublishAsync(new Create<WidgetExtension>(new WidgetExtension(widget,
new WidgetAssembly(assembly))), cancellationToken);
}
}
+2 -2
View File
@@ -3,7 +3,7 @@
namespace Hyperbar.Widget;
public class WidgetExtensionHandler(IServiceProvider provider,
IMediator mediator,
IPublisher publisher,
IProxyServiceCollection<IWidgetBuilder> typedServices) :
INotificationHandler<Create<WidgetExtension>>
{
@@ -22,7 +22,7 @@ public class WidgetExtensionHandler(IServiceProvider provider,
});
IWidgetHost host = builder.Build();
await mediator.PublishAsync(new Create<IWidgetHost>(host),
await publisher.PublishAsync(new Create<IWidgetHost>(host),
cancellationToken);
}
}
@@ -1,8 +1,8 @@
namespace Hyperbar.Widget;
public class WidgetExtensionInitializer(IMediator mediator) :
public class WidgetExtensionInitializer(IPublisher publisher) :
IInitializer
{
public async Task InitializeAsync() =>
await mediator.PublishAsync<Enumerate<WidgetExtension>>();
await publisher.PublishAsync<Enumerate<WidgetExtension>>();
}
+5 -5
View File
@@ -7,17 +7,17 @@ public sealed class WidgetHost :
IWidgetHost
{
private readonly IServiceProvider services;
private readonly IMediator mediator;
private readonly IPublisher publisher;
private readonly IProxyService<IMediator> proxyMediator;
private readonly IEnumerable<IHostedService> hostedServices;
public WidgetHost(IServiceProvider services,
IMediator mediator,
IPublisher publisher,
IProxyService<IMediator> proxyMediator,
IEnumerable<IHostedService> hostedServices)
{
this.services = services;
this.mediator = mediator;
this.publisher = publisher;
this.proxyMediator = proxyMediator;
this.hostedServices = hostedServices;
}
@@ -41,11 +41,11 @@ public sealed class WidgetHost :
if (proxyMediator.Proxy is IMediator mediator)
{
await mediator.PublishAsync(new Started<IWidgetHost>(this),
await publisher.PublishAsync(new Started<IWidgetHost>(this),
cancellationToken);
}
await this.mediator.PublishAsync(new Started<IWidgetHost>(this),
await this.publisher.PublishAsync(new Started<IWidgetHost>(this),
cancellationToken);
}
+3 -2
View File
@@ -6,12 +6,13 @@ namespace Hyperbar.Widget;
[NotificationHandler(nameof(Id))]
public partial class WidgetMenuViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
Guid id = default,
string? text = null,
string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer)
RelayCommand? command = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
[ObservableProperty]
private IRelayCommand? click = command;
@@ -6,13 +6,14 @@ namespace Hyperbar.Widget;
[NotificationHandler(nameof(Id))]
public partial class WidgetSplitButtonViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IEnumerable<IWidgetComponentViewModel> items,
Guid id = default,
string? text = null,
string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer, items)
RelayCommand? command = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer, items)
{
[ObservableProperty]
private IRelayCommand? click = command;
+2 -2
View File
@@ -2,7 +2,7 @@
namespace Hyperbar.Widget;
public class WidgetStartedHandler(IMediator mediator) :
public class WidgetStartedHandler(IPublisher publisher) :
INotificationHandler<Started<IWidgetHost>>
{
public async Task Handle(Started<IWidgetHost> notification,
@@ -12,7 +12,7 @@ public class WidgetStartedHandler(IMediator mediator) :
{
if (host.Services.GetService<IWidgetViewModel>() is IWidgetViewModel viewModel)
{
await mediator.PublishAsync(new Create<IWidgetViewModel>(viewModel),
await publisher.PublishAsync(new Create<IWidgetViewModel>(viewModel),
nameof(IWidgetHostViewModel), cancellationToken);
}
}
+2 -2
View File
@@ -3,7 +3,7 @@
namespace Hyperbar.Widget;
public class WidgetViewModelEnumerator(IWidgetHost host,
IMediator mediator) :
IPublisher publisher) :
INotificationHandler<Enumerate<IWidgetViewModel>>
{
public async Task Handle(Enumerate<IWidgetViewModel> notification,
@@ -13,7 +13,7 @@ public class WidgetViewModelEnumerator(IWidgetHost host,
{
foreach (IWidgetViewModel viewModel in viewModels)
{
await mediator.PublishAsync(new Create<IWidgetViewModel>(viewModel),
await publisher.PublishAsync(new Create<IWidgetViewModel>(viewModel),
nameof(IWidgetHostViewModel), cancellationToken);
}
}
+1 -5
View File
@@ -2,16 +2,12 @@
<Application
x:Class="Hyperbar.Windows.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:Hyperbar.UI.Windows">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="DefaultDataTemplate">
<ui:ViewModelTemplatePresenter />
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
+3 -2
View File
@@ -10,8 +10,9 @@ public partial class ApplicationBarViewModel :
public ApplicationBarViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
Template = template;
@@ -2,9 +2,10 @@
public class GeneralSettingsNavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
string text) :
NavigationViewModel(serviceProvider, serviceFactory, mediator, disposer, text)
NavigationViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer, text)
{
}
+3 -2
View File
@@ -7,10 +7,11 @@ namespace Hyperbar.Widget;
public partial class PrimaryViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
int index) :
ObservableCollectionViewModel<IWidgetViewModel>(serviceProvider, serviceFactory, mediator, disposer),
ObservableCollectionViewModel<IWidgetViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer),
IWidgetHostViewModel
{
[ObservableProperty]
+3 -2
View File
@@ -13,9 +13,10 @@ public partial class SecondaryViewModel :
public SecondaryViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
int index) : base(serviceProvider, serviceFactory, mediator, disposer)
int index) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
Template = template;
this.index = index;
+2 -2
View File
@@ -5,9 +5,9 @@ namespace Hyperbar.Windows;
public partial class SettingsButtonViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
IDisposer disposer) :
ObservableViewModel(serviceProvider, serviceFactory, mediator, disposer)
ObservableViewModel(serviceProvider, serviceFactory, publisher, disposer)
{
public IViewModelTemplate Template => template;
}
+3 -2
View File
@@ -8,8 +8,9 @@ public partial class SettingsViewModel :
public SettingsViewModel(IViewModelTemplate template,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
Template = template;
@@ -2,9 +2,10 @@
public class WidgetNavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
string text) :
NavigationViewModel(serviceProvider, serviceFactory, mediator, disposer, text)
NavigationViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer, text)
{
}
@@ -2,10 +2,11 @@
public class WidgetSettingsNavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
string text) :
NavigationViewModel<WidgetNavigationViewModel>(serviceProvider, serviceFactory, mediator, disposer, text)
NavigationViewModel<WidgetNavigationViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer, text)
{
}
@@ -1,6 +1,6 @@
namespace Hyperbar;
public class ConfigurationInitializer<TConfiguration>(IMediator mediator,
public class ConfigurationInitializer<TConfiguration>(IPublisher publisher,
IConfigurationMonitor<TConfiguration> monitor,
IConfigurationReader<TConfiguration> reader,
IConfigurationWriter<TConfiguration> writer,
@@ -21,7 +21,7 @@ public class ConfigurationInitializer<TConfiguration>(IMediator mediator,
}
}
await mediator.PublishAsync(new Changed<TConfiguration>(configuration));
await publisher.PublishAsync(new Changed<TConfiguration>(configuration));
await monitor.InitializeAsync();
}
}
@@ -2,7 +2,7 @@
public class ConfigurationMonitor<TConfiguration>(IConfigurationFile<TConfiguration> configurationFile,
IConfigurationReader<TConfiguration> reader,
IMediator mediator) :
IPublisher publisher) :
IConfigurationMonitor<TConfiguration>
where TConfiguration :
class
@@ -16,7 +16,7 @@ public class ConfigurationMonitor<TConfiguration>(IConfigurationFile<TConfigurat
{
if (reader.Read() is { } configuration)
{
await mediator.PublishAsync(new Changed<TConfiguration>(configuration));
await publisher.PublishAsync(new Changed<TConfiguration>(configuration));
}
}
@@ -1,6 +1,6 @@
namespace Hyperbar;
public class ConfigurationValueChangedNotification<TConfiguration, TValue>(IMediator mediator,
public class ConfigurationValueChangedNotification<TConfiguration, TValue>(IPublisher publisher,
Func<TConfiguration, Action<TValue>> factory) :
IConfigurationValueChangedNotification<TConfiguration>
where TConfiguration :
@@ -18,7 +18,7 @@ public class ConfigurationValueChangedNotification<TConfiguration, TValue>(IMedi
if (value is null || !value.Equals(newValue))
{
value = newValue;
await mediator.PublishAsync(new Changed<TValue>(value));
await publisher.PublishAsync(new Changed<TValue>(value));
}
}
}
@@ -19,7 +19,7 @@ public static class IServiceCollectionExtensions
class, new()
{
services.AddSingleton<IConfigurationValueChangedNotification<TConfiguration>>(provider =>
new ConfigurationValueChangedNotification<TConfiguration, TValue>(provider.GetRequiredService<IMediator>(),
new ConfigurationValueChangedNotification<TConfiguration, TValue>(provider.GetRequiredService<IPublisher>(),
factory));
return services;
@@ -237,18 +237,6 @@ public static class IServiceCollectionExtensions
return services;
}
public static IServiceCollection AddNotificationRelay<TFromNotification,
TToNotification>(this IServiceCollection services)
where TFromNotification :
INotification
where TToNotification :
INotification, new()
{
return services.AddHandler<NotficationRelayHandler<TFromNotification,
TToNotification>>();
}
public static IServiceCollection AddRange(this IServiceCollection services,
IServiceCollection fromServices)
{
+23
View File
@@ -0,0 +1,23 @@
using System.Runtime.CompilerServices;
namespace Hyperbar;
public class AsyncLock(int initial = 1,
int maximum = 1) :
IDisposable
{
private readonly SemaphoreSlim semaphore = new(initial, maximum);
public void Dispose()
{
semaphore.Release();
}
public TaskAwaiter<AsyncLock> GetAwaiter() => LockAsync().GetAwaiter();
private async Task<AsyncLock> LockAsync()
{
await semaphore.WaitAsync();
return this;
}
}
+7 -6
View File
@@ -11,24 +11,25 @@ public class Cache<TValue>(IDisposer disposer) :
public void Add(TValue value)
{
disposer.Add(value!, Disposable.Create(() =>
if (value is null)
{
Remove(value);
}));
return;
}
disposer.Add(value, Disposable.Create(() => Remove(value)));
cache.Add(value);
}
public void Clear() => cache.Clear();
public System.Collections.Generic.IEnumerator<TValue> GetEnumerator() => cache.GetEnumerator();
public IEnumerator<TValue> GetEnumerator() => cache.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public bool Remove(TValue value) => cache.Remove(value);
}
public class Cache<TKey, TValue>(IDisposer disposer) :
public class Cache<TKey, TValue> :
ICache<TKey, TValue>
where TKey :
notnull
@@ -47,7 +48,7 @@ public class Cache<TKey, TValue>(IDisposer disposer) :
public bool ContainsKey(TKey key) => cache.ContainsKey(key);
public System.Collections.Generic.IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => cache.GetEnumerator();
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => cache.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+1 -22
View File
@@ -1,30 +1,9 @@
using System.Runtime.CompilerServices;
using System.Reactive.Disposables;
using System.Reactive.Disposables;
using System.Collections;
using System.Collections.Concurrent;
namespace Hyperbar;
public class AsyncLock(int initial = 1,
int maximum = 1) : IDisposable
{
private readonly SemaphoreSlim semaphore = new(initial, maximum);
public void Dispose()
{
semaphore.Release();
}
public TaskAwaiter<AsyncLock> GetAwaiter() => LockAsync().GetAwaiter();
private async Task<AsyncLock> LockAsync()
{
await semaphore.WaitAsync();
return this;
}
}
public class Disposer :
IDisposer
{
-6
View File
@@ -1,6 +0,0 @@
namespace Hyperbar;
public interface IEnumerator<TItem>
{
IEnumerable<TItem?> Get();
}
+1 -1
View File
@@ -6,7 +6,7 @@ public interface IObservableViewModel :
{
public IDisposer Disposer { get; }
public IMediator Mediator { get; }
public IPublisher Publisher { get; }
public IServiceFactory ServiceFactory { get; }
+1 -1
View File
@@ -4,5 +4,5 @@ namespace Hyperbar;
public record Navigate(object Key) :
INotification;
public record Navigate<TTemplate>(TTemplate Template, object Content) :
public record Navigate<TView>(TView View, object ViewModel) :
INotification;
+4 -4
View File
@@ -7,16 +7,16 @@ public class NavigateHandler :
{
private readonly IViewModelTemplateDescriptorProvider contentTemplateDescriptors;
private readonly IServiceProvider provider;
private readonly IMediator mediator;
private readonly IPublisher publisher;
private readonly IEnumerable<INavigationDescriptor> navigationDescriptors;
public NavigateHandler(IServiceProvider provider,
IMediator mediator,
IPublisher publisher,
IEnumerable<INavigationDescriptor> navigationDescriptors,
IViewModelTemplateDescriptorProvider contentTemplateDescriptors)
{
this.provider = provider;
this.mediator = mediator;
this.publisher = publisher;
this.navigationDescriptors = navigationDescriptors;
this.contentTemplateDescriptors = contentTemplateDescriptors;
}
@@ -43,7 +43,7 @@ public class NavigateHandler :
if (Activator.CreateInstance(navigateType,
new object[] { template, content }) is object navigate)
{
await mediator.PublishAsync(navigate, cancellationToken);
await publisher.PublishAsync(navigate, cancellationToken);
}
}
}
+11 -14
View File
@@ -2,29 +2,26 @@
namespace Hyperbar;
public partial class NavigationViewModel :
ObservableCollectionViewModel<INavigationViewModel>,
public partial class NavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
string text) :
ObservableCollectionViewModel<INavigationViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer),
INavigationViewModel
{
[ObservableProperty]
private string? text;
public NavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
string text) : base(serviceProvider, serviceFactory, mediator, disposer)
{
this.text = text;
}
private string? text = text;
}
public partial class NavigationViewModel<TNavigationViewModel>(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
string text) :
ObservableCollectionViewModel<TNavigationViewModel>(serviceProvider, serviceFactory, mediator, disposer),
ObservableCollectionViewModel<TNavigationViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer),
INavigationViewModel
where TNavigationViewModel :
INavigationViewModel
@@ -27,31 +27,33 @@ public partial class ObservableCollectionViewModel<TViewModel> :
public ObservableCollectionViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer)
{
ServiceProvider = serviceProvider;
ServiceFactory = serviceFactory;
Mediator = mediator;
Publisher = publisher;
Disposer = disposer;
mediator.Subscribe(this);
subscriber.Add(this);
collection.CollectionChanged += OnCollectionChanged;
}
public ObservableCollectionViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IEnumerable<TViewModel> items)
{
ServiceProvider = serviceProvider;
ServiceFactory = serviceFactory;
Mediator = mediator;
Publisher = publisher;
Disposer = disposer;
mediator.Subscribe(this);
subscriber.Add(this);
collection.CollectionChanged += OnCollectionChanged;
AddRange(items);
@@ -74,14 +76,14 @@ public partial class ObservableCollectionViewModel<TViewModel> :
bool ICollection.IsSynchronized => false;
public IMediator Mediator { get; private set; }
public IPublisher Publisher { get; private set; }
public IServiceFactory ServiceFactory { get; private set; }
object ICollection.SyncRoot => this;
public IServiceProvider ServiceProvider { get; private set; }
object ICollection.SyncRoot => this;
public TViewModel this[int index]
{
get => collection[index];
@@ -374,7 +376,7 @@ public partial class ObservableCollectionViewModel<TViewModel> :
isInitialized = true;
await Mediator.PublishAsync<Enumerate<TViewModel>>();
await Publisher.PublishAsync<Enumerate<TViewModel>>();
await InitializeAsync();
}
@@ -384,6 +386,7 @@ public partial class ObservableCollectionViewModel<TViewModel> :
public class ObservableCollectionViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) :
ObservableCollectionViewModel<IDisposable>(serviceProvider, serviceFactory, mediator, disposer);
ObservableCollectionViewModel<IDisposable>(serviceProvider, serviceFactory, publisher, subscriber, disposer);
+2 -2
View File
@@ -6,7 +6,7 @@ namespace Hyperbar;
public class ObservableViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IPublisher publisher,
IDisposer disposer) :
ObservableObject,
IObservableViewModel
@@ -18,7 +18,7 @@ public class ObservableViewModel(IServiceProvider serviceProvider,
public ICommand InitializeCommand =>
new AsyncRelayCommand(CoreInitializeAsync);
public IMediator Mediator => mediator;
public IPublisher Publisher => publisher;
public IServiceFactory ServiceFactory => serviceFactory;
-1
View File
@@ -3,5 +3,4 @@
public delegate Task<TResponse> HandlerDelegate<TMessage, TResponse>(TMessage message,
CancellationToken cancellationToken)
where TMessage :
notnull,
IMessage;
+15 -17
View File
@@ -1,27 +1,25 @@
namespace Hyperbar;
public class HandlerWrapper<TRequest, TResponse>
where TRequest :
class,
IRequest<TResponse>
public class HandlerWrapper<TMessage, TReply>(IHandler<TMessage, TReply> handler,
IEnumerable<IPipelineBehavior<TMessage, TReply>> pipelineBehaviours)
where TMessage : class, IRequest<TReply>
{
private readonly HandlerDelegate<TRequest, TResponse> handler;
private readonly IEnumerable<IPipelineBehavior<TMessage, TReply>> pipelineBehaviours =
pipelineBehaviours.Reverse();
public HandlerWrapper(IHandler<TRequest, TResponse> concreteHandler,
IEnumerable<IPipelineBehavior<TRequest, TResponse>> pipelineBehaviours)
public async Task<TReply> Handle(TMessage message, CancellationToken cancellationToken)
{
HandlerDelegate<TRequest, TResponse> handler = concreteHandler.Handle;
foreach (IPipelineBehavior<TRequest, TResponse>? pipeline in pipelineBehaviours.Reverse())
HandlerDelegate<TMessage, TReply> currentHandler = handler.Handle;
foreach (IPipelineBehavior<TMessage, TReply> behavior in pipelineBehaviours)
{
HandlerDelegate<TRequest, TResponse> handlerCopy = handler;
IPipelineBehavior<TRequest, TResponse> pipelineCopy = pipeline;
handler = (TRequest message, CancellationToken cancellationToken) =>
pipelineCopy.Handle(message, handlerCopy, cancellationToken);
HandlerDelegate<TMessage, TReply> previousHandler = currentHandler;
currentHandler = async (msg, token) =>
{
return await behavior.Handle(msg, previousHandler, token);
};
}
this.handler = handler;
return await currentHandler(message, cancellationToken);
}
public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken);
}
-32
View File
@@ -2,41 +2,9 @@
public interface IMediator
{
Task PublishAsync<TNotification>(TNotification notification,
object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification,
new();
Task PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
Task PublishAsync(object notification,
CancellationToken cancellationToken = default);
Task PublishAsync(object notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default);
Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
where TNotification :
INotification,
new();
Task<TResponse?> SendAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default);
Task<object?> SendAsync(object message, CancellationToken
cancellationToken = default);
void Subscribe(object handler);
}
+2 -1
View File
@@ -1,3 +1,4 @@
namespace Hyperbar;
public interface INotification : IMessage;
public interface INotification :
IMessage;
+34
View File
@@ -0,0 +1,34 @@
namespace Hyperbar;
public interface IPublisher
{
Task PublishAsync<TNotification>(TNotification notification,
object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification,
new();
Task PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
Task PublishAsync(object notification,
CancellationToken cancellationToken = default);
Task PublishAsync(object notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default);
Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
where TNotification :
INotification,
new();
}
+8
View File
@@ -0,0 +1,8 @@
namespace Hyperbar;
public interface ISubscriber
{
void Remove(object subscriber);
void Add(object subscriber);
}
@@ -0,0 +1,10 @@
namespace Hyperbar;
public interface ISubscriptionManager
{
IEnumerable<object?> GetHandlers(Type notificationType, object key);
void Remove(object subscriber);
void Add(object subscriber);
}
+2 -125
View File
@@ -1,91 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using System.Reflection;
namespace Hyperbar;
namespace Hyperbar;
public class Mediator(IServiceProvider provider,
IDispatcher dispatcher) :
public class Mediator(IServiceProvider provider) :
IMediator
{
private readonly ConcurrentDictionary<object, List<object>> handlers = [];
public Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification,
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken);
public Task PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
}
public Task PublishAsync<TNotification>(TNotification notification,
object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken);
}
public Task PublishAsync(object notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default)
{
Type notificationType = notification.GetType();
List<object?> handlers = provider.GetServices(typeof(INotificationHandler<>)
.MakeGenericType(notificationType)).ToList();
foreach (KeyValuePair<object, List<object>> subscriber in this.handlers)
{
if (subscriber.Key.Equals($"{key?.ToString()}:{notificationType}"))
{
handlers.AddRange(subscriber.Value);
}
}
foreach (object? handler in handlers)
{
if (handler is not null)
{
Type? handlerType = handler.GetType();
MethodInfo? handleMethod = handlerType.GetMethod("Handle",
[notificationType, typeof(CancellationToken)]);
if (handleMethod is not null)
{
marshal(() => (Task)handleMethod.Invoke(handler, new object[] { notification,
cancellationToken })!);
}
}
}
return Task.CompletedTask;
}
public Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
where TNotification :
INotification,
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
public Task PublishAsync(object notification,
CancellationToken cancellationToken = default)
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
}
public Task<TResponse?> SendAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
@@ -121,44 +38,4 @@ public class Mediator(IServiceProvider provider,
return Task.FromResult<object?>(default);
}
public void Subscribe(object handler)
{
Type handlerType = handler.GetType();
object? key = GetKeyFromHandler(handlerType, handler);
foreach (Type interfaceType in GetNotificationHandlerInterfaces(handlerType))
{
if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType)
{
handlers.AddOrUpdate($"{(key is not null ? $"{key}:" : "")}{argumentType}", new List<object> { handler }, (value, collection) =>
{
collection.Add(handler);
return collection;
});
}
}
}
private object? GetKeyFromHandler(Type handlerType, object handler)
{
if (handlerType.GetCustomAttribute<NotificationHandlerAttribute>() is NotificationHandlerAttribute attribute)
{
if (handlerType.GetProperty($"{attribute.Key}") is PropertyInfo property
&& property.GetValue(handler) is { } value)
{
return value;
}
else
{
return attribute.Key;
}
}
return null;
}
private IEnumerable<Type> GetNotificationHandlerInterfaces(Type handlerType) => handlerType.GetInterfaces()
.Where(interfaceType => interfaceType.IsGenericType && interfaceType
.GetGenericTypeDefinition() == typeof(INotificationHandler<>));
}
@@ -1,16 +0,0 @@
namespace Hyperbar;
public class NotficationRelayHandler<TFromNotification, ToNotification>(IMediator mediator) :
INotificationHandler<TFromNotification>,
IHandler
where TFromNotification :
INotification
where ToNotification :
INotification, new()
{
private readonly IMediator mediator = mediator;
public Task Handle(TFromNotification notification, CancellationToken cancellationToken) =>
mediator.PublishAsync<ToNotification>(cancellationToken);
}
+84
View File
@@ -0,0 +1,84 @@
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace Hyperbar;
public class Publisher(ISubscriptionManager subscriptionManager,
IServiceProvider provider,
IDispatcher dispatcher) :
IPublisher
{
public Task PublishAsync<TNotification>(object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification,
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken);
public Task PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
}
public Task PublishAsync<TNotification>(TNotification notification,
object key,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken);
}
public Task PublishAsync(object notification,
Func<Func<Task>, Task> marshal,
object? key = null,
CancellationToken cancellationToken = default)
{
Type notificationType = notification.GetType();
List<object?> handlers = provider.GetServices(typeof(INotificationHandler<>)
.MakeGenericType(notificationType)).ToList();
foreach (object? handler in subscriptionManager.GetHandlers(notificationType,
$"{key?.ToString()}:{notificationType}"))
{
handlers.Add(handler);
}
foreach (object? handler in handlers)
{
if (handler is not null)
{
Type? handlerType = handler.GetType();
MethodInfo? handleMethod = handlerType.GetMethod("Handle",
[notificationType, typeof(CancellationToken)]);
if (handleMethod is not null)
{
marshal(() => (Task)handleMethod.Invoke(handler, new object[] { notification,
cancellationToken })!);
}
}
}
return Task.CompletedTask;
}
public Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
where TNotification :
INotification,
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
public Task PublishAsync(object notification,
CancellationToken cancellationToken = default)
{
return PublishAsync(notification, args => dispatcher.InvokeAsync(async () => await args()),
null, cancellationToken);
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace Hyperbar;
public class Subscriber(ISubscriptionManager subscriptionManager) :
ISubscriber
{
public void Remove(object subscriber) =>
subscriptionManager.Remove(subscriber);
public void Add(object subscriber) =>
subscriptionManager.Add(subscriber);
}
+3
View File
@@ -0,0 +1,3 @@
namespace Hyperbar;
public record Subscription(object Handler);
+90
View File
@@ -0,0 +1,90 @@
using System.Collections.Concurrent;
using System.Reflection;
namespace Hyperbar;
public class SubscriptionManager :
ISubscriptionManager
{
private readonly ConcurrentDictionary<object, List<WeakReference>> subscriptions = new();
public IEnumerable<object?> GetHandlers(Type notificationType, object key)
{
if (subscriptions.TryGetValue($"{key?.ToString()}:{notificationType}",
out List<WeakReference>? subscribers))
{
foreach (WeakReference weakRef in subscribers.ToArray())
{
object? target = weakRef.Target;
if (target != null)
{
yield return target;
}
else
{
subscribers.Remove(weakRef);
}
}
}
}
public void Remove(object subscriber)
{
Type handlerType = subscriber.GetType();
object? key = GetKeyFromHandler(handlerType, subscriber);
foreach (Type interfaceType in GetHandlerInterfaces(handlerType))
{
if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType)
{
if (subscriptions.TryGetValue($"{(key is not null ? $"{key}:" : "")}{argumentType}", out List<WeakReference>? subscribers))
{
for (int i = subscribers.Count - 1; i >= 0; i--)
{
if (!subscribers[i].IsAlive || subscribers[i].Target == subscriber)
{
subscribers.RemoveAt(i);
}
}
}
}
}
}
public void Add(object subscriber)
{
Type handlerType = subscriber.GetType();
object? key = GetKeyFromHandler(handlerType, subscriber);
foreach (Type interfaceType in GetHandlerInterfaces(handlerType))
{
if (interfaceType.GetGenericArguments().FirstOrDefault() is Type argumentType)
{
subscriptions.AddOrUpdate($"{(key is not null ? $"{key}:" : "")}{argumentType}", _ => new List<WeakReference> { new WeakReference(subscriber) }, (_, collection) =>
{
collection.Add(new WeakReference(subscriber));
return collection;
});
}
}
}
private static object? GetKeyFromHandler(Type handlerType, object handler)
{
if (handlerType.GetCustomAttribute<NotificationHandlerAttribute>() is NotificationHandlerAttribute attribute)
{
if (handlerType.GetProperty($"{attribute.Key}") is PropertyInfo property
&& property.GetValue(handler) is { } value)
{
return value;
}
else
{
return attribute.Key;
}
}
return null;
}
private static IEnumerable<Type> GetHandlerInterfaces(Type handlerType) =>
handlerType.GetInterfaces().Where(interfaceType => interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>));
}