diff --git a/Hyperbar.Windows.Primary/PrimaryWidget.cs b/Hyperbar.Windows.Primary/PrimaryWidget.cs index 7b564a4..2f06b8c 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidget.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidget.cs @@ -10,14 +10,13 @@ public class PrimaryWidget : { args.Id = Guid.Parse("cfdfe07c-d9d6-4174-ae41-988ca24d2e10"); args.Name = "Primary commands"; - - }).ConfigureServices(args => + }).ConfigureServices(services => { - args.AddCache<(Guid ParentId, Guid Id), PrimaryCommandConfiguration>() + services.AddCache<(Guid ParentId, Guid Id), PrimaryCommandConfiguration>() .AddCache() .AddTransient, WidgetComponentProvider>() .AddTransient, WidgetComponentFactory>() - .AddTransient, WidgetComponentEnumerator>() + .AddTransient, WidgetComponentEnumerationHandler>() .AddWidgetTemplate() .AddHandler(); }); diff --git a/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs b/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs index 7d534b7..0a6e2a2 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs @@ -20,66 +20,70 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator, (Guid currentParentId, List currentConfigurations) = stack.Pop(); foreach (PrimaryCommandConfiguration configuration in currentConfigurations) { - items.Add(new KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration>((currentParentId, configuration.Id), configuration)); - if (configuration.Commands is not null && configuration.Commands.Count > 0) + var key = (currentParentId, configuration.Id); + items.Add(new KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration>(key, configuration)); + + if (configuration.Commands?.Count > 0) { stack.Push((configuration.Id, configuration.Commands)); } } } - foreach (KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> moved in - items.ExceptBy(cache.Select(x => new { x.Value.Order, x.Value.Id }), x => new { x.Value.Order, x.Value.Id })) + HashSet cacheIds = new(cache.Select(x => x.Key.Id)); + HashSet itemIds = new(items.Select(x => x.Key.Id)); + + List> movedItems = + items.ExceptBy(cache.Select(x => new { x.Value.Order, x.Value.Id }), x => + new { x.Value.Order, x.Value.Id }).ToList(); + + foreach (KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> moved in movedItems) { - if (moved.Value is PrimaryCommandConfiguration configuration) + if (moved.Value is PrimaryCommandConfiguration configuration && + provider.Get(configuration) is IWidgetComponentViewModel viewModel) { - if (provider.Get(configuration) is IWidgetComponentViewModel viewModel) - { - await mediator.PublishAsync(new Moved(configuration.Order, viewModel), - moved.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : moved.Key.ParentId, - cancellationToken); + await mediator.PublishAsync( + new Moved(configuration.Order, viewModel), + moved.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : moved.Key.ParentId, + cancellationToken + ); - cache.Remove(moved.Key); - cache.Add(moved.Key, moved.Value); - } + cache.Remove(moved.Key); + cache.Add(moved.Key, moved.Value); } - - - - - // // cache.Add(added.Key, added.Value); - //} } - foreach (KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> added in - items.ExceptBy(cache.Select(x => x.Key.Id), x => x.Key.Id)) + List> addedItems = + items.ExceptBy(cacheIds.Select(x => x), x => x.Key.Id).ToList(); + + foreach (KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> added in addedItems) { - if (added.Value is PrimaryCommandConfiguration configuration) + if (added.Value is PrimaryCommandConfiguration configuration && + factory.Create(configuration) is IWidgetComponentViewModel viewModel) { - if (factory.Create(configuration) is IWidgetComponentViewModel viewModel) - { - await mediator.PublishAsync(new Inserted(configuration.Order, viewModel), - added.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : added.Key.ParentId, - cancellationToken); - } + await mediator.PublishAsync( + new Inserted(configuration.Order, viewModel), + added.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : added.Key.ParentId, + cancellationToken); cache.Add(added.Key, added.Value); } } - foreach (KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> removed in - cache.ExceptBy(items.Select(x => x.Key.Id), x => x.Key.Id)) - { - if (removed.Value is PrimaryCommandConfiguration configuration) - { - if (provider.Get(configuration) is IWidgetComponentViewModel viewModel) - { - await mediator.PublishAsync(new Removed(viewModel), - removed.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : removed.Key.ParentId, - cancellationToken); + List> removedItems = + cache.ExceptBy(itemIds.Select(x => x), x => x.Key.Id).ToList(); - cache.Remove(removed.Key); - } + foreach (KeyValuePair<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> removed in removedItems) + { + if (removed.Value is PrimaryCommandConfiguration configuration && + provider.Get(configuration) is IWidgetComponentViewModel viewModel) + { + await mediator.PublishAsync( + new Removed(viewModel), + removed.Key.ParentId == Guid.Empty ? nameof(PrimaryWidgetViewModel) : removed.Key.ParentId, + cancellationToken); + + cache.Remove(removed.Key); } } } diff --git a/Hyperbar.Windows.Primary/WidgetComponentEnumerator.cs b/Hyperbar.Windows.Primary/WidgetComponentEnumerationHandler.cs similarity index 88% rename from Hyperbar.Windows.Primary/WidgetComponentEnumerator.cs rename to Hyperbar.Windows.Primary/WidgetComponentEnumerationHandler.cs index 35b45a8..3220fa4 100644 --- a/Hyperbar.Windows.Primary/WidgetComponentEnumerator.cs +++ b/Hyperbar.Windows.Primary/WidgetComponentEnumerationHandler.cs @@ -1,11 +1,11 @@ namespace Hyperbar.Windows.Primary; -public class WidgetComponentEnumerator(PrimaryWidgetConfiguration configuration, +public class WidgetComponentEnumerationHandler(PrimaryWidgetConfiguration configuration, IFactory factory, ICache<(Guid ParentId, Guid Id), PrimaryCommandConfiguration> cache) : IEnumerator { - public IEnumerable Next() + public IEnumerable Get() { Stack<(Guid, List)> stack = new(); stack.Push((Guid.Empty, configuration.Commands)); diff --git a/Hyperbar.Windows/App.xaml.cs b/Hyperbar.Windows/App.xaml.cs index 7189c27..8e7fda3 100644 --- a/Hyperbar.Windows/App.xaml.cs +++ b/Hyperbar.Windows/App.xaml.cs @@ -1,10 +1,12 @@ using Hyperbar.Windows.Controls; +using Hyperbar.Windows.Interop; using Hyperbar.Windows.UI; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.UI.Xaml; +using System; using System.Reflection; namespace Hyperbar.Windows; @@ -27,24 +29,40 @@ public partial class App : }) .ConfigureServices((context, services) => { - services.AddSingleton(provider => - new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddDefault(); services.AddHostedService(); - services.AddConfiguration(args => { args.Placement = DesktopBarPlacemenet.Top; }); - services.AddTransient(); + services.AddSingleton(); services.AddTransient(); - - services.AddSingleton(); - - services.AddContentTemplate(); + services.AddTransient(); services.AddHandler(); + services.AddConfiguration(args => + { + args.Placement = DesktopBarPlacemenet.Top; + }); + + services.AddSingleton(); + services.AddContentTemplate(); + + services.AddTransient(provider => + new WidgetServiceCollection(services => + { + services.AddSingleton(); + services.AddTransient(); + + services.AddScoped(); + + services.AddHandler(); + services.AddHandler(); + + services.AddTransient(); + + services.AddContentTemplate(); + services.AddContentTemplate(); + services.AddContentTemplate(); + })); //services.AddWidget(); //services.AddWidget(); diff --git a/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs b/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs index 10e5bba..73a03bf 100644 --- a/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs +++ b/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs @@ -3,59 +3,57 @@ using Hyperbar.Windows.UI; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.UI.Dispatching; namespace Hyperbar.Windows { - public static class IServiceCollectionExtensions - { - public static IServiceCollection AddWidget(this IServiceCollection services) - where TWidget : - IWidget, - new() - { - IHost? host = new HostBuilder() - .UseContentRoot(AppContext.BaseDirectory) - .ConfigureAppConfiguration(config => - { - config.SetBasePath(AppContext.BaseDirectory); - config.AddJsonFile("Settings.json", true, true); + //public static class IServiceCollectionExtensions + //{ + // public static IServiceCollection AddWidget(this IServiceCollection services) + // where TWidget : + // IWidget, + // new() + // { + // IHost? host = new HostBuilder() + // .UseContentRoot(AppContext.BaseDirectory) + // .ConfigureAppConfiguration(config => + // { + // config.SetBasePath(AppContext.BaseDirectory); + // config.AddJsonFile("Settings.json", true, true); + // }) + // .ConfigureServices((context, isolatedServices) => + // { + // isolatedServices.AddScoped(provider => + // new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); - config.Build(); - }) - .ConfigureServices((context, isolatedServices) => - { - isolatedServices.AddScoped(provider => - new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); + // isolatedServices.AddHostedService(); + // isolatedServices.AddTransient(); - isolatedServices.AddHostedService(); - isolatedServices.AddTransient(); + // isolatedServices.AddScoped(); + // isolatedServices.AddScoped(); + // isolatedServices.AddSingleton(); - isolatedServices.AddScoped(); - isolatedServices.AddScoped(); - isolatedServices.AddSingleton(); + // isolatedServices.AddScoped(); - isolatedServices.AddScoped(); + // isolatedServices.AddHandler(); + // isolatedServices.AddHandler(); - isolatedServices.AddHandler(); - isolatedServices.AddHandler(); + // isolatedServices.AddTransient(); - isolatedServices.AddTransient(); + // isolatedServices.AddContentTemplate(); + // isolatedServices.AddContentTemplate(); + // isolatedServices.AddContentTemplate(); - isolatedServices.AddContentTemplate(); - isolatedServices.AddContentTemplate(); - isolatedServices.AddContentTemplate(); + // TWidget widget = new(); + // IWidgetBuilder builder = widget.Create(); - TWidget widget = new(); - IWidgetBuilder builder = widget.Create(); - isolatedServices.AddRange(builder.Services); + // isolatedServices.AddRange(builder.Services); - }).Build(); + // }).Build(); - services.AddTransient(provider => new WidgetContext(host.Services)); + // services.AddTransient(provider => new WidgetContext(host.Services)); - host.Start(); - return services; - } - } + // host.Start(); + // return services; + // } + //} } \ No newline at end of file diff --git a/Hyperbar/Configuration/Configuration.cs b/Hyperbar/Configuration/Configuration.cs index 1d4471b..b0a2570 100644 --- a/Hyperbar/Configuration/Configuration.cs +++ b/Hyperbar/Configuration/Configuration.cs @@ -3,7 +3,7 @@ public class Configuration(IConfigurationReader reader) : IConfiguration where TConfiguration : - class, new() + class { public TConfiguration Value => reader.Read(); } diff --git a/Hyperbar/Configuration/ConfigurationFactory.cs b/Hyperbar/Configuration/ConfigurationFactory.cs new file mode 100644 index 0000000..18f8238 --- /dev/null +++ b/Hyperbar/Configuration/ConfigurationFactory.cs @@ -0,0 +1,9 @@ +namespace Hyperbar; + +public class ConfigurationFactory(Func factory) : + IConfigurationFactory + where TConfiguration : + class +{ + public TConfiguration Create() => factory.Invoke(); +} diff --git a/Hyperbar/Configuration/ConfigurationFile.cs b/Hyperbar/Configuration/ConfigurationFile.cs new file mode 100644 index 0000000..ca9eb17 --- /dev/null +++ b/Hyperbar/Configuration/ConfigurationFile.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.FileProviders; + +namespace Hyperbar; + +public class ConfigurationFile(IFileInfo fileInfo) : + IConfigurationFile + where TConfiguration : + class +{ + public IFileInfo FileInfo => fileInfo; +} diff --git a/Hyperbar/Configuration/ConfigurationInitializer.cs b/Hyperbar/Configuration/ConfigurationInitializer.cs index b9bea88..d9cdc45 100644 --- a/Hyperbar/Configuration/ConfigurationInitializer.cs +++ b/Hyperbar/Configuration/ConfigurationInitializer.cs @@ -1,23 +1,13 @@ namespace Hyperbar; -public class ConfigurationInitializer(DefaultConfiguration defaults, - IConfigurationReader reader, - IConfigurationWriter writer) : +public class ConfigurationInitializer(IConfigurationReader reader) : IInitializer where TConfiguration : - class, - new() + class { public Task InitializeAsync() { - if (!reader.TryRead(out TConfiguration? _)) - { - if (defaults.Configuration is not null) - { - writer.Write(defaults.Configuration); - } - } - + reader.Read(); return Task.CompletedTask; } } diff --git a/Hyperbar/Configuration/ConfigurationMonitor.cs b/Hyperbar/Configuration/ConfigurationMonitor.cs index 54420b8..1ab715d 100644 --- a/Hyperbar/Configuration/ConfigurationMonitor.cs +++ b/Hyperbar/Configuration/ConfigurationMonitor.cs @@ -4,7 +4,7 @@ public class ConfigurationMonitor(IConfigurationFile reader, IMediator mediator) : IInitializer where TConfiguration : - class, new() + class { private FileSystemWatcher? watcher; diff --git a/Hyperbar/Configuration/ConfigurationReader.cs b/Hyperbar/Configuration/ConfigurationReader.cs index a5ff9a3..a7a820c 100644 --- a/Hyperbar/Configuration/ConfigurationReader.cs +++ b/Hyperbar/Configuration/ConfigurationReader.cs @@ -1,19 +1,19 @@ namespace Hyperbar; -public class ConfigurationReader(IConfigurationSource source) : +public class ConfigurationReader(IConfigurationSource source, + IConfigurationFactory factory) : IConfigurationReader where TConfiguration : - class, new() + class { public TConfiguration Read() { - if ((source.TryGet(out TConfiguration? value) ? value : - new TConfiguration()) is TConfiguration configuration) + if ((source.TryGet(out TConfiguration? value) ? value : factory.Create()) is TConfiguration configuration) { return configuration; } - return new TConfiguration(); + return factory.Create(); } public bool TryRead(out TConfiguration? configuration) diff --git a/Hyperbar/Configuration/ConfigurationWriter.cs b/Hyperbar/Configuration/ConfigurationWriter.cs index c08b365..cf39aec 100644 --- a/Hyperbar/Configuration/ConfigurationWriter.cs +++ b/Hyperbar/Configuration/ConfigurationWriter.cs @@ -1,13 +1,15 @@ namespace Hyperbar; -public class ConfigurationWriter(IConfigurationSource source) : +public class ConfigurationWriter(IConfigurationSource source, + IConfigurationFactory factory) : IConfigurationWriter where TConfiguration : - class, new() + class { public void Write(Action updateDelegate) { - if ((source.TryGet(out TConfiguration? value) ? value : new TConfiguration()) is TConfiguration updatedValue) + if ((source.TryGet(out TConfiguration? value) ? value : + factory.Create()) is TConfiguration updatedValue) { updateDelegate?.Invoke(updatedValue); Write(updatedValue); diff --git a/Hyperbar/Configuration/DefaultConfiguration.cs b/Hyperbar/Configuration/DefaultConfiguration.cs deleted file mode 100644 index 27006af..0000000 --- a/Hyperbar/Configuration/DefaultConfiguration.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Hyperbar; - -public class DefaultConfiguration(TConfiguration? configuration = null) - where TConfiguration : - class -{ - public TConfiguration? Configuration => configuration; -} diff --git a/Hyperbar/Configuration/IConfiguration.cs b/Hyperbar/Configuration/IConfiguration.cs index 8c8a6cc..0a531e6 100644 --- a/Hyperbar/Configuration/IConfiguration.cs +++ b/Hyperbar/Configuration/IConfiguration.cs @@ -2,7 +2,7 @@ public interface IConfiguration where TConfiguration : - class, new() + class { TConfiguration Value { get; } } diff --git a/Hyperbar/Configuration/IConfigurationFactory.cs b/Hyperbar/Configuration/IConfigurationFactory.cs new file mode 100644 index 0000000..43988f4 --- /dev/null +++ b/Hyperbar/Configuration/IConfigurationFactory.cs @@ -0,0 +1,7 @@ +namespace Hyperbar +{ + public interface IConfigurationFactory where TConfiguration : class + { + TConfiguration Create(); + } +} \ No newline at end of file diff --git a/Hyperbar/Configuration/IConfigurationFile.cs b/Hyperbar/Configuration/IConfigurationFile.cs new file mode 100644 index 0000000..ef82400 --- /dev/null +++ b/Hyperbar/Configuration/IConfigurationFile.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Hyperbar; + +public interface IConfigurationFile + where TConfiguration : + class +{ + IFileInfo FileInfo { get; } +} diff --git a/Hyperbar/Configuration/IConfigurationReader.cs b/Hyperbar/Configuration/IConfigurationReader.cs index 5828d26..8f95bcf 100644 --- a/Hyperbar/Configuration/IConfigurationReader.cs +++ b/Hyperbar/Configuration/IConfigurationReader.cs @@ -2,7 +2,7 @@ public interface IConfigurationReader where TConfiguration : - class, new() + class { bool TryRead(out TConfiguration? configuration); diff --git a/Hyperbar/Configuration/IConfigurationSource.cs b/Hyperbar/Configuration/IConfigurationSource.cs index 9a3b3eb..50fb7bd 100644 --- a/Hyperbar/Configuration/IConfigurationSource.cs +++ b/Hyperbar/Configuration/IConfigurationSource.cs @@ -1,6 +1,4 @@ -using Microsoft.Extensions.FileProviders; - -namespace Hyperbar; +namespace Hyperbar; public interface IConfigurationSource where TConfiguration : diff --git a/Hyperbar/Configuration/IConfigurationWriter.cs b/Hyperbar/Configuration/IConfigurationWriter.cs index e9b394c..9d68b60 100644 --- a/Hyperbar/Configuration/IConfigurationWriter.cs +++ b/Hyperbar/Configuration/IConfigurationWriter.cs @@ -2,7 +2,7 @@ public interface IConfigurationWriter where TConfiguration : - class, new() + class { void Write(Action updateDelegate); diff --git a/Hyperbar/Configuration/IWritableConfiguration.cs b/Hyperbar/Configuration/IWritableConfiguration.cs index fce9357..483bba4 100644 --- a/Hyperbar/Configuration/IWritableConfiguration.cs +++ b/Hyperbar/Configuration/IWritableConfiguration.cs @@ -2,7 +2,7 @@ public interface IWritableConfiguration where TConfiguration : - class, new() + class { void Write(Action updateDelegate); } diff --git a/Hyperbar/Configuration/WritableConfiguration.cs b/Hyperbar/Configuration/WritableConfiguration.cs index 0dd788a..a2c8c8c 100644 --- a/Hyperbar/Configuration/WritableConfiguration.cs +++ b/Hyperbar/Configuration/WritableConfiguration.cs @@ -3,7 +3,7 @@ public class WritableConfiguration(IConfigurationWriter writer) : IWritableConfiguration where TConfiguration : - class, new() + class { public void Write(Action updateDelegate) => writer.Write(updateDelegate); } diff --git a/Hyperbar/Extensions/IServiceCollectionExtensions.cs b/Hyperbar/Extensions/IServiceCollectionExtensions.cs index 9f6e1e3..9feeb8a 100644 --- a/Hyperbar/Extensions/IServiceCollectionExtensions.cs +++ b/Hyperbar/Extensions/IServiceCollectionExtensions.cs @@ -7,23 +7,26 @@ using System.Text.Json; namespace Hyperbar; -public interface IConfigurationFile - where TConfiguration : - class -{ - IFileInfo FileInfo { get; } -} - -public class ConfigurationFile(IFileInfo fileInfo) : - IConfigurationFile - where TConfiguration : - class -{ - public IFileInfo FileInfo => fileInfo; -} - public static class IServiceCollectionExtensions { + public static IServiceCollection AddDefault(this IServiceCollection services) + { + services.AddSingleton(provider => + new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient, WidgetFactory>(); + + services.AddHandler(); + services.AddHandler(); + services.AddHandler(); + + return services; + } + public static IServiceCollection AddCache(this IServiceCollection services) where TKey : notnull @@ -46,38 +49,45 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddConfiguration(this IServiceCollection services) where TConfiguration : - class, new() - { - return services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", null); - } + class => services.AddConfiguration(typeof(TConfiguration).Name, + "Settings.json", + null); public static IServiceCollection AddConfiguration(this IServiceCollection services, Action configurationDelegate) where TConfiguration : - class, new() + class, + new() { TConfiguration configuration = new(); configurationDelegate.Invoke(configuration); - return services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", + return services.AddConfiguration(typeof(TConfiguration).Name, + "Settings.json", configuration); } public static IServiceCollection AddConfiguration(this IServiceCollection services, - TConfiguration? defaults = null) + TConfiguration configuration) where TConfiguration : - class, new() - { - return services.AddConfiguration(typeof(TConfiguration).Name, "Settings.json", defaults); - } + class => services.AddConfiguration(configuration.GetType().Name, + "Settings.json", + configuration); + + public static IServiceCollection AddConfiguration(this IServiceCollection services, + object configuration) + where TConfiguration : + class => services.AddConfiguration(configuration.GetType().Name, + "Settings.json", + (TConfiguration?)configuration); public static IServiceCollection AddConfiguration(this IServiceCollection services, string section, string path = "Settings.json", - TConfiguration? defaults = null, + object? configuration = null, Action? serializerDelegate = null) where TConfiguration : - class, new() + class { services.AddSingleton>(provider => { @@ -109,7 +119,17 @@ public static class IServiceCollectionExtensions services.AddSingleton, ConfigurationReader>(); services.AddSingleton, ConfigurationWriter>(); - services.AddTransient(provider => new DefaultConfiguration(defaults)); + if (configuration is not null) + { + services.AddTransient(typeof(TConfiguration), provider => configuration); + } + + services.AddTransient>(provider => new ConfigurationFactory(() => + { + var fo = configuration ?? provider.GetRequiredService(); + + return (TConfiguration)fo; + })); services.AddTransient>(); services.AddTransient, WritableConfiguration>(); @@ -134,7 +154,12 @@ public static class IServiceCollectionExtensions services.AddKeyedTransient(contentType, key); services.AddKeyedTransient(templateType, key); - services.AddTransient(provider => new ContentTemplateDescriptor { ContentType = contentType, TemplateType = templateType, Key = key }); + services.AddTransient(provider => new ContentTemplateDescriptor + { + ContentType = contentType, + TemplateType = templateType, + Key = key + }); return services; } diff --git a/Hyperbar/Views/Changed.cs b/Hyperbar/Mediators/Changed.cs similarity index 90% rename from Hyperbar/Views/Changed.cs rename to Hyperbar/Mediators/Changed.cs index 0c72d25..e6da2a5 100644 --- a/Hyperbar/Views/Changed.cs +++ b/Hyperbar/Mediators/Changed.cs @@ -1,4 +1,3 @@ namespace Hyperbar; -public record Changed(TValue? Value = default) : INotification; - +public record Changed(TValue? Value = default) : INotification; \ No newline at end of file diff --git a/Hyperbar/Mediators/Created.cs b/Hyperbar/Mediators/Created.cs new file mode 100644 index 0000000..192defe --- /dev/null +++ b/Hyperbar/Mediators/Created.cs @@ -0,0 +1,3 @@ +namespace Hyperbar; + +public record Created(TValue Value, object? Target = null) : INotification; \ No newline at end of file diff --git a/Hyperbar/Mediators/Enumerate.cs b/Hyperbar/Mediators/Enumerate.cs new file mode 100644 index 0000000..50fd63e --- /dev/null +++ b/Hyperbar/Mediators/Enumerate.cs @@ -0,0 +1,3 @@ +namespace Hyperbar; + +public record Enumerate : INotification; \ No newline at end of file diff --git a/Hyperbar/Views/Inserted.cs b/Hyperbar/Mediators/Inserted.cs similarity index 100% rename from Hyperbar/Views/Inserted.cs rename to Hyperbar/Mediators/Inserted.cs diff --git a/Hyperbar/Views/Moved.cs b/Hyperbar/Mediators/Moved.cs similarity index 100% rename from Hyperbar/Views/Moved.cs rename to Hyperbar/Mediators/Moved.cs diff --git a/Hyperbar/Views/Removed.cs b/Hyperbar/Mediators/Removed.cs similarity index 100% rename from Hyperbar/Views/Removed.cs rename to Hyperbar/Mediators/Removed.cs diff --git a/Hyperbar/Views/Created.cs b/Hyperbar/Views/Created.cs deleted file mode 100644 index 7a436cb..0000000 --- a/Hyperbar/Views/Created.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Hyperbar; - -public record Created(TValue Value, object Target) : INotification -{ - public static Created For(TValue value) - { - return new Created(value, typeof(TTarget).Name); - } -} \ No newline at end of file diff --git a/Hyperbar/Views/IEnumerator.cs b/Hyperbar/Views/IEnumerator.cs index 94c164a..f95353c 100644 --- a/Hyperbar/Views/IEnumerator.cs +++ b/Hyperbar/Views/IEnumerator.cs @@ -2,5 +2,5 @@ public interface IEnumerator { - IEnumerable Next(); + IEnumerable Get(); } diff --git a/Hyperbar/Views/ObservableCollectionViewModel.cs b/Hyperbar/Views/ObservableCollectionViewModel.cs index 2dcf467..3029c7a 100644 --- a/Hyperbar/Views/ObservableCollectionViewModel.cs +++ b/Hyperbar/Views/ObservableCollectionViewModel.cs @@ -50,7 +50,7 @@ public partial class ObservableCollectionViewModel : if (enumerator is not null) { - foreach (TItem? item in enumerator.Next()) + foreach (TItem? item in enumerator.Get()) { if (item is not null) { diff --git a/Hyperbar/Widgets/IWidgetAssemblyLoader.cs b/Hyperbar/Widgets/IWidgetAssemblyLoader.cs deleted file mode 100644 index 3929e0f..0000000 --- a/Hyperbar/Widgets/IWidgetAssemblyLoader.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Hyperbar -{ - public interface IWidgetAssemblyLoader - { - } -} \ No newline at end of file diff --git a/Hyperbar/Widgets/IWidgetBuilder.cs b/Hyperbar/Widgets/IWidgetBuilder.cs index ce1ae1f..96e4701 100644 --- a/Hyperbar/Widgets/IWidgetBuilder.cs +++ b/Hyperbar/Widgets/IWidgetBuilder.cs @@ -4,7 +4,7 @@ namespace Hyperbar; public interface IWidgetBuilder { - WidgetConfiguration Configuration { get; } + IWidgetBuilder ConfigureServices(Action configureDelegate); - IServiceCollection Services { get; } + IWidgetHost Build(); } \ No newline at end of file diff --git a/Hyperbar/Widgets/IWidgetBuilderExtensions.cs b/Hyperbar/Widgets/IWidgetBuilderExtensions.cs index b01cf62..cc8e53a 100644 --- a/Hyperbar/Widgets/IWidgetBuilderExtensions.cs +++ b/Hyperbar/Widgets/IWidgetBuilderExtensions.cs @@ -1,35 +1,13 @@ using Microsoft.Extensions.DependencyInjection; -using System.Reflection; -using System.Runtime.Loader; namespace Hyperbar; -public class AssemblyProvider -{ - public static void Load() - { - string extensionsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Extensions"); - Directory.CreateDirectory(extensionsPath); - } - - public static IEnumerable Get(string path) - { - string extensionsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Extensions"); - - Directory.CreateDirectory(extensionsPath); - foreach (string assemblyPath in Directory.GetFiles(extensionsPath, "*.dll", SearchOption.AllDirectories)) - { - yield return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); - } - } -} - public static class IWidgetBuilderExtensions { - public static IWidgetBuilder ConfigureServices(this IWidgetBuilder builder, - Action servicesDelegate) - { - servicesDelegate(builder.Services); - return builder; - } + //public static IWidgetBuilder ConfigureServices(this IWidgetBuilder builder, + // Action servicesDelegate) + //{ + // servicesDelegate(builder.Services); + // return builder; + //} } diff --git a/Hyperbar/Widgets/IWidgetHost.cs b/Hyperbar/Widgets/IWidgetHost.cs new file mode 100644 index 0000000..8e65433 --- /dev/null +++ b/Hyperbar/Widgets/IWidgetHost.cs @@ -0,0 +1,6 @@ +namespace Hyperbar; + +public interface IWidgetHost +{ + +} diff --git a/Hyperbar/Widgets/IWidgetServiceBuilder.cs b/Hyperbar/Widgets/IWidgetServiceBuilder.cs new file mode 100644 index 0000000..94322de --- /dev/null +++ b/Hyperbar/Widgets/IWidgetServiceBuilder.cs @@ -0,0 +1,6 @@ +namespace Hyperbar; + +public interface IWidgetServiceBuilder +{ + void ConfigureWidgetServices(IWidgetServiceCollection widgetServices); +} diff --git a/Hyperbar/Widgets/IWidgetServiceCollection.cs b/Hyperbar/Widgets/IWidgetServiceCollection.cs new file mode 100644 index 0000000..ff538fc --- /dev/null +++ b/Hyperbar/Widgets/IWidgetServiceCollection.cs @@ -0,0 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Hyperbar; + +public interface IWidgetServiceCollection +{ + IServiceCollection Services { get; } +} diff --git a/Hyperbar/Widgets/WidgetAssembly.cs b/Hyperbar/Widgets/WidgetAssembly.cs new file mode 100644 index 0000000..7bd26cd --- /dev/null +++ b/Hyperbar/Widgets/WidgetAssembly.cs @@ -0,0 +1,6 @@ +using System.Reflection; + +namespace Hyperbar; + +public record WidgetAssembly(Assembly? Assembly = default) : + INotification; \ No newline at end of file diff --git a/Hyperbar/Widgets/WidgetAssemblyHandler.cs b/Hyperbar/Widgets/WidgetAssemblyHandler.cs new file mode 100644 index 0000000..6b4f793 --- /dev/null +++ b/Hyperbar/Widgets/WidgetAssemblyHandler.cs @@ -0,0 +1,23 @@ +using System.Reflection; + +namespace Hyperbar; + +public class WidgetAssemblyHandler(IMediator mediator, + IFactory factory) : + INotificationHandler> +{ + public Task Handle(Created notification, + CancellationToken cancellationToken) + { + if (notification.Value?.GetTypes().FirstOrDefault(x => typeof(IWidget).IsAssignableFrom(x)) is Type widgetType) + { + if (factory.Create(widgetType) is IWidget widget) + { + mediator.PublishAsync(new Created(widget), + cancellationToken); + } + } + + return Task.CompletedTask; + } +} diff --git a/Hyperbar/Widgets/WidgetBuilder.cs b/Hyperbar/Widgets/WidgetBuilder.cs index 1c46b83..8e4df68 100644 --- a/Hyperbar/Widgets/WidgetBuilder.cs +++ b/Hyperbar/Widgets/WidgetBuilder.cs @@ -1,17 +1,42 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Reflection; namespace Hyperbar; -public class WidgetBuilder(WidgetConfiguration configuration) : - IWidgetBuilder +public class WidgetBuilder : + IWidgetBuilder, + IWidgetServiceBuilder { - public IServiceCollection Services => new ServiceCollection(); + private readonly IHostBuilder hostBuilder; - public WidgetConfiguration Configuration => configuration; + public WidgetBuilder(IWidgetConfiguration configuration) + { + hostBuilder = new HostBuilder() + .UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + Assembly.GetEntryAssembly()?.GetName().Name!), true) + .ConfigureAppConfiguration(config => + { + config.AddJsonFile("Settings.json", true, true); + }) + .ConfigureServices((context, services) => + { + services.AddScoped(provider => + new ServiceFactory((type, parameters) => + ActivatorUtilities.CreateInstance(provider, type, parameters!))); + + services.AddHostedService(); + + services.AddScoped(); + services.AddScoped(); + services.AddConfiguration(configuration); + }); + } public static IWidgetBuilder Configure(Action configurationDelegate) where TWidgetConfiguration : - WidgetConfiguration, + IWidgetConfiguration, new() { TWidgetConfiguration configuration = new(); @@ -19,4 +44,19 @@ public class WidgetBuilder(WidgetConfiguration configuration) : return new WidgetBuilder(configuration); } + + public IWidgetHost Build() + { + IHost host = hostBuilder.Build(); + return new WidgetHost(host); + } + + public IWidgetBuilder ConfigureServices(Action configureDelegate) + { + hostBuilder.ConfigureServices(configureDelegate.Invoke); + return this; + } + + public void ConfigureWidgetServices(IWidgetServiceCollection widgetServices) => + hostBuilder.ConfigureServices(services => services.AddRange(widgetServices.Services)); } \ No newline at end of file diff --git a/Hyperbar/Widgets/WidgetConfiguration.cs b/Hyperbar/Widgets/WidgetConfiguration.cs index 3a3aba0..8de8a8b 100644 --- a/Hyperbar/Widgets/WidgetConfiguration.cs +++ b/Hyperbar/Widgets/WidgetConfiguration.cs @@ -1,6 +1,16 @@ namespace Hyperbar; -public abstract class WidgetConfiguration +public interface IWidgetConfiguration +{ + Guid Id { get; set; } + + string? Name { get; set; } + + string? Description { get; set; } +} + +public abstract class WidgetConfiguration : + IWidgetConfiguration { public Guid Id { get; set; } = Guid.NewGuid(); diff --git a/Hyperbar/Widgets/WidgetContext.cs b/Hyperbar/Widgets/WidgetContext.cs index 78c44b1..7b763d4 100644 --- a/Hyperbar/Widgets/WidgetContext.cs +++ b/Hyperbar/Widgets/WidgetContext.cs @@ -1,30 +1,21 @@ -namespace Hyperbar; +using Microsoft.Extensions.Hosting; -public class WidgetContext(IServiceProvider serviceProvider) : - IInitializer +namespace Hyperbar; +public class WidgetHost : + IWidgetHost { - public IServiceProvider ServiceProvider => serviceProvider; - - public Task InitializeAsync() + public WidgetHost(IHost host) { - throw new NotImplementedException(); + } -} -public class WidgetMonitor : - IInitializer -{ - public Task InitializeAsync() + public void Start() { - throw new NotImplementedException(); + } -} -public class WidgetManager : - IInitializer -{ - public Task InitializeAsync() + public void Stop() { - throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/Hyperbar/Widgets/WidgetEnumerationHandler.cs b/Hyperbar/Widgets/WidgetEnumerationHandler.cs new file mode 100644 index 0000000..8756618 --- /dev/null +++ b/Hyperbar/Widgets/WidgetEnumerationHandler.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Hosting; +using System.Reflection; +using System.Runtime.Loader; + +namespace Hyperbar; + +public class WidgetEnumerationHandler(IHostEnvironment hostEnvironment, + IMediator mediator) : + INotificationHandler> +{ + public Task Handle(Enumerate notification, + CancellationToken cancellationToken) + { + string extensionsDirectory = Path.Combine(hostEnvironment.ContentRootPath, "Extensions"); + if (Directory.Exists(extensionsDirectory)) + { + List assemblyPaths = + [ + .. Directory.GetDirectories(extensionsDirectory) + .AsParallel() + .SelectMany(assemblyDirectory => Directory.GetFiles(assemblyDirectory, "*.dll")) + ]; + + Parallel.ForEach(assemblyPaths, (string assemblyPath) => + { + Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); + mediator.PublishAsync(new Created(assembly)); + }); + } + + return Task.CompletedTask; + } +} diff --git a/Hyperbar/Widgets/WidgetEnumerator.cs b/Hyperbar/Widgets/WidgetEnumerator.cs deleted file mode 100644 index 1b8260f..0000000 --- a/Hyperbar/Widgets/WidgetEnumerator.cs +++ /dev/null @@ -1,11 +0,0 @@ - -namespace Hyperbar; - -public class WidgetEnumerator() : - IEnumerator -{ - public IEnumerable Next() - { - return null; - } -} diff --git a/Hyperbar/Widgets/WidgetFactory.cs b/Hyperbar/Widgets/WidgetFactory.cs new file mode 100644 index 0000000..8f14ea8 --- /dev/null +++ b/Hyperbar/Widgets/WidgetFactory.cs @@ -0,0 +1,15 @@ +namespace Hyperbar; + +public class WidgetFactory : + IFactory +{ + public IWidget? Create(Type value) + { + if (Activator.CreateInstance(value) is IWidget widget) + { + return widget; + } + + return default; + } +} diff --git a/Hyperbar/Widgets/WidgetHandler.cs b/Hyperbar/Widgets/WidgetHandler.cs new file mode 100644 index 0000000..66f9fa3 --- /dev/null +++ b/Hyperbar/Widgets/WidgetHandler.cs @@ -0,0 +1,23 @@ +namespace Hyperbar; + +public class WidgetHandler(IWidgetServiceCollection serviceCollection, + IMediator mediator) : + INotificationHandler> +{ + public Task Handle(Created notification, + CancellationToken cancellationToken) + { + if(notification.Value is IWidget widget) + { + IWidgetBuilder widgetBuilder = widget.Create(); + if (widgetBuilder is IWidgetServiceBuilder serviceBuilder) + { + serviceBuilder.ConfigureWidgetServices(serviceCollection); + } + + widgetBuilder.Build(); + } + + return Task.CompletedTask; + } +} diff --git a/Hyperbar/Widgets/WidgetManager.cs b/Hyperbar/Widgets/WidgetManager.cs new file mode 100644 index 0000000..538c9f5 --- /dev/null +++ b/Hyperbar/Widgets/WidgetManager.cs @@ -0,0 +1,11 @@ +namespace Hyperbar; + +public class WidgetInitializer(IMediator mediator) : + IInitializer +{ + public Task InitializeAsync() + { + mediator.PublishAsync>(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Hyperbar/Widgets/WidgetMonitor.cs b/Hyperbar/Widgets/WidgetMonitor.cs new file mode 100644 index 0000000..57a73f6 --- /dev/null +++ b/Hyperbar/Widgets/WidgetMonitor.cs @@ -0,0 +1,10 @@ +namespace Hyperbar; + +public class WidgetMonitor : + IInitializer +{ + public Task InitializeAsync() + { + throw new NotImplementedException(); + } +} diff --git a/Hyperbar/Widgets/WidgetServiceCollection.cs b/Hyperbar/Widgets/WidgetServiceCollection.cs new file mode 100644 index 0000000..79c7337 --- /dev/null +++ b/Hyperbar/Widgets/WidgetServiceCollection.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Hyperbar; + +public class WidgetServiceCollection : + IWidgetServiceCollection +{ + public WidgetServiceCollection(Action configureDelegate) + { + Services = new ServiceCollection(); + configureDelegate.Invoke(Services); + } + + public IServiceCollection Services { get; private set; } +} \ No newline at end of file