bunch ov fixes

This commit is contained in:
TheXamlGuy
2024-01-28 14:57:56 +00:00
parent 9f6cc35bc1
commit 6d40220412
28 changed files with 238 additions and 249 deletions
+25 -11
View File
@@ -1,4 +1,7 @@
using System.Runtime.CompilerServices; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls.Primitives;
using System.Reflection;
using System.Runtime.CompilerServices;
using WinRT; using WinRT;
namespace Hyperbar.UI.Windows; namespace Hyperbar.UI.Windows;
@@ -6,19 +9,30 @@ namespace Hyperbar.UI.Windows;
public static class IWinRTObjectExtensions public static class IWinRTObjectExtensions
{ {
public static void InitializeComponent<TComponent>(this TComponent component, public static void InitializeComponent<TComponent>(this TComponent component,
ref bool loaded, ref bool loaded, [CallerFilePath] string path = "")
[CallerFilePath] string path = "") where TComponent : IWinRTObject
where TComponent :
IWinRTObject
{ {
if (loaded) if (!loaded)
{ {
return;
}
loaded = true; loaded = true;
//Uri resourceLocator = ApplicationExtensionHost.Current.LocateResource(component, callerFilePath); Type type = component.GetType();
//Application.LoadComponent(component, resourceLocator, ComponentResourceLocation.Nested); if (type.Assembly is Assembly assembly && Path.GetDirectoryName(assembly.Location) is string assemblyDirectory)
{
string resourceName = Path.GetFileNameWithoutExtension(path);
string[] pathParts = path.Split(Path.DirectorySeparatorChar)[..^1];
string? resourcePath = pathParts
.Reverse()
.Select(part => Path.Combine(assemblyDirectory, part, resourceName))
.FirstOrDefault(File.Exists);
if (resourcePath is not null)
{
Application.LoadComponent(component, new Uri($"ms-appx:///{resourcePath.Replace('\\', '/')}"),
ComponentResourceLocation.Nested);
}
}
}
} }
} }
@@ -1,3 +1,3 @@
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public record Foward : INotification; public record Forward : INotification;
@@ -1,9 +1,11 @@
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Hyperbar.UI.Windows;
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public sealed partial class MediaButtonView : public sealed partial class MediaButtonView :
UserControl UserControl
{ {
public MediaButtonView() => InitializeComponent(); public MediaButtonView() =>
this.InitializeComponent(ref _contentLoaded);
} }
@@ -1,21 +1,47 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Hyperbar.Widget;
using System.Windows.Input;
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public class MediaButtonViewModel(IServiceFactory serviceFactory, public class MediaButtonViewModel :
WidgetButtonViewModel,
IInitialization,
INotificationHandler<Changed<PlaybackInformation>>
{
private readonly PlaybackButtonType buttonType;
public MediaButtonViewModel(IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, ITemplateFactory templateFactory,
PlaybackButtonType buttonType,
Guid guid = default, Guid guid = default,
string? text = null, string? text = null,
string? icon = null, string? icon = null,
RelayCommand? command = null) : RelayCommand? command = null) : base (serviceFactory, mediator, disposer, templateFactory, guid, text, icon, command)
WidgetButtonViewModel(serviceFactory, mediator, disposer, templateFactory, guid, text, icon, command), {
IInitialization this.buttonType = buttonType;
{ mediator.Subscribe(this);
public ICommand Initialize => new AsyncRelayCommand(InitializeAsync); }
public async Task InitializeAsync() => await Mediator.PublishAsync<Request<Playback>>(); public Task Handle(Changed<PlaybackInformation> notification,
CancellationToken cancellationToken)
{
if (notification.Value is PlaybackInformation information)
{
switch (buttonType)
{
case PlaybackButtonType.Play:
Visible = information.Status is PlaybackStatus.Playing;
break;
case PlaybackButtonType.Pause:
Visible = information.Status is PlaybackStatus.Paused;
break;
}
}
return Task.CompletedTask;
}
public override async Task InitializeAsync() =>
await Mediator.PublishAsync<Request<PlaybackInformation>>();
} }
@@ -5,14 +5,14 @@ namespace Hyperbar.Widget.MediaController.Windows;
public class MediaController : public class MediaController :
INotificationHandler<Play>, INotificationHandler<Play>,
INotificationHandler<Pause>, INotificationHandler<Pause>,
INotificationHandler<Request<Playback>>, INotificationHandler<Request<PlaybackInformation>>,
INotificationHandler<Request<MediaInformation>>, INotificationHandler<Request<MediaInformation>>,
IDisposable IDisposable
{ {
private readonly IMediator mediator;
private readonly IDisposer disposer;
private readonly GlobalSystemMediaTransportControlsSession session;
private readonly AsyncLock asyncLock = new(); private readonly AsyncLock asyncLock = new();
private readonly IDisposer disposer;
private readonly IMediator mediator;
private readonly GlobalSystemMediaTransportControlsSession session;
public MediaController(IMediator mediator, public MediaController(IMediator mediator,
IDisposer disposer, IDisposer disposer,
@@ -31,51 +31,46 @@ public class MediaController :
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
disposer.Dispose(this); disposer.Dispose(this);
} }
public async Task Handle(Play notification, public async Task Handle(Play notification,
CancellationToken cancellationToken) => CancellationToken cancellationToken)
{
await session.TryPlayAsync(); await session.TryPlayAsync();
await UpdateMediaPlaybackPropertiesAsync();
}
public async Task Handle(Pause notification, public async Task Handle(Pause notification,
CancellationToken cancellationToken) => CancellationToken cancellationToken)
{
await session.TryPauseAsync(); await session.TryPauseAsync();
await UpdateMediaPlaybackPropertiesAsync();
public async Task Handle(Request<Playback> notification,
CancellationToken cancellationToken)
{
await mediator.PublishAsync(new Changed<Playback>(), cancellationToken);
} }
public async Task Handle(Request<MediaInformation> _, public async Task Handle(Request<PlaybackInformation> args,
CancellationToken cancellationToken) CancellationToken cancellationToken) => await UpdateMediaPlaybackPropertiesAsync();
{
using (await asyncLock)
{
try
{
GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties = await session.TryGetMediaPropertiesAsync();
await mediator.PublishAsync(new Changed<MediaInformation>(new MediaInformation(mediaProperties.Title,
mediaProperties.Subtitle)), cancellationToken);
}
catch
{
} public async Task Handle(Request<MediaInformation> args,
} CancellationToken cancellationToken) => await UpdateMediaPropertiesAsync();
}
private async void OnMediaPropertiesChanged(GlobalSystemMediaTransportControlsSession sender, private async void OnMediaPropertiesChanged(GlobalSystemMediaTransportControlsSession sender,
MediaPropertiesChangedEventArgs args) MediaPropertiesChangedEventArgs args) => await UpdateMediaPropertiesAsync();
private async void OnPlaybackInfoChanged(GlobalSystemMediaTransportControlsSession sender,
PlaybackInfoChangedEventArgs args) => await UpdateMediaPlaybackPropertiesAsync();
private async Task UpdateMediaPlaybackPropertiesAsync()
{ {
using (await asyncLock) using (await asyncLock)
{ {
try try
{ {
GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties = await session.TryGetMediaPropertiesAsync(); GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo = session.GetPlaybackInfo();
await mediator.PublishAsync(new Changed<MediaInformation>(new MediaInformation(mediaProperties.Title, await mediator.PublishAsync(new Changed<PlaybackInformation>(
mediaProperties.Artist))); new PlaybackInformation((PlaybackStatus)playbackInfo.PlaybackStatus)));
} }
catch catch
{ {
@@ -84,9 +79,23 @@ public class MediaController :
} }
} }
private async void OnPlaybackInfoChanged(GlobalSystemMediaTransportControlsSession sender, private async Task UpdateMediaPropertiesAsync()
PlaybackInfoChangedEventArgs args)
{ {
await mediator.PublishAsync(new Changed<Playback>()); using (await asyncLock)
{
try
{
GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties =
await session.TryGetMediaPropertiesAsync();
await mediator.PublishAsync(new Changed<MediaInformation>(new MediaInformation(mediaProperties.Title,
mediaProperties.Artist)));
}
catch
{
}
}
} }
} }
@@ -22,8 +22,8 @@ public class MediaControllerHandler(IMediator mediator,
if (factory.Create(mediaController) is MediaControllerViewModel mediaControllerViewModel) if (factory.Create(mediaController) is MediaControllerViewModel mediaControllerViewModel)
{ {
cache.Add(mediaController, mediaControllerViewModel); cache.Add(mediaController, mediaControllerViewModel);
//await mediator.PublishAsync(new Created<MediaControllerViewModel>(mediaControllerViewModel), await mediator.PublishAsync(new Created<MediaControllerViewModel>(mediaControllerViewModel),
// cancellationToken); cancellationToken);
} }
} }
} }
@@ -1,9 +1,11 @@
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Hyperbar.UI.Windows;
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public sealed partial class MediaControllerView : public sealed partial class MediaControllerView :
UserControl UserControl
{ {
public MediaControllerView() => InitializeComponent(); public MediaControllerView() =>
this.InitializeComponent(ref _contentLoaded);
} }
@@ -1,5 +1,4 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Hyperbar.Widget;
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
@@ -15,10 +14,24 @@ public class MediaControllerViewModel :
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
Add<MediaInformationViewModel>(); Add<MediaInformationViewModel>();
Add<MediaButtonViewModel>("Backward", "\uEB9E");
Add<MediaButtonViewModel>("Play", "\uE768", new RelayCommand(async () => await mediator.PublishAsync<Play>())); Add<MediaButtonViewModel>(PlaybackButtonType.Previous,
Add<MediaButtonViewModel>("Pause", "\uE769", new RelayCommand(async () => await mediator.PublishAsync<Pause>())); "Previous", "\uEB9E",
Add<MediaButtonViewModel>("Forward", "\uEB9D"); new RelayCommand(async () => await mediator.PublishAsync<Previous>()));
Add<MediaButtonViewModel>(PlaybackButtonType.Play,
"Play", "\uE768",
new RelayCommand(async () => await mediator.PublishAsync<Play>()));
Add<MediaButtonViewModel>(PlaybackButtonType.Pause,
"Pause", "\uE769",
new RelayCommand(async () => await mediator.PublishAsync<Pause>()));
Add<MediaButtonViewModel>(PlaybackButtonType.Forward,
"Forward", "\uEB9D",
new RelayCommand(async () => await mediator.PublishAsync<Forward>()));
mediator.Subscribe(this);
} }
public ITemplateFactory TemplateFactory { get; set; } public ITemplateFactory TemplateFactory { get; set; }
@@ -2,6 +2,12 @@
<UserControl <UserControl
x:Class="Hyperbar.Widget.MediaController.Windows.MediaControllerWidgetView" x:Class="Hyperbar.Widget.MediaController.Windows.MediaControllerWidgetView"
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"
<Button>sdfsdfsdg</Button> xmlns:ui="using:Hyperbar.UI.Windows">
<FlipView
x:Name="FlipView"
Width="400"
Background="Transparent"
ItemTemplateSelector="{Binding Converter={ui:DataTemplateConverter}}"
ItemsSource="{Binding}" />
</UserControl> </UserControl>
@@ -1,74 +1,11 @@
using CustomExtensions.WinUI;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using System.Reflection; using Hyperbar.UI.Windows;
using System.Runtime.CompilerServices;
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public sealed partial class MediaControllerWidgetView : public sealed partial class MediaControllerWidgetView :
UserControl UserControl
{ {
public MediaControllerWidgetView() public MediaControllerWidgetView() =>
{ this.InitializeComponent(ref _contentLoaded);
Foo(@"C:\Users\dan_c\AppData\Local\Packages\24ccddba-447f-4d37-891d-523e8d820f45_rmhrgjnfy8he0\LocalCache\Local\Hyperbar.Windows\Extensions\Hyperbar.Windows.MediaController\Hyperbar.Windows.MediaController.dll");
LocateResourcePath(this);
}
public Assembly ForeignAssembly { get; set; }
private string ForeignAssemblyDir;
private string ForeignAssemblyName;
private bool? IsHotReloadAvailable;
private DisposableCollection Disposables = new();
private bool IsDisposed;
internal static readonly Assembly? EntryAssembly;
internal static readonly string HostingProcessDir;
static MediaControllerWidgetView()
{
EntryAssembly = Assembly.GetEntryAssembly();
HostingProcessDir = Path.GetDirectoryName(EntryAssembly.Location);
}
public void Foo(string assemblyPath)
{
// TODO: For some reason WinUI gets very angry when loading via AssemblyLoadContext,
// even if using AssemblyLoadContext.Default which *should* have no difference than
// Assembly.LoadFrom(), but it does.
//
// ExtensionContext = new(assemblyPath);
// ForeignAssembly = ExtensionContext.LoadFromAssemblyPath(assemblyPath);
ForeignAssembly = Assembly.LoadFrom(assemblyPath);
ForeignAssemblyDir = Path.GetDirectoryName(ForeignAssembly.Location);
ForeignAssemblyName = ForeignAssembly.GetName().Name;
}
private string LocateResourcePath(object component, [CallerFilePath] string callerFilePath = "")
{
if (component.GetType().Assembly != ForeignAssembly)
{
throw new InvalidProgramException();
}
string resourceName = Path.GetFileName(callerFilePath)[..^3];
string[] pathParts = callerFilePath.Split('\\')[..^1];
for (int i = pathParts.Length - 1; i > 1; i++)
{
string pathCandidate = Path.Join(pathParts[i..pathParts.Length].Append(resourceName).Prepend(ForeignAssemblyName).ToArray());
FileInfo sourceResource = new(Path.Combine(ForeignAssemblyDir, pathCandidate));
FileInfo colocatedResource = new(Path.Combine(HostingProcessDir, pathCandidate));
if (colocatedResource.Exists)
{
return pathCandidate;
}
if (sourceResource.Exists)
{
return sourceResource.FullName;
}
throw new FileNotFoundException("Could not find resource", resourceName);
}
throw new FileNotFoundException("Could not find resource", resourceName);
}
} }
@@ -9,7 +9,7 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="40" /> <ColumnDefinition Width="40" />
<ColumnDefinition Width="8" /> <ColumnDefinition Width="8" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="80" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Border <Border
Grid.Column="0" Grid.Column="0"
@@ -6,5 +6,6 @@ namespace Hyperbar.Widget.MediaController.Windows;
public sealed partial class MediaInformationView : public sealed partial class MediaInformationView :
UserControl UserControl
{ {
public MediaInformationView() => this.InitializeComponent(ref _contentLoaded); public MediaInformationView() =>
this.InitializeComponent(ref _contentLoaded);
} }
@@ -1,3 +0,0 @@
namespace Hyperbar.Widget.MediaController.Windows;
public record Playback : INotification;
@@ -0,0 +1,9 @@
namespace Hyperbar.Widget.MediaController.Windows;
public enum PlaybackButtonType
{
Previous,
Play,
Pause,
Forward
}
@@ -0,0 +1,4 @@
namespace Hyperbar.Widget.MediaController.Windows;
public record PlaybackInformation(PlaybackStatus Status) :
INotification;
@@ -0,0 +1,11 @@
namespace Hyperbar.Widget.MediaController.Windows;
public enum PlaybackStatus
{
Closed,
Opened,
Changing,
Stopped,
Playing,
Paused
}
@@ -1,3 +1,3 @@
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public record Backward : INotification; public record Previous : INotification;
@@ -7,13 +7,6 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="WidgetBarView.xaml" />
<None Remove="WidgetButtonView.xaml" />
<None Remove="WidgetContainerView.xaml" />
<None Remove="WidgetSplitButtonView.xaml" />
<None Remove="WidgetView.xaml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.231202003-experimental1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26031-preview" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26031-preview" />
@@ -25,26 +18,4 @@
<ProjectReference Include="..\Hyperbar.Widget\Hyperbar.Widget.csproj" /> <ProjectReference Include="..\Hyperbar.Widget\Hyperbar.Widget.csproj" />
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" /> <ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="WidgetBarView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="WidgetButtonView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="WidgetContainerView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="WidgetSplitButtonView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="WidgetView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project> </Project>
@@ -1,5 +1,4 @@
using System.Reflection; using Windows.ApplicationModel.Resources.Core;
using Windows.ApplicationModel.Resources.Core;
using Windows.Storage; using Windows.Storage;
namespace Hyperbar.Widget.Windows; namespace Hyperbar.Widget.Windows;
@@ -9,27 +8,23 @@ internal class WidgetResourceInitialization(IWidgetAssembly widgetAssembly) :
{ {
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
if (widgetAssembly.Assembly is Assembly assembly) string assemblyDirectory = Path.GetDirectoryName(widgetAssembly.Assembly.Location) ?? string.Empty;
{ string[] possibleFileNames = ["resources.pri", $"{widgetAssembly.Assembly.GetName().Name}.pri"];
if (Path.GetDirectoryName(assembly.Location) is string assemblyDirectory)
{
FileInfo resourceFileInfo = new(Path.Combine(assemblyDirectory,
"resources.pri"));
if (!resourceFileInfo.Exists) FileInfo? resourceFileInfo = null;
foreach (string fileName in possibleFileNames)
{ {
resourceFileInfo = new(Path.Combine(assemblyDirectory, resourceFileInfo = new FileInfo(Path.Combine(assemblyDirectory, fileName));
$"{assembly.GetName().Name}.pri")); if (resourceFileInfo.Exists)
{
break;
}
} }
if (!resourceFileInfo.Exists) if (resourceFileInfo?.Exists is true)
{ {
return;
}
StorageFile file = await StorageFile.GetFileFromPathAsync(resourceFileInfo.FullName); StorageFile file = await StorageFile.GetFileFromPathAsync(resourceFileInfo.FullName);
ResourceManager.Current.LoadPriFiles(new[] { file }); ResourceManager.Current.LoadPriFiles(new[] { file });
} }
} }
}
} }
@@ -12,7 +12,6 @@ public static class IServiceCollectionExtensions
services.AddHandler<WidgetExtensionEnumerator>(); services.AddHandler<WidgetExtensionEnumerator>();
services.AddHandler<WidgetExtensionHandler>(); services.AddHandler<WidgetExtensionHandler>();
services.AddHandler<WidgetExtensionHandler>();
services.AddHandler<WidgetHostHandler>(); services.AddHandler<WidgetHostHandler>();
return services; return services;
-3
View File
@@ -25,9 +25,6 @@ public class WidgetBuilder<TConfiguration>(TConfiguration configuration) :
services.AddScoped<IServiceFactory>(provider => services.AddScoped<IServiceFactory>(provider =>
new ServiceFactory((type, parameters) => new ServiceFactory((type, parameters) =>
ActivatorUtilities.CreateInstance(provider, type, parameters!))); ActivatorUtilities.CreateInstance(provider, type, parameters!)));
services.AddHostedService<WidgetService>();
services.AddScoped<IMediator, Mediator>(); services.AddScoped<IMediator, Mediator>();
services.AddScoped<IDisposer, Disposer>(); services.AddScoped<IDisposer, Disposer>();
+6
View File
@@ -16,6 +16,9 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
[ObservableProperty] [ObservableProperty]
private IRelayCommand? click = command; private IRelayCommand? click = command;
[ObservableProperty]
private bool enabled;
[ObservableProperty] [ObservableProperty]
private string? icon = icon; private string? icon = icon;
@@ -24,4 +27,7 @@ public partial class WidgetButtonViewModel(IServiceFactory serviceFactory,
[ObservableProperty] [ObservableProperty]
private string? text = text; private string? text = text;
[ObservableProperty]
private bool visible;
} }
+1 -3
View File
@@ -1,6 +1,4 @@
using CommunityToolkit.Mvvm.ComponentModel; namespace Hyperbar.Widget;
namespace Hyperbar.Widget;
public partial class WidgetComponentViewModel : public partial class WidgetComponentViewModel :
ObservableCollectionViewModel<IWidgetComponentViewModel>, ObservableCollectionViewModel<IWidgetComponentViewModel>,
@@ -12,7 +12,6 @@ public class WidgetExtensionHandler(IProxyServiceCollection<IWidgetBuilder> type
if(notification.Value is WidgetExtension widgetExtension) if(notification.Value is WidgetExtension widgetExtension)
{ {
IWidgetBuilder builder = widgetExtension.Widget.Create(); IWidgetBuilder builder = widgetExtension.Widget.Create();
builder.ConfigureServices(args => builder.ConfigureServices(args =>
{ {
args.AddSingleton(widgetExtension.Assembly); args.AddSingleton(widgetExtension.Assembly);
-17
View File
@@ -1,17 +0,0 @@
using Microsoft.Extensions.Hosting;
namespace Hyperbar.Widget;
public sealed class WidgetService(IEnumerable<IInitializer> initializers) :
IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
foreach (IInitializer initializer in initializers)
{
await initializer.InitializeAsync();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
-6
View File
@@ -1,11 +1,5 @@
namespace Hyperbar; namespace Hyperbar;
[AttributeUsage(AttributeTargets.Class)]
public class NotificationHandlerAttribute(object key) : Attribute
{
public object Key { get; } = key;
}
public interface IMediator public interface IMediator
{ {
Task PublishAsync<TNotification>(TNotification notification, Task PublishAsync<TNotification>(TNotification notification,
+29 -20
View File
@@ -107,31 +107,18 @@ public class Mediator(IServiceProvider provider,
public void Subscribe(object handler) public void Subscribe(object handler)
{ {
Type handlerType = handler.GetType(); Type handlerType = handler.GetType();
object? key = null; object? key = GetKeyFromHandler(handlerType, handler);
if (Attribute.GetCustomAttribute(handlerType, typeof(NotificationHandlerAttribute)) foreach (Type interfaceType in GetNotificationHandlerInterfaces(handlerType))
is NotificationHandlerAttribute attribute)
{ {
if (handlerType.GetProperty($"{attribute.Key}") is PropertyInfo property) if (interfaceType.GetGenericArguments().FirstOrDefault()
is Type argumentType)
{ {
if (property.GetValue(handler, null) is { } value) if (object.Equals(key, default))
{ {
key = value;
}
}
else
{
key = attribute.Key;
}
}
foreach (Type interfaceType in handlerType.GetInterfaces().Where(x => x.IsGenericType }
&& x.GetGenericTypeDefinition() == typeof(INotificationHandler<>))) subjects.AddOrUpdate(key ?? argumentType, new List<object> { handler }, (value, collection) =>
{
if (interfaceType.GetGenericArguments() is { Length: 1 } arguments)
{
key ??= arguments[0];
subjects.AddOrUpdate(key, [handler], (value, collection) =>
{ {
collection.Add(handler); collection.Add(handler);
return collection; return collection;
@@ -139,4 +126,26 @@ public class Mediator(IServiceProvider provider,
} }
} }
} }
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<>));
} }
@@ -0,0 +1,7 @@
namespace Hyperbar;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class NotificationHandlerAttribute(object key) : Attribute
{
public object Key { get; } = key;
}