add keyed based publication to mediator
This commit is contained in:
@@ -7,6 +7,7 @@ namespace Hyperbar.Windows.Primary;
|
||||
public class PrimaryCommandConfiguration
|
||||
{
|
||||
public List<PrimaryCommandConfiguration> Commands { get; set; } = [];
|
||||
|
||||
public required string Icon { get; set; }
|
||||
|
||||
public required Guid Id { get; set; }
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class PrimaryWidgetConfiguration :
|
||||
List<PrimaryCommandConfiguration>
|
||||
public class PrimaryWidgetConfiguration
|
||||
{
|
||||
public static PrimaryWidgetConfiguration Defaults => new()
|
||||
{
|
||||
new KeyAcceleratorCommandConfiguration { Id = Guid.NewGuid(), Order = 1, Icon = "\uE720", Text = "Test", Key = 91, Modifiers = [] }
|
||||
};
|
||||
public List<PrimaryCommandConfiguration> Commands { get; set; } = [];
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class PrimaryWidgetConfigurationHandler(IMediator mediator,
|
||||
PrimaryWidgetConfiguration configuration,
|
||||
@@ -9,27 +10,53 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator,
|
||||
public async Task Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
HashSet<Guid> configurationIds = new(configuration.SelectMany(item => new[] { item }
|
||||
.Concat(item.Commands).Select(x => x.Id)));
|
||||
List<(Guid ParentId, Guid Id, PrimaryCommandConfiguration Configuration)> items = [];
|
||||
|
||||
foreach (KeyValuePair<Guid, IWidgetComponentViewModel> item in cache.Where(x => !configurationIds.Contains(x.Key)))
|
||||
void AddToItems(Guid parentId, Guid id, List<PrimaryCommandConfiguration> configurations)
|
||||
{
|
||||
if (configurations is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Stack<(Guid, List<PrimaryCommandConfiguration>)> stack = new();
|
||||
stack.Push((parentId, configurations));
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
(Guid currentParentId, List<PrimaryCommandConfiguration> currentConfigurations) = stack.Pop();
|
||||
foreach (PrimaryCommandConfiguration configuration in currentConfigurations)
|
||||
{
|
||||
items.Add((currentParentId, configuration.Id, configuration));
|
||||
if (configuration.Commands is not null && configuration.Commands.Count > 0)
|
||||
{
|
||||
stack.Push((configuration.Id, configuration.Commands));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddToItems(Guid.Empty, Guid.Empty, configuration.Commands);
|
||||
|
||||
foreach (KeyValuePair<Guid, IWidgetComponentViewModel> item in cache
|
||||
.Where(x => !items.Any(k => x.Key == k.Id)))
|
||||
{
|
||||
await mediator.PublishAsync(new Removed<IWidgetComponentViewModel>(item.Value),
|
||||
nameof(PrimaryWidgetViewModel),
|
||||
cancellationToken);
|
||||
|
||||
cache.Remove(item.Key);
|
||||
}
|
||||
|
||||
foreach (PrimaryCommandConfiguration item in configuration)
|
||||
foreach ((Guid ParentId, Guid Id, PrimaryCommandConfiguration Configuration) item in
|
||||
items.Where(x => !cache.Any(k => x.Id == k.Key)))
|
||||
{
|
||||
if (!cache.ContainsKey(item.Id))
|
||||
if (factory.Create(item.Configuration) is IWidgetComponentViewModel viewModel)
|
||||
{
|
||||
if (factory.Create(item) is IWidgetComponentViewModel viewModel)
|
||||
{
|
||||
await mediator.PublishAsync(Inserted<IWidgetComponentViewModel>
|
||||
.For<PrimaryWidgetViewModel>(item.Order, viewModel),
|
||||
await mediator.PublishAsync(new Inserted<IWidgetComponentViewModel>(item.Configuration.Order, viewModel),
|
||||
nameof(PrimaryWidgetViewModel),
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
[NotificationHandler(nameof(PrimaryWidgetViewModel))]
|
||||
public class PrimaryWidgetViewModel(ITemplateFactory templateFactory,
|
||||
IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
|
||||
@@ -6,7 +6,7 @@ public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration confi
|
||||
{
|
||||
public IEnumerable<IWidgetComponentViewModel?> Next()
|
||||
{
|
||||
foreach (PrimaryCommandConfiguration item in configuration.OrderBy(x => x.Order))
|
||||
foreach (PrimaryCommandConfiguration item in configuration.Commands.OrderBy(x => x.Order))
|
||||
{
|
||||
yield return factory.Create(item);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||
if (childViewModel is not null)
|
||||
{
|
||||
childViewModels.Add(childViewModel);
|
||||
cache.Add(childViewModel.Id, childViewModel);
|
||||
cache.Add(childCommandConfiguration.Id, childViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class WidgetComponentViewModelFactory(IServiceFactory service,
|
||||
|
||||
if (viewModel is not null)
|
||||
{
|
||||
cache.Add(viewModel.Id, viewModel);
|
||||
cache.Add(configuration.Id, viewModel);
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
FontSize="16">
|
||||
<SplitButton.Flyout>
|
||||
<Flyout ShouldConstrainToRootBounds="False">
|
||||
<Border
|
||||
Width="300"
|
||||
Height="300"
|
||||
Background="red">
|
||||
<ItemsControl Margin="-16,-13,-16,-15" ItemsSource="{Binding Mode=TwoWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -33,7 +29,6 @@
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
</Flyout>
|
||||
</SplitButton.Flyout>
|
||||
</SplitButton>
|
||||
|
||||
@@ -1,7 +1,25 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class NotificationHandlerAttribute(object key) : Attribute
|
||||
{
|
||||
public object Key { get; } = key;
|
||||
}
|
||||
|
||||
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 :
|
||||
@@ -9,6 +27,7 @@ public interface IMediator
|
||||
|
||||
Task PublishAsync<TNotification>(TNotification notification,
|
||||
Func<Func<Task>, Task> marshal,
|
||||
object? key = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
where TNotification :
|
||||
INotification;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
@@ -7,18 +8,37 @@ public class Mediator(IServiceProvider provider,
|
||||
IDispatcher dispatcher) :
|
||||
IMediator
|
||||
{
|
||||
private readonly ConcurrentDictionary<Type, List<dynamic>> subjects = [];
|
||||
private readonly ConcurrentDictionary<object, List<dynamic>> subjects = [];
|
||||
|
||||
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()), cancellationToken);
|
||||
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<TNotification>(TNotification notification,
|
||||
Func<Func<Task>, Task> marshal,
|
||||
object? key = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
where TNotification :
|
||||
INotification
|
||||
@@ -26,14 +46,11 @@ public class Mediator(IServiceProvider provider,
|
||||
List<INotificationHandler<TNotification>> handlers =
|
||||
provider.GetServices<INotificationHandler<TNotification>>().ToList();
|
||||
|
||||
foreach (KeyValuePair<Type, List<dynamic>> handler in subjects)
|
||||
foreach (KeyValuePair<object, List<dynamic>> handler in subjects)
|
||||
{
|
||||
if (handler.Key == typeof(TNotification))
|
||||
if (key is not null && handler.Key.Equals(key) || handler.Key.Equals(typeof(TNotification)))
|
||||
{
|
||||
foreach (dynamic value in handler.Value)
|
||||
{
|
||||
handlers.Add(value);
|
||||
}
|
||||
handlers.Add(handler.Value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +65,8 @@ public class Mediator(IServiceProvider provider,
|
||||
public Task PublishAsync<TNotification>(CancellationToken cancellationToken = default)
|
||||
where TNotification :
|
||||
INotification,
|
||||
new() => PublishAsync(new TNotification(), null, cancellationToken);
|
||||
new() => PublishAsync(new TNotification(), args => dispatcher.InvokeAsync(async () => await args()),
|
||||
null, cancellationToken);
|
||||
|
||||
public Task<TResponse?> SendAsync<TResponse>(IRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
@@ -88,15 +106,32 @@ public class Mediator(IServiceProvider provider,
|
||||
|
||||
public void Subscribe(object handler)
|
||||
{
|
||||
Type[] interfaceTypes = handler.GetType().GetInterfaces();
|
||||
foreach (Type interfaceType in interfaceTypes.Where(x => x.IsGenericType))
|
||||
Type handlerType = handler.GetType();
|
||||
object? key = null;
|
||||
|
||||
if (Attribute.GetCustomAttribute(handlerType, typeof(NotificationHandlerAttribute))
|
||||
is NotificationHandlerAttribute attribute)
|
||||
{
|
||||
if (interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>))
|
||||
if (handlerType.GetProperty($"{attribute.Key}") is PropertyInfo property)
|
||||
{
|
||||
if (property.GetValue(handler, null) is { } value)
|
||||
{
|
||||
key = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key = attribute.Key;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Type interfaceType in handlerType.GetInterfaces().Where(x => x.IsGenericType
|
||||
&& x.GetGenericTypeDefinition() == typeof(INotificationHandler<>)))
|
||||
{
|
||||
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
|
||||
{
|
||||
Type notificationType = arguments[0];
|
||||
subjects.AddOrUpdate(notificationType, [handler], (value, collection) =>
|
||||
key ??= arguments[0];
|
||||
subjects.AddOrUpdate(key, [handler], (value, collection) =>
|
||||
{
|
||||
collection.Add(handler);
|
||||
return collection;
|
||||
@@ -105,4 +140,3 @@ public class Mediator(IServiceProvider provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,3 @@
|
||||
namespace Hyperbar;
|
||||
|
||||
public record Inserted<TValue>(int Index, TValue Value, object Target) : INotification
|
||||
{
|
||||
public static Inserted<TValue> For<TTarget>(int index, TValue value)
|
||||
{
|
||||
return new Inserted<TValue>(index, value, typeof(TTarget).Name);
|
||||
}
|
||||
}
|
||||
public record Inserted<TValue>(int Index, TValue Value) : INotification;
|
||||
@@ -228,28 +228,22 @@ public partial class ObservableCollectionViewModel<TItem> :
|
||||
|
||||
public Task Handle(Created<TItem> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (notification.Target.Equals(GetType().Name))
|
||||
{
|
||||
if (notification.Value is TItem item)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(Inserted<TItem> notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (notification.Target.Equals(GetType().Name))
|
||||
{
|
||||
if (notification.Value is TItem item)
|
||||
{
|
||||
Insert(notification.Index, item);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@ using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
[NotificationHandler(nameof(Id))]
|
||||
public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid guid = default,
|
||||
Guid id,
|
||||
string? text = null,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
@@ -18,6 +19,9 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
[ObservableProperty]
|
||||
private Guid id = id;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
@@ -7,29 +7,20 @@ public partial class WidgetComponentViewModel :
|
||||
IWidgetComponentViewModel,
|
||||
ITemplatedViewModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
private Guid id;
|
||||
|
||||
public WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
IEnumerable<IWidgetComponentViewModel> items,
|
||||
Guid id = default) : base(serviceFactory, mediator, disposer, items)
|
||||
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, disposer, items)
|
||||
{
|
||||
this.id = id;
|
||||
|
||||
TemplateFactory = templateFactory;
|
||||
}
|
||||
|
||||
public WidgetComponentViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid id = default) : base(serviceFactory, mediator, disposer)
|
||||
ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer)
|
||||
{
|
||||
this.id = id;
|
||||
|
||||
TemplateFactory = templateFactory;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,15 @@ using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
[NotificationHandler(nameof(Id))]
|
||||
public partial class WidgetMenuViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
Guid guid = default,
|
||||
Guid id = default,
|
||||
string? text = null,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, guid)
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
@@ -18,6 +19,9 @@ public partial class WidgetMenuViewModel(IServiceFactory serviceFactory,
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
[ObservableProperty]
|
||||
private Guid id = id;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
@@ -3,15 +3,16 @@ using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Hyperbar;
|
||||
|
||||
[NotificationHandler(nameof(Id))]
|
||||
public partial class WidgetSplitButtonViewModel(IServiceFactory serviceFactory,
|
||||
IMediator mediator,
|
||||
IDisposer disposer,
|
||||
ITemplateFactory templateFactory,
|
||||
IEnumerable<IWidgetComponentViewModel> items,
|
||||
Guid guid = default,
|
||||
Guid id = default,
|
||||
string? text = null,
|
||||
string? icon = null,
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, items, guid)
|
||||
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, items)
|
||||
{
|
||||
[ObservableProperty]
|
||||
private IRelayCommand? click = command;
|
||||
@@ -19,6 +20,9 @@ public partial class WidgetSplitButtonViewModel(IServiceFactory serviceFactory,
|
||||
[ObservableProperty]
|
||||
private string? icon = icon;
|
||||
|
||||
[ObservableProperty]
|
||||
private Guid id = id;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? text = text;
|
||||
}
|
||||
Reference in New Issue
Block a user