Added messaging pipeline

This commit is contained in:
TheXamlGuy
2024-01-06 13:57:18 +00:00
parent 4a27534e39
commit 53537aa4c7
60 changed files with 609 additions and 141 deletions
@@ -1,15 +1,11 @@
using Hyperbar.Lifecycles;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar.Extensions.Contextual; namespace Hyperbar.Widget.Contextual;
public class ContextualWidgetBuilder : public class ContextualWidgetBuilder :
IWidgetBuilder IWidgetBuilder
{ {
public void Create(IServiceCollection services) public void Create(IServiceCollection services) => services
{
services
.AddConfiguration<ContextualWidgetConfiguration>() .AddConfiguration<ContextualWidgetConfiguration>()
.AddWidgetTemplate<ContextualWidgetViewModel>(); .AddWidgetTemplate<ContextualWidgetViewModel>();
}
} }
@@ -1,4 +1,4 @@
namespace Hyperbar.Extensions.Contextual; namespace Hyperbar.Widget.Contextual;
public class ContextualWidgetConfiguration public class ContextualWidgetConfiguration
{ {
@@ -1,11 +1,12 @@
using Hyperbar.Lifecycles; namespace Hyperbar.Widget.Contextual;
using Hyperbar.Templates;
namespace Hyperbar.Extensions.Contextual; public class ContextualWidgetViewModel :
WidgetViewModelBase
public class ContextualWidgetViewModel(ITemplateFactory templateFactory) :
IWidgetViewModel,
ITemplatedViewModel
{ {
public ITemplateFactory TemplateFactory { get; } = templateFactory; public ContextualWidgetViewModel(ITemplateFactory templateFactory,
IServiceFactory serviceFactory) : base(templateFactory, serviceFactory)
{
Add<WidgetButtonViewModel>();
Add<WidgetButtonViewModel>();
}
} }
@@ -1,21 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>Hyperbar.Windows.Contextual</RootNamespace>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<UseRidGraph>true</UseRidGraph> <UseRidGraph>true</UseRidGraph>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25936-preview" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Hyperbar.Windows.Win32\Hyperbar.Windows.Win32.csproj" />
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" /> <ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,4 +1,3 @@
using Hyperbar.Lifecycles;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.Primary;
@@ -6,10 +5,7 @@ namespace Hyperbar.Windows.Primary;
public class PrimaryWidgetBuilder : public class PrimaryWidgetBuilder :
IWidgetBuilder IWidgetBuilder
{ {
public void Create(IServiceCollection services) public void Create(IServiceCollection services) => services.AddConfiguration<PrimaryWidgetConfiguration>()
{ .AddWidgetTemplate<PrimaryWidgetViewModel>();
services.AddConfiguration<PrimaryWidgetConfiguration>()
.AddTransient<PrimaryWidgetViewModel>();
}
} }
@@ -1,14 +1,19 @@
using Hyperbar.Lifecycles; namespace Hyperbar.Windows.Primary;
using Hyperbar.Templates;
namespace Hyperbar.Windows.Primary;
public class PrimaryWidgetViewModel : public class PrimaryWidgetViewModel :
WidgetViewModelBase WidgetViewModelBase
{ {
public PrimaryWidgetViewModel(ITemplateFactory templateFactory) : base(templateFactory) public PrimaryWidgetViewModel(ITemplateFactory templateFactory,
IServiceFactory serviceFactory) : base(templateFactory, serviceFactory)
{ {
;
Add<WidgetButtonViewModel>("test 1", new Action(() => {
}));
Add<WidgetButtonViewModel>("test 2", new Action(() => { }));
Add<WidgetButtonViewModel>("test 4", new Action(() => { }));
Add<WidgetButtonViewModel>("test 5", new Action(() => { }));
} }
} }
+12 -11
View File
@@ -1,10 +1,10 @@
using Hyperbar.Windows.Controls; using Hyperbar.Windows.Controls;
using Hyperbar.Lifecycles;
using Hyperbar.Templates;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Hyperbar.Widget.Contextual;
using Hyperbar.Windows.Primary;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
@@ -28,29 +28,30 @@ public partial class App :
}) })
.ConfigureServices((context, services) => .ConfigureServices((context, services) =>
{ {
services.AddSingleton<IServiceFactory>(provider =>
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
services.AddHostedService<AppService>(); services.AddHostedService<AppService>();
services.AddTransient<IInitializer, AppInitializer>(); services.AddTransient<IInitializer, AppInitializer>();
services.AddTransient<DesktopFlyout>();
services.AddTransient<ITemplateFactory, TemplateFactory>(); services.AddTransient<ITemplateFactory, TemplateFactory>();
services.AddTransient<ITemplateGeneratorFactory, TemplateGeneratorFactory>(); services.AddTransient<ITemplateGeneratorFactory, TemplateGeneratorFactory>();
services.AddTransient<DesktopFlyout>();
services.AddContentTemplate<CommandViewModel, CommandView>(); services.AddContentTemplate<CommandViewModel, CommandView>();
//services.AddCommand<ContextualCommandWidgetBuilder>(""); services.AddWidget<ContextualWidgetBuilder>();
//services.AddWidget<PrimaryCommandWidgetBuilder>(""); services.AddWidget<PrimaryWidgetBuilder>();
services.AddTransient(provider => services.AddTransient(provider =>
{ {
static IEnumerable<IWidgetViewModel> Resolve(IServiceProvider services) static IEnumerable<IWidgetViewModel> Resolve(IServiceProvider services)
{ {
foreach (IWidgetContext commandContext in services.GetServices<IWidgetContext>()) foreach (IWidgetContext widgetContext in services.GetServices<IWidgetContext>())
{ {
if (commandContext.ServiceProvider.GetService<IWidgetViewModel>() is if (widgetContext.ServiceProvider.GetService<IWidgetViewModel>() is
IWidgetViewModel commandViewModel) IWidgetViewModel viewModel)
{ {
yield return commandViewModel; yield return viewModel;
} }
} }
} }
+8
View File
@@ -14,6 +14,9 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="Views\WidgetButtonView.xaml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" /> <Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" /> <Content Include="Assets\LockScreenLogo.scale-200.png" />
@@ -38,6 +41,11 @@
<ProjectReference Include="..\Hyperbar.Windows.Primary\Hyperbar.Widget.Primary.csproj" /> <ProjectReference Include="..\Hyperbar.Windows.Primary\Hyperbar.Widget.Primary.csproj" />
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" /> <ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Views\WidgetButtonView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> <PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu> <HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup> </PropertyGroup>
@@ -1,7 +1,5 @@
using Hyperbar.Windows.Controls; using Hyperbar.Windows.Controls;
using Hyperbar.Lifecycles;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
@@ -1,14 +1,11 @@
using Hyperbar.Lifecycles; using Microsoft.Extensions.DependencyInjection;
using Hyperbar.Templates;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace Hyperbar.Windows namespace Hyperbar.Windows
{ {
public static class IServiceCollectionExtensions public static class IServiceCollectionExtensions
{ {
public static IServiceCollection AddWidget<TCommandBuilder>(this IServiceCollection services, public static IServiceCollection AddWidget<TCommandBuilder>(this IServiceCollection services)
string key)
where TCommandBuilder : where TCommandBuilder :
IWidgetBuilder, new() IWidgetBuilder, new()
{ {
@@ -16,6 +13,12 @@ namespace Hyperbar.Windows
IHost? host = new HostBuilder() IHost? host = new HostBuilder()
.ConfigureServices(isolatedServices => .ConfigureServices(isolatedServices =>
{ {
isolatedServices.AddSingleton<IServiceFactory>(provider =>
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
isolatedServices.AddTransient<IWidgetView, WidgetView>();
isolatedServices.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>();
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>(); isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
isolatedServices.AddTransient<ITemplateGeneratorFactory, TemplateGeneratorFactory>(); isolatedServices.AddTransient<ITemplateGeneratorFactory, TemplateGeneratorFactory>();
@@ -1,10 +1,6 @@
using Hyperbar.Templates; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
@@ -1,5 +1,4 @@
using Hyperbar.Templates; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
+5 -8
View File
@@ -1,18 +1,15 @@
using Hyperbar.Lifecycles; namespace Hyperbar.Windows;
using Hyperbar.Templates;
using System.Collections.Generic;
namespace Hyperbar.Windows;
public partial class CommandViewModel : public partial class CommandViewModel :
ObservableCollectionViewModel, ObservableCollectionViewModel<IWidgetViewModel>,
ITemplatedViewModel ITemplatedViewModel
{ {
public CommandViewModel(ITemplateFactory templateFactory, public CommandViewModel(ITemplateFactory templateFactory,
IEnumerable<IWidgetViewModel> commands) IServiceFactory serviceFactory,
IEnumerable<IWidgetViewModel> widgets) : base(serviceFactory)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
AddRange(commands); AddRange(widgets);
} }
public ITemplateFactory TemplateFactory { get; } public ITemplateFactory TemplateFactory { get; }
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="Hyperbar.Windows.WidgetButtonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<SolidColorBrush x:Key="ButtonBackground" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrushPointerOver" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrushPressed" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrushDisabled" Color="Transparent" />
<x:Double x:Key="ButtonWidth">32</x:Double>
<x:Double x:Key="ButtonHeight">32</x:Double>
</UserControl.Resources>
<Button
Width="{StaticResource ButtonWidth}"
Height="{StaticResource ButtonHeight}"
Command="{Binding Click}"
Content="{Binding Icon}" />
</UserControl>
@@ -0,0 +1,9 @@
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows;
public sealed partial class WidgetButtonView :
UserControl
{
public WidgetButtonView() => InitializeComponent();
}
+7 -1
View File
@@ -3,5 +3,11 @@
x:Class="Hyperbar.Windows.WidgetView" x:Class="Hyperbar.Windows.WidgetView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid /> <ItemsControl ItemTemplateSelector="{Binding TemplateFactory}" ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</UserControl> </UserControl>
@@ -2,7 +2,7 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Hyperbar.Options; namespace Hyperbar;
public class ConfigurationWriter<TConfiguration>(string path, public class ConfigurationWriter<TConfiguration>(string path,
string section, string section,
@@ -1,12 +1,10 @@
namespace Hyperbar;
namespace Hyperbar.Options
{ public interface IConfigurationWriter<TConfiguration>
public interface IConfigurationWriter<TConfiguration>
where TConfiguration : where TConfiguration :
class, new() class, new()
{ {
void Write(Action<TConfiguration> updateDelegate); void Write(Action<TConfiguration> updateDelegate);
void Write(TConfiguration value); void Write(TConfiguration value);
}
} }
@@ -1,5 +1,4 @@
using Hyperbar.Options; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Hyperbar; namespace Hyperbar;
@@ -1,7 +1,4 @@
using Hyperbar.Lifecycles; using Microsoft.Extensions.DependencyInjection;
using Hyperbar.Options;
using Hyperbar.Templates;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@@ -67,10 +64,10 @@ public static class IServiceCollectionExtensions
string key = contentType.Name; string key = contentType.Name;
services.AddTransient(typeof(IWidgetViewModel), contentType); services.AddTransient(typeof(IWidgetViewModel), contentType);
services.TryAddTransient(provider => provider.GetService<IWidgetView>()!); services.TryAddTransient(templateType, provider => provider.GetService<IWidgetView>()!);
services.AddKeyedTransient(typeof(IWidgetViewModel), key, contentType); services.AddKeyedTransient(typeof(IWidgetViewModel), key, contentType);
services.TryAddKeyedTransient(templateType, key); services.TryAddKeyedTransient<IWidgetView>(key, (provider, key) => provider.GetService<IWidgetView>()!);
services.AddTransient<IContentTemplateDescriptor>(provider => new ContentTemplateDescriptor services.AddTransient<IContentTemplateDescriptor>(provider => new ContentTemplateDescriptor
{ {
+6
View File
@@ -0,0 +1,6 @@
namespace Hyperbar;
public interface IServiceFactory
{
TService Create<TService>(params object?[] parameters);
}
+8
View File
@@ -0,0 +1,8 @@
namespace Hyperbar;
public class ServiceFactory(Func<Type, object?[], object> factory) :
IServiceFactory
{
public TService Create<TService>(params object?[] parameters) =>
(TService)factory(typeof(TService), parameters);
}
+1
View File
@@ -5,6 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+1 -1
View File
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public class AppService(IEnumerable<IInitializer> initializers) : public class AppService(IEnumerable<IInitializer> initializers) :
IHostedService IHostedService
+1 -1
View File
@@ -1,4 +1,4 @@
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public interface IInitializer public interface IInitializer
{ {
+1 -1
View File
@@ -1,6 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public interface IWidgetBuilder public interface IWidgetBuilder
{ {
+1 -1
View File
@@ -1,4 +1,4 @@
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public interface IWidgetContext public interface IWidgetContext
{ {
@@ -1,21 +0,0 @@
using System.Collections.ObjectModel;
namespace Hyperbar.Lifecycles;
public class ObservableCollectionViewModel<TItem> :
ObservableCollection<TItem>
{
public void AddRange(IEnumerable<TItem> collection)
{
foreach (TItem? item in collection)
{
Add(item);
}
}
}
public class ObservableCollectionViewModel :
ObservableCollectionViewModel<object>
{
}
@@ -1,6 +0,0 @@
namespace Hyperbar.Lifecycles;
public class WidgetButtonViewModel
{
}
+1 -1
View File
@@ -1,4 +1,4 @@
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public class WidgetContext(IServiceProvider serviceProvider) : public class WidgetContext(IServiceProvider serviceProvider) :
IWidgetContext IWidgetContext
@@ -1,11 +0,0 @@
using Hyperbar.Templates;
namespace Hyperbar.Lifecycles;
public class WidgetViewModelBase(ITemplateFactory templateFactory) :
ObservableCollectionViewModel<IWidgetComponentViewModel>,
IWidgetViewModel,
ITemplatedViewModel
{
public ITemplateFactory TemplateFactory { get; } = templateFactory;
}
@@ -0,0 +1,27 @@
namespace Hyperbar;
public class CommandClassHandlerWrapper<TRequest, TResponse>
where TRequest :
class,
ICommand<TResponse>
{
private readonly MessageHandlerDelegate<TRequest, TResponse> handler;
public CommandClassHandlerWrapper(ICommandHandler<TRequest, TResponse> concreteHandler,
IEnumerable<IPipelineBehavior<TRequest, TResponse>> pipelineBehaviours)
{
MessageHandlerDelegate<TRequest, TResponse> handler = concreteHandler.Handle;
foreach (IPipelineBehavior<TRequest, TResponse>? pipeline in pipelineBehaviours.Reverse())
{
MessageHandlerDelegate<TRequest, TResponse> handlerCopy = handler;
IPipelineBehavior<TRequest, TResponse> pipelineCopy = pipeline;
handler = (TRequest message, CancellationToken cancellationToken) =>
pipelineCopy.Handle(message, handlerCopy, cancellationToken);
}
this.handler = handler;
}
public ValueTask<TResponse> Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken);
}
+6
View File
@@ -0,0 +1,6 @@
namespace Hyperbar;
public interface ICommand : ICommand<Unit>;
public interface ICommand<out TResponse> : IMessage;
+13
View File
@@ -0,0 +1,13 @@
namespace Hyperbar;
public interface ICommandHandler<in TCommand> : ICommandHandler<TCommand, Unit>
where TCommand :
ICommand<Unit>;
public interface ICommandHandler<in TCommand, TResponse>
where TCommand :
ICommand<TResponse>
{
ValueTask<TResponse> Handle(TCommand command,
CancellationToken cancellationToken);
}
+24
View File
@@ -0,0 +1,24 @@
namespace Hyperbar;
public interface IMediator
{
ValueTask Publish<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification;
ValueTask<TResponse> Send<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default);
ValueTask<TResponse> Send<TResponse>(ICommand<TResponse> command,
CancellationToken cancellationToken = default);
ValueTask<TResponse> Send<TResponse>(IQuery<TResponse> query,
CancellationToken cancellationToken = default);
ValueTask<object?> Send(object message, CancellationToken
cancellationToken = default);
void Subscribe(object subscriber);
}
+4
View File
@@ -0,0 +1,4 @@
namespace Hyperbar;
public interface IMessage;
+4
View File
@@ -0,0 +1,4 @@
namespace Hyperbar;
public interface INotification : IMessage;
@@ -0,0 +1,10 @@
namespace Hyperbar;
public interface INotificationHandler<in TNotification>
where TNotification :
INotification
{
ValueTask Handle(TNotification notification,
CancellationToken cancellationToken);
}
+12
View File
@@ -0,0 +1,12 @@
namespace Hyperbar;
public interface IPipelineBehavior<TMessage, TResponse>
where TMessage :
notnull,
IMessage
{
ValueTask<TResponse> Handle(TMessage message,
MessageHandlerDelegate<TMessage, TResponse> next,
CancellationToken cancellationToken = default);
}
+3
View File
@@ -0,0 +1,3 @@
namespace Hyperbar;
public interface IQuery<out TResponse> : IMessage;
+9
View File
@@ -0,0 +1,9 @@
namespace Hyperbar;
public interface IQueryHandler<in TQuery, TResponse>
where TQuery :
IQuery<TResponse>
{
ValueTask<TResponse> Handle(TQuery query,
CancellationToken cancellationToken);
}
+5
View File
@@ -0,0 +1,5 @@
namespace Hyperbar;
public interface IRequest<out TResponse> : IMessage;
public interface IRequest : IRequest<Unit>;
+17
View File
@@ -0,0 +1,17 @@
namespace Hyperbar;
public interface IRequestHandler<in TRequest, TResponse>
where TRequest :
IRequest<TResponse>
{
ValueTask<TResponse> Handle(TRequest request,
CancellationToken cancellationToken);
}
public interface IRequestHandler<in TRequest> :
IRequestHandler<TRequest, Unit>
where TRequest :
IRequest<Unit>
{
}
+149
View File
@@ -0,0 +1,149 @@
using Microsoft.Extensions.DependencyInjection;
using System.Runtime.CompilerServices;
namespace Hyperbar;
public class Mediator(IServiceProvider provider) :
IMediator
{
private readonly ConditionalWeakTable<Type, dynamic> handlers = [];
public ValueTask Publish<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification
{
List<INotificationHandler<TNotification>> handlers =
provider.GetServices<INotificationHandler<TNotification>>().ToList();
foreach (KeyValuePair<Type, dynamic> handler in this.handlers)
{
if (handler.Key == typeof(TNotification))
{
handlers.Add(handler.Value);
}
}
if (handlers.Count == 0)
{
return default;
}
else if (handlers.Count == 1)
{
return handlers[0].Handle(notification, cancellationToken);
}
return default;
}
public ValueTask<TResponse> Send<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
dynamic? handler = provider.GetService(typeof(RequestClassHandlerWrapper<,>)
.MakeGenericType(request.GetType(), typeof(TResponse)));
if (handler is not null)
{
return handler.Handle((dynamic)request, cancellationToken);
}
return default;
}
public ValueTask<TResponse> Send<TResponse>(ICommand<TResponse> command,
CancellationToken cancellationToken = default)
{
dynamic? handler = provider.GetService(typeof(CommandClassHandlerWrapper<,>)
.MakeGenericType(command.GetType(), typeof(TResponse)));
if (handler is not null)
{
return handler.Handle((dynamic)command, cancellationToken);
}
return default;
}
public ValueTask<TResponse> Send<TResponse>(IQuery<TResponse> query,
CancellationToken cancellationToken = default)
{
dynamic? handler = provider.GetService(typeof(QueryClassHandlerWrapper<,>)
.MakeGenericType(query.GetType(), typeof(TResponse)));
if (handler is not null)
{
return handler.Handle((dynamic)query, cancellationToken);
}
return default;
}
public ValueTask<object?> Send(object message,
CancellationToken cancellationToken = default)
{
if (message.GetType().GetInterface(typeof(IRequest<>).Name) is { } requestType)
{
if (requestType.GetGenericArguments() is { Length: 1 } arguments)
{
Type responseType = arguments[0];
dynamic? handler = provider.GetService(typeof(RequestClassHandlerWrapper<,>)
.MakeGenericType(message.GetType(), responseType));
if (handler is not null)
{
return handler.Handle((dynamic)message, cancellationToken);
}
}
}
if (message.GetType().GetInterface(typeof(ICommand<>).Name) is { } commandType)
{
if (commandType.GetGenericArguments() is { Length: 1 } arguments)
{
Type responseType = arguments[0];
dynamic? handler = provider.GetService(typeof(CommandClassHandlerWrapper<,>)
.MakeGenericType(message.GetType(), responseType));
if (handler is not null)
{
return handler.Handle((dynamic)message, cancellationToken);
}
}
}
if (message.GetType().GetInterface(typeof(IQuery<>).Name) is { } queryType)
{
if (queryType.GetGenericArguments() is { Length: 1 } arguments)
{
Type responseType = arguments[0];
dynamic? handler = provider.GetService(typeof(QueryClassHandlerWrapper<,>)
.MakeGenericType(message.GetType(), responseType));
if (handler is not null)
{
return handler.Handle((dynamic)message, cancellationToken);
}
}
}
return default;
}
public void Subscribe(object subject)
{
Type[] interfaceTypes = subject.GetType().GetInterfaces();
foreach (Type interfaceType in interfaceTypes.Where(x => x.IsGenericType))
{
if (interfaceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>))
{
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
{
Type notificationType = arguments[0];
handlers.Add(notificationType, subject);
}
}
}
}
}
@@ -0,0 +1,7 @@
namespace Hyperbar;
public delegate ValueTask<TResponse> MessageHandlerDelegate<TMessage, TResponse>(TMessage message,
CancellationToken cancellationToken)
where TMessage :
notnull,
IMessage;
@@ -0,0 +1,28 @@
namespace Hyperbar;
public class QueryClassHandlerWrapper<TRequest, TResponse>
where TRequest :
class,
IQuery<TResponse>
{
private readonly MessageHandlerDelegate<TRequest, TResponse> handler;
public QueryClassHandlerWrapper(IQueryHandler<TRequest, TResponse> concreteHandler,
IEnumerable<IPipelineBehavior<TRequest, TResponse>> pipelineBehaviours)
{
MessageHandlerDelegate<TRequest, TResponse> handler = concreteHandler.Handle;
foreach (IPipelineBehavior<TRequest, TResponse>? pipeline in pipelineBehaviours.Reverse())
{
MessageHandlerDelegate<TRequest, TResponse> handlerCopy = handler;
IPipelineBehavior<TRequest, TResponse> pipelineCopy = pipeline;
handler = (TRequest message, CancellationToken cancellationToken) =>
pipelineCopy.Handle(message, handlerCopy, cancellationToken);
}
this.handler = handler;
}
public ValueTask<TResponse> Handle(TRequest request, CancellationToken cancellationToken) =>
handler(request, cancellationToken);
}
@@ -0,0 +1,27 @@
namespace Hyperbar;
public class RequestClassHandlerWrapper<TRequest, TResponse>
where TRequest :
class,
IRequest<TResponse>
{
private readonly MessageHandlerDelegate<TRequest, TResponse> handler;
public RequestClassHandlerWrapper(IRequestHandler<TRequest, TResponse> concreteHandler,
IEnumerable<IPipelineBehavior<TRequest, TResponse>> pipelineBehaviours)
{
MessageHandlerDelegate<TRequest, TResponse> handler = concreteHandler.Handle;
foreach (IPipelineBehavior<TRequest, TResponse>? pipeline in pipelineBehaviours.Reverse())
{
MessageHandlerDelegate<TRequest, TResponse> handlerCopy = handler;
IPipelineBehavior<TRequest, TResponse> pipelineCopy = pipeline;
handler = (TRequest message, CancellationToken cancellationToken) =>
pipelineCopy.Handle(message, handlerCopy, cancellationToken);
}
this.handler = handler;
}
public ValueTask<TResponse> Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken);
}
+30
View File
@@ -0,0 +1,30 @@
namespace Hyperbar;
public readonly struct Unit :
IEquatable<Unit>,
IComparable<Unit>,
IComparable
{
private static readonly Unit value = new();
public static ref readonly Unit Value => ref value;
public static ValueTask<Unit> ValueTask => new(value);
public int CompareTo(Unit other) => 0;
int IComparable.CompareTo(object? obj) => 0;
public override int GetHashCode() => 0;
public bool Equals(Unit other) => true;
public override bool Equals(object? obj) => obj is Unit;
public static bool operator ==(Unit _, Unit __) => true;
public static bool operator !=(Unit _, Unit __) => false;
public override string ToString() => "()";
}
@@ -1,4 +1,4 @@
namespace Hyperbar.Templates; namespace Hyperbar;
public record ContentTemplateDescriptor : public record ContentTemplateDescriptor :
IContentTemplateDescriptor IContentTemplateDescriptor
@@ -1,4 +1,4 @@
namespace Hyperbar.Templates; namespace Hyperbar;
public interface IContentTemplateDescriptor public interface IContentTemplateDescriptor
{ {
+1 -1
View File
@@ -1,4 +1,4 @@
namespace Hyperbar.Templates; namespace Hyperbar;
public interface ITemplateFactory public interface ITemplateFactory
{ {
+1 -1
View File
@@ -1,4 +1,4 @@
namespace Hyperbar.Templates; namespace Hyperbar;
public interface ITemplatedViewModel public interface ITemplatedViewModel
{ {
@@ -1,4 +1,4 @@
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public interface IWidgetComponentViewModel public interface IWidgetComponentViewModel
{ {
@@ -1,4 +1,4 @@
namespace Hyperbar.Lifecycles; namespace Hyperbar;
public interface IWidgetViewModel public interface IWidgetViewModel
{ {
@@ -0,0 +1,48 @@
using System.Collections.ObjectModel;
namespace Hyperbar;
public class ObservableCollectionViewModel<TItem>(IServiceFactory serviceFactory) :
ObservableCollection<TItem>
{
public TItem Add()
{
TItem? item = serviceFactory.Create<TItem>();
Add(item);
return item;
}
public TItem Add<T>(params object?[] parameters)
where T : TItem
{
T? item = serviceFactory.Create<T>(parameters);
Add(item);
return item;
}
public TItem Add<T>()
where T :
TItem
{
T? item = serviceFactory.Create<T>();
Add(item);
return item;
}
public void AddRange(IEnumerable<TItem> items)
{
foreach (TItem? item in items)
{
Add(item);
}
}
}
public class ObservableCollectionViewModel(IServiceFactory serviceFactory) :
ObservableCollectionViewModel<object>(serviceFactory)
{
}
+25
View File
@@ -0,0 +1,25 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace Hyperbar;
public partial class WidgetButtonViewModel :
WidgetComponentViewModelBase
{
[ObservableProperty]
private string? icon;
[ObservableProperty]
private IRelayCommand? click;
public WidgetButtonViewModel(ITemplateFactory templateFactory,
string? icon = null,
Action? action = null) : base(templateFactory)
{
this.icon = icon;
if (action is not null)
{
click = new RelayCommand(action);
}
}
}
@@ -0,0 +1,11 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Hyperbar;
public partial class WidgetComponentViewModelBase(ITemplateFactory templateFactory) :
ObservableObject,
IWidgetComponentViewModel,
ITemplatedViewModel
{
public ITemplateFactory TemplateFactory => templateFactory;
}
+10
View File
@@ -0,0 +1,10 @@
namespace Hyperbar;
public class WidgetViewModelBase(ITemplateFactory templateFactory,
IServiceFactory serviceFactory) :
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory),
IWidgetViewModel,
ITemplatedViewModel
{
public ITemplateFactory TemplateFactory => templateFactory;
}