diff --git a/Hyperbar.Windows.Contextual/ContextualWidgetViewModel.cs b/Hyperbar.Windows.Contextual/ContextualWidgetViewModel.cs index c9028fe..90b0190 100644 --- a/Hyperbar.Windows.Contextual/ContextualWidgetViewModel.cs +++ b/Hyperbar.Windows.Contextual/ContextualWidgetViewModel.cs @@ -1,18 +1,13 @@ - -namespace Hyperbar.Widget.Contextual; +namespace Hyperbar.Widget.Contextual; -public class ContextualWidgetViewModel : - ObservableCollectionViewModel, +public class ContextualWidgetViewModel(ITemplateFactory templateFactory, + IServiceFactory serviceFactory, + IMediator mediator, + IDisposer disposer, + IEnumerable items) : + ObservableCollectionViewModel(serviceFactory, mediator, disposer, items), IWidgetViewModel, ITemplatedViewModel { - public ContextualWidgetViewModel(ITemplateFactory templateFactory, - IServiceFactory serviceFactory, - IMediator mediator, - IEnumerable items) : base(serviceFactory, mediator, items) - { - TemplateFactory = templateFactory; - } - - public ITemplateFactory TemplateFactory { get; } + public ITemplateFactory TemplateFactory => templateFactory; } \ No newline at end of file diff --git a/Hyperbar.Windows.MediaController/MediaController.cs b/Hyperbar.Windows.MediaController/MediaController.cs index dd1440f..666e24d 100644 --- a/Hyperbar.Windows.MediaController/MediaController.cs +++ b/Hyperbar.Windows.MediaController/MediaController.cs @@ -3,22 +3,17 @@ namespace Hyperbar.Windows.Primary; public class MediaController : - INotificationHandler, - IDisposable + INotificationHandler { - public MediaController(GlobalSystemMediaTransportControlsSession session) - { + private readonly GlobalSystemMediaTransportControlsSession session; + public MediaController(GlobalSystemMediaTransportControlsSession session, + IMediator mediator) + { + this.session = session; + mediator.Subscribe(this); } - public void Dispose() - { - throw new NotImplementedException(); - } - - public ValueTask Handle(PlayRequest notification, - CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + public async ValueTask Handle(PlayRequest notification, CancellationToken cancellationToken) => + await session.TryPlayAsync(); } \ No newline at end of file diff --git a/Hyperbar.Windows.MediaController/MediaControllerInitializer.cs b/Hyperbar.Windows.MediaController/MediaControllerInitializer.cs deleted file mode 100644 index 303a93f..0000000 --- a/Hyperbar.Windows.MediaController/MediaControllerInitializer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Windows.Media.Control; - -namespace Hyperbar.Windows.Primary; - -public class MediaControllerInitializer : - IInitializer -{ - private readonly List sessions = []; - - public async Task InitializeAsync() - { - GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager = - await GlobalSystemMediaTransportControlsSessionManager.RequestAsync(); - - mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged; - IReadOnlyList sessions = - mediaTransportControlsSessionManager.GetSessions(); - - foreach (var session in sessions) - { - this.sessions.Add(session); - } - } - - private void OnSessionsChanged(GlobalSystemMediaTransportControlsSessionManager sender, - SessionsChangedEventArgs args) - { - IReadOnlyList sessions = - sender.GetSessions(); - - foreach (var session in this.sessions.ToList()) - { - if (!sessions.Contains(session)) - { - this.sessions.Remove(session); - } - } - - foreach (var session in sessions) - { - if (!this.sessions.Contains(session)) - { - this.sessions.Add(session); - } - } - } -} diff --git a/Hyperbar.Windows.MediaController/MediaControllerManager.cs b/Hyperbar.Windows.MediaController/MediaControllerManager.cs new file mode 100644 index 0000000..fdfe3f6 --- /dev/null +++ b/Hyperbar.Windows.MediaController/MediaControllerManager.cs @@ -0,0 +1,93 @@ +using System.Collections.Concurrent; +using System.Threading.Channels; +using Windows.Media.Control; + +namespace Hyperbar.Windows.Primary; + +public class MediaControllerManager : + IInitializer +{ + private readonly ConcurrentDictionary cachedSessions = []; + private readonly IMediator mediator; + private readonly Queue mediaControllers; + private readonly IServiceFactory serviceFactory; + + public MediaControllerManager(IServiceFactory serviceFactory, + IMediator mediator, + Queue mediaControllers) + { + this.serviceFactory = serviceFactory; + this.mediator = mediator; + this.mediaControllers = mediaControllers; + } + + private Channel d; + public async Task InitializeAsync() + { + d = Channel.CreateUnbounded(); + + _ = Task.Run(async () => { + + await foreach (var coordinates in d.Reader.ReadAllAsync()) + { + Console.WriteLine(coordinates); + } + }); + + + GlobalSystemMediaTransportControlsSessionManager mediaTransportControlsSessionManager = + await GlobalSystemMediaTransportControlsSessionManager.RequestAsync(); + mediaTransportControlsSessionManager.SessionsChanged += OnSessionsChanged; + + IReadOnlyList sessions = + mediaTransportControlsSessionManager.GetSessions(); + + foreach (GlobalSystemMediaTransportControlsSession session in sessions) + { + await InitializeSessionAsync(session); + } + } + + private async Task InitializeSessionAsync(GlobalSystemMediaTransportControlsSession session) + { + if (serviceFactory.Create(session) is MediaController mediaController) + { + await d.Writer.WriteAsync(mediaController); + + mediaControllers.Enqueue(mediaController); + + cachedSessions.TryAdd(session, mediaController); + await mediator.PublishAsync(new Created(mediaController)); + } + } + + private async void OnSessionsChanged(GlobalSystemMediaTransportControlsSessionManager sender, + SessionsChangedEventArgs args) + { + IReadOnlyList sessions = + sender.GetSessions(); + + foreach (KeyValuePair session in + cachedSessions) + { + if (!sessions.Contains(session.Key)) + { + cachedSessions.TryRemove(session); + } + } + + foreach (GlobalSystemMediaTransportControlsSession session in sessions) + { + await InitializeSessionAsync(session); + } + } + + private void RemoveSession(GlobalSystemMediaTransportControlsSession session) + { + if (serviceFactory.Create(session) is MediaController mediaController) + { + cachedSessions.TryAdd(session, mediaController); + + } + } +} diff --git a/Hyperbar.Windows.MediaController/MediaControllerViewModel.cs b/Hyperbar.Windows.MediaController/MediaControllerViewModel.cs index 622d169..08e3fe4 100644 --- a/Hyperbar.Windows.MediaController/MediaControllerViewModel.cs +++ b/Hyperbar.Windows.MediaController/MediaControllerViewModel.cs @@ -1,20 +1,17 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; namespace Hyperbar.Windows.Primary; -public partial class MediaInformationViewModel : - WidgetComponentViewModel -{ - [ObservableProperty] - private string title = "this is a test"; - - [ObservableProperty] - private string description = "this is a test description"; - - public MediaInformationViewModel(ITemplateFactory templateFactory) : base(templateFactory) - { - } -} +//public class MediaControllerViewModelFactory(IServiceFactory service, +// IMediator mediator, +// Queue mediaControllers) : +// IFactory +//{ +// public MediaControllerViewModel Create() +// { +// throw new NotImplementedException(); +// } +//} public class MediaControllerViewModel : ObservableCollectionViewModel, @@ -22,7 +19,8 @@ public class MediaControllerViewModel : { public MediaControllerViewModel(ITemplateFactory templateFactory, IServiceFactory serviceFactory, - IMediator mediator) : base(serviceFactory, mediator) + IMediator mediator, + IDisposer disposer) : base(serviceFactory, mediator, disposer) { TemplateFactory = templateFactory; diff --git a/Hyperbar.Windows.MediaController/MediaControllerWidgetProvider.cs b/Hyperbar.Windows.MediaController/MediaControllerWidgetProvider.cs index 0794183..464c377 100644 --- a/Hyperbar.Windows.MediaController/MediaControllerWidgetProvider.cs +++ b/Hyperbar.Windows.MediaController/MediaControllerWidgetProvider.cs @@ -9,10 +9,8 @@ public class MediaControllerWidgetProvider : { public void Create(HostBuilderContext comtext, IServiceCollection services) => services.AddWidgetTemplate() - .AddTransient() - .AddContentTemplate() - .AddContentTemplate() - .AddContentTemplate() - .AddContentTemplate(); + .AddSingleton>() + .AddSingleton() + .AddContentTemplate(); } \ No newline at end of file diff --git a/Hyperbar.Windows.MediaController/MediaControllerWidgetViewModel.cs b/Hyperbar.Windows.MediaController/MediaControllerWidgetViewModel.cs index dd53641..e8a5d87 100644 --- a/Hyperbar.Windows.MediaController/MediaControllerWidgetViewModel.cs +++ b/Hyperbar.Windows.MediaController/MediaControllerWidgetViewModel.cs @@ -3,8 +3,8 @@ public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory, IServiceFactory serviceFactory, IMediator mediator, - IEnumerable items) : - ObservableCollectionViewModel(serviceFactory, mediator, items), + IDisposer disposer) : + ObservableCollectionViewModel(serviceFactory, mediator, disposer), IWidgetViewModel, ITemplatedViewModel { diff --git a/Hyperbar.Windows.MediaController/MediaInformationViewModel.cs b/Hyperbar.Windows.MediaController/MediaInformationViewModel.cs new file mode 100644 index 0000000..e7f2ad2 --- /dev/null +++ b/Hyperbar.Windows.MediaController/MediaInformationViewModel.cs @@ -0,0 +1,17 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Hyperbar.Windows.Primary; + +public partial class MediaInformationViewModel : + WidgetComponentViewModel +{ + [ObservableProperty] + private string title = "this is a test"; + + [ObservableProperty] + private string description = "this is a test description"; + + public MediaInformationViewModel(IServiceFactory serviceFactory, IMediator mediator, IDisposer disposer, ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer, templateFactory) + { + } +} diff --git a/Hyperbar.Windows.Primary/Hyperbar.Widget.Primary.csproj b/Hyperbar.Windows.Primary/Hyperbar.Widget.Primary.csproj index 4a8f052..b84d4ea 100644 --- a/Hyperbar.Windows.Primary/Hyperbar.Widget.Primary.csproj +++ b/Hyperbar.Windows.Primary/Hyperbar.Widget.Primary.csproj @@ -7,6 +7,7 @@ + diff --git a/Hyperbar.Windows.Primary/IPrimaryCommandConfiguration.cs b/Hyperbar.Windows.Primary/IPrimaryCommandConfiguration.cs index 69750ea..c964863 100644 --- a/Hyperbar.Windows.Primary/IPrimaryCommandConfiguration.cs +++ b/Hyperbar.Windows.Primary/IPrimaryCommandConfiguration.cs @@ -6,7 +6,7 @@ namespace Hyperbar.Windows.Primary; [JsonDerivedType(typeof(ProcessCommandConfiguration), typeDiscriminator: "ProcessCommand")] public class PrimaryCommandConfiguration { - public required string Id { get; set; } + public required Guid Id { get; set; } public required string Icon { get; set; } } \ No newline at end of file diff --git a/Hyperbar.Windows.Primary/PrimaryWidgetConfiguration.cs b/Hyperbar.Windows.Primary/PrimaryWidgetConfiguration.cs index f443dca..43a6370 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidgetConfiguration.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidgetConfiguration.cs @@ -5,6 +5,6 @@ public class PrimaryWidgetConfiguration : { public static PrimaryWidgetConfiguration Defaults => new() { - new KeyAcceleratorCommandConfiguration { Id = $"{Guid.NewGuid()}", Icon = "\uE720", Key = 91, Modifiers = [] } + new KeyAcceleratorCommandConfiguration { Id = Guid.NewGuid(), Icon = "\uE720", Key = 91, Modifiers = [] } }; } \ No newline at end of file diff --git a/Hyperbar.Windows.Primary/PrimaryWidgetProvider.cs b/Hyperbar.Windows.Primary/PrimaryWidgetProvider.cs index 416cfe2..1c4855a 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidgetProvider.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidgetProvider.cs @@ -8,9 +8,10 @@ public class PrimaryWidgetProvider : { public void Create(HostBuilderContext comtext, IServiceCollection services) => services.AddConfiguration() - .AddTransient>, WidgetComponentViewModelFactory>() + .AddTransient, WidgetComponentViewModelEnumerator>() + .AddTransient, WidgetComponentViewModelFactory>() .AddWidgetTemplate() .AddNotificationPipeline, - ValueChanging>>(); + CollectionChanged>>(); } \ No newline at end of file diff --git a/Hyperbar.Windows.Primary/PrimaryWidgetViewModel.cs b/Hyperbar.Windows.Primary/PrimaryWidgetViewModel.cs index 44656db..81ca47c 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidgetViewModel.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidgetViewModel.cs @@ -4,8 +4,9 @@ namespace Hyperbar.Windows.Primary; public class PrimaryWidgetViewModel(ITemplateFactory templateFactory, IServiceFactory serviceFactory, IMediator mediator, - IFactory> factory) : - ObservableCollectionViewModel(serviceFactory, mediator, factory), + IDisposer disposer, + IViewModelEnumerator enumerator) : + ObservableCollectionViewModel(serviceFactory, mediator, disposer, enumerator), IWidgetViewModel, ITemplatedViewModel { diff --git a/Hyperbar.Windows.Primary/WidgetComponentViewModelCollectionFactory.cs b/Hyperbar.Windows.Primary/WidgetComponentViewModelCollectionFactory.cs new file mode 100644 index 0000000..ef0e3e7 --- /dev/null +++ b/Hyperbar.Windows.Primary/WidgetComponentViewModelCollectionFactory.cs @@ -0,0 +1,28 @@ +using CommunityToolkit.Mvvm.Input; + +namespace Hyperbar.Windows.Primary; + +public class WidgetComponentViewModelFactory(IServiceFactory service, + IMediator mediator) : + IViewModelFactory +{ + public async ValueTask CreateAsync(PrimaryCommandConfiguration value) + { + if (value is KeyAcceleratorCommandConfiguration keyAcceleratorCommand) + { + return await ValueTask.FromResult(service.Create(keyAcceleratorCommand.Id, keyAcceleratorCommand.Icon, + new RelayCommand(async () => await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey) + keyAcceleratorCommand.Key, keyAcceleratorCommand.Modifiers? + .Select(modifier => (VirtualKey)modifier).ToArray()))))); + } + + if (value is ProcessCommandConfiguration commandConfiguration) + { + return await ValueTask.FromResult(service.Create(commandConfiguration.Id, + commandConfiguration.Icon, new RelayCommand(async () => + await mediator.SendAsync(new ProcessRequest(commandConfiguration.Path))))); + } + + return default; + } +} \ No newline at end of file diff --git a/Hyperbar.Windows.Primary/WidgetComponentViewModelEnumerator.cs b/Hyperbar.Windows.Primary/WidgetComponentViewModelEnumerator.cs new file mode 100644 index 0000000..bea8ffc --- /dev/null +++ b/Hyperbar.Windows.Primary/WidgetComponentViewModelEnumerator.cs @@ -0,0 +1,14 @@ +namespace Hyperbar.Windows.Primary; + +public class WidgetComponentViewModelEnumerator(PrimaryWidgetConfiguration configuration, + IViewModelFactory factory) : + IViewModelEnumerator +{ + public async IAsyncEnumerable Next() + { + foreach (PrimaryCommandConfiguration item in configuration) + { + yield return await factory.CreateAsync(item); + } + } +} diff --git a/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs b/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs deleted file mode 100644 index a317906..0000000 --- a/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Hyperbar.Windows.Primary; - -public class WidgetComponentViewModelFactory(PrimaryWidgetConfiguration configuration, - IServiceFactory service, - IMediator mediator) : - IFactory> -{ - public IEnumerable Create() - { - foreach (PrimaryCommandConfiguration item in configuration) - { - if (item is KeyAcceleratorCommandConfiguration keyAcceleratorCommandConfiguration) - { - yield return service.Create(keyAcceleratorCommandConfiguration.Icon, new Action(async () => - await mediator.SendAsync(new KeyAcceleratorRequest((VirtualKey)keyAcceleratorCommandConfiguration.Key, - keyAcceleratorCommandConfiguration.Modifiers?.Select(modifier => (VirtualKey)modifier).ToArray())))); - } - - if (item is ProcessCommandConfiguration processCommandConfiguration) - { - yield return service.Create(processCommandConfiguration.Icon, new Action(async () => - await mediator.SendAsync(new ProcessRequest(processCommandConfiguration.Path)))); - } - } - } -} \ No newline at end of file diff --git a/Hyperbar.Windows/App.xaml.cs b/Hyperbar.Windows/App.xaml.cs index 5ddee42..f4b0f78 100644 --- a/Hyperbar.Windows/App.xaml.cs +++ b/Hyperbar.Windows/App.xaml.cs @@ -36,6 +36,8 @@ public partial class App : new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); services.AddSingleton(); + services.AddSingleton(); + services.AddHostedService(); services.AddTransient(); @@ -45,7 +47,7 @@ public partial class App : services.AddContentTemplate(); - services.AddWidgetProvider(); + //services.AddWidgetProvider(); services.AddWidgetProvider(); services.AddTransient(provider => diff --git a/Hyperbar.Windows/Hyperbar.Windows.csproj b/Hyperbar.Windows/Hyperbar.Windows.csproj index 8134743..cf6c2ee 100644 --- a/Hyperbar.Windows/Hyperbar.Windows.csproj +++ b/Hyperbar.Windows/Hyperbar.Windows.csproj @@ -30,6 +30,7 @@ + diff --git a/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs b/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs index 55014a5..4b33438 100644 --- a/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs +++ b/Hyperbar.Windows/Lifecycles/IServiceCollectionExtensions.cs @@ -35,6 +35,7 @@ namespace Hyperbar.Windows isolatedServices.AddTransient(); isolatedServices.AddSingleton(); + isolatedServices.AddSingleton(); isolatedServices.AddSingleton(); diff --git a/Hyperbar.Windows/Views/WidgetButtonView.xaml b/Hyperbar.Windows/Views/WidgetButtonView.xaml index e8e04ba..404e20f 100644 --- a/Hyperbar.Windows/Views/WidgetButtonView.xaml +++ b/Hyperbar.Windows/Views/WidgetButtonView.xaml @@ -17,8 +17,8 @@ Width="{StaticResource ButtonWidth}" Height="{StaticResource ButtonHeight}" Padding="{StaticResource ButtonPadding}" - FontSize="16" Command="{Binding Click}" Content="{Binding Icon}" - FontFamily="{StaticResource SymbolThemeFontFamily}" /> + FontFamily="{StaticResource SymbolThemeFontFamily}" + FontSize="16" /> \ No newline at end of file diff --git a/Hyperbar.Windows/Views/WidgetView.xaml b/Hyperbar.Windows/Views/WidgetView.xaml index a1d793c..24db384 100644 --- a/Hyperbar.Windows/Views/WidgetView.xaml +++ b/Hyperbar.Windows/Views/WidgetView.xaml @@ -3,12 +3,20 @@ x:Class="Hyperbar.Windows.WidgetView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" + xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:ui="using:Hyperbar.Windows.UI"> + + + + + + \ No newline at end of file diff --git a/Hyperbar.Windows/Views/WidgetView.xaml.cs b/Hyperbar.Windows/Views/WidgetView.xaml.cs index 45f9a72..d77a38f 100644 --- a/Hyperbar.Windows/Views/WidgetView.xaml.cs +++ b/Hyperbar.Windows/Views/WidgetView.xaml.cs @@ -1,5 +1,4 @@ using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; namespace Hyperbar.Windows; @@ -7,13 +6,5 @@ public sealed partial class WidgetView : UserControl, IWidgetView { - public WidgetView() - { - InitializeComponent(); - } - - protected override void OnKeyDown(KeyRoutedEventArgs e) - { - base.OnKeyDown(e); - } + public WidgetView() => InitializeComponent(); } \ No newline at end of file diff --git a/Hyperbar/Factories/IMapping.cs b/Hyperbar/Factories/IMapping.cs deleted file mode 100644 index fb29cde..0000000 --- a/Hyperbar/Factories/IMapping.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Hyperbar; - -public interface IMapping -{ - TTo Create(); -} diff --git a/Hyperbar/Factories/IViewModelFactory.cs b/Hyperbar/Factories/IViewModelFactory.cs new file mode 100644 index 0000000..fe380a6 --- /dev/null +++ b/Hyperbar/Factories/IViewModelFactory.cs @@ -0,0 +1,6 @@ +namespace Hyperbar; + +public interface IViewModelFactory +{ + ValueTask CreateAsync(TIn value); +} \ No newline at end of file diff --git a/Hyperbar/Lifecycles/IFactory.cs b/Hyperbar/Lifecycles/IFactory.cs deleted file mode 100644 index 9713b38..0000000 --- a/Hyperbar/Lifecycles/IFactory.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Hyperbar; - -public interface IFactory -{ - T Create(); -} \ No newline at end of file diff --git a/Hyperbar/Mediators/NotficationPipelineHandler.cs b/Hyperbar/Mediators/NotficationPipelineHandler.cs index 4607b10..3ea0f47 100644 --- a/Hyperbar/Mediators/NotficationPipelineHandler.cs +++ b/Hyperbar/Mediators/NotficationPipelineHandler.cs @@ -10,9 +10,6 @@ public class NotficationPipelineHandler(IMedi { private readonly IMediator mediator = mediator; - public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken) - { - return mediator.PublishAsync(new ToNotification(), - cancellationToken); - } + public ValueTask Handle(TFromNotification notification, CancellationToken cancellationToken) => + mediator.PublishAsync(new ToNotification(), cancellationToken); } diff --git a/Hyperbar/Views/CollectionChanged.cs b/Hyperbar/Views/CollectionChanged.cs new file mode 100644 index 0000000..5040e6b --- /dev/null +++ b/Hyperbar/Views/CollectionChanged.cs @@ -0,0 +1,3 @@ +namespace Hyperbar; + +public record CollectionChanged : INotification; \ No newline at end of file diff --git a/Hyperbar/Views/CollectionChanging.cs b/Hyperbar/Views/CollectionChanging.cs deleted file mode 100644 index e4ecc13..0000000 --- a/Hyperbar/Views/CollectionChanging.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Collections; - -namespace Hyperbar; - -public record ValueChanging : INotification; diff --git a/Hyperbar/Views/Created.cs b/Hyperbar/Views/Created.cs new file mode 100644 index 0000000..6fa947b --- /dev/null +++ b/Hyperbar/Views/Created.cs @@ -0,0 +1,3 @@ +namespace Hyperbar; + +public record Created(TValue? Value = default) : INotification; diff --git a/Hyperbar/Views/IObservableCollectionViewModel.cs b/Hyperbar/Views/IObservableCollectionViewModel.cs index 21b3853..ab316e7 100644 --- a/Hyperbar/Views/IObservableCollectionViewModel.cs +++ b/Hyperbar/Views/IObservableCollectionViewModel.cs @@ -8,4 +8,4 @@ public interface IObservableCollectionViewModel : IList, IReadOnlyList, INotifyCollectionChanged, - INotificationHandler>>; + IInitializer; diff --git a/Hyperbar/Views/IViewModelEnumerator.cs b/Hyperbar/Views/IViewModelEnumerator.cs new file mode 100644 index 0000000..b2b4188 --- /dev/null +++ b/Hyperbar/Views/IViewModelEnumerator.cs @@ -0,0 +1,6 @@ +namespace Hyperbar; + +public interface IViewModelEnumerator +{ + IAsyncEnumerable Next(); +} diff --git a/Hyperbar/Views/ObservableCollectionViewModel.cs b/Hyperbar/Views/ObservableCollectionViewModel.cs index 31401e7..2b23f5d 100644 --- a/Hyperbar/Views/ObservableCollectionViewModel.cs +++ b/Hyperbar/Views/ObservableCollectionViewModel.cs @@ -1,7 +1,10 @@ using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.Reactive.Disposables; +using System.Windows.Input; namespace Hyperbar; @@ -11,14 +14,19 @@ public partial class ObservableCollectionViewModel : { public ObservableCollection collection = []; private readonly SynchronizationContext? context; + private readonly IViewModelEnumerator? enumerator; private readonly IServiceFactory serviceFactory; + private readonly IDisposer disposer; public ObservableCollectionViewModel(IServiceFactory serviceFactory, - IMediator mediator) + IMediator mediator, + IDisposer disposer) { context = SynchronizationContext.Current; this.serviceFactory = serviceFactory; + this.disposer = disposer; + mediator.Subscribe(this); collection.CollectionChanged += OnCollectionChanged; @@ -26,28 +34,44 @@ public partial class ObservableCollectionViewModel : public ObservableCollectionViewModel(IServiceFactory serviceFactory, IMediator mediator, - IFactory> factory) + IDisposer disposer, + IViewModelEnumerator enumerator) { context = SynchronizationContext.Current; this.serviceFactory = serviceFactory; + this.disposer = disposer; + this.enumerator = enumerator; + mediator.Subscribe(this); collection.CollectionChanged += OnCollectionChanged; - - if (factory is not null && factory.Create() is { } items) + + if (enumerator is not null) { - AddRange(factory.Create()); + Task.Run(async () => + { + await foreach (TItem? item in enumerator.Next()) + { + if (item is not null) + { + Add(item); + } + } + }); } } public ObservableCollectionViewModel(IServiceFactory serviceFactory, IMediator mediator, + IDisposer disposer, IEnumerable items) { context = SynchronizationContext.Current; this.serviceFactory = serviceFactory; + this.disposer = disposer; + mediator.Subscribe(this); collection.CollectionChanged += OnCollectionChanged; @@ -59,6 +83,10 @@ public partial class ObservableCollectionViewModel : public int Count => collection.Count; + public ICommand Initialize => new AsyncRelayCommand(InitializeAsync); + + public bool Initialized { get; private set; } + bool IList.IsFixedSize => false; bool ICollection.IsReadOnly => false; @@ -157,6 +185,7 @@ public partial class ObservableCollectionViewModel : context?.Post(state => Add(item), null); } } + public void Clear() => ClearItems(); public bool Contains(TItem item) => collection.Contains(item); @@ -171,19 +200,21 @@ public partial class ObservableCollectionViewModel : IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)collection).GetEnumerator(); - //public ValueTask Handle(CollectionChanged> notification, - // CancellationToken cancellationToken) - //{ - // context?.Post(state => Clear(), null); - // AddRange(notification.Items); - - // return ValueTask.CompletedTask; - //} - public int IndexOf(TItem item) => collection.IndexOf(item); int IList.IndexOf(object? value) => IsCompatibleObject(value) ? IndexOf((TItem)value!) : -1; + public Task InitializeAsync() + { + if (Initialized) + { + return Task.CompletedTask; + } + + Initialized = true; + return Task.CompletedTask; + } + public void Insert(int index, TItem item) => InsertItem(index, item); void IList.Insert(int index, object? value) @@ -209,6 +240,7 @@ public partial class ObservableCollectionViewModel : Remove((TItem)value!); } } + public void RemoveAt(int index) => RemoveItem(index); protected virtual void ClearItems() => collection.Clear(); @@ -217,7 +249,15 @@ public partial class ObservableCollectionViewModel : { if (value is TItem item) { - collection.Insert(index, item); + disposer.Add(item, Disposable.Create(() => + { + if (Contains(item)) + { + Remove(item); + } + })); + + context?.Post(state => collection.Insert(index, item), null); } } @@ -230,13 +270,9 @@ public partial class ObservableCollectionViewModel : private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args) => CollectionChanged?.Invoke(this, args); - - public ValueTask Handle(ValueChanging> notification, - CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } } -public class ObservableCollectionViewModel(IServiceFactory serviceFactory, IMediator mediator) : - ObservableCollectionViewModel(serviceFactory, mediator); \ No newline at end of file +public class ObservableCollectionViewModel(IServiceFactory serviceFactory, + IMediator mediator, + IDisposer disposer) : + ObservableCollectionViewModel(serviceFactory, mediator, disposer); \ No newline at end of file diff --git a/Hyperbar/Views/Removed.cs b/Hyperbar/Views/Removed.cs new file mode 100644 index 0000000..090a148 --- /dev/null +++ b/Hyperbar/Views/Removed.cs @@ -0,0 +1,3 @@ +namespace Hyperbar; + +public record Removed(TValue Value) : INotification; diff --git a/Hyperbar/Views/WidgetBarViewModel.cs b/Hyperbar/Views/WidgetBarViewModel.cs index 0c09499..2cf9398 100644 --- a/Hyperbar/Views/WidgetBarViewModel.cs +++ b/Hyperbar/Views/WidgetBarViewModel.cs @@ -1,11 +1,11 @@ - -namespace Hyperbar; +namespace Hyperbar; public partial class WidgetBarViewModel(ITemplateFactory templateFactory, IServiceFactory serviceFactory, IMediator mediator, + IDisposer disposer, IEnumerable items) : - ObservableCollectionViewModel(serviceFactory, mediator, items), + ObservableCollectionViewModel(serviceFactory, mediator, disposer, items), ITemplatedViewModel { public ITemplateFactory TemplateFactory => templateFactory; diff --git a/Hyperbar/Views/WidgetButtonViewModel.cs b/Hyperbar/Views/WidgetButtonViewModel.cs index b2d8dc6..d506541 100644 --- a/Hyperbar/Views/WidgetButtonViewModel.cs +++ b/Hyperbar/Views/WidgetButtonViewModel.cs @@ -3,23 +3,20 @@ using CommunityToolkit.Mvvm.Input; namespace Hyperbar; -public partial class WidgetButtonViewModel : - WidgetComponentViewModel +public partial class WidgetButtonViewModel(IServiceFactory serviceFactory, + IMediator mediator, + IDisposer disposer, + ITemplateFactory templateFactory, + Guid id = default, + string? icon = null, + RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory) { [ObservableProperty] - private string? icon; + private Guid id = id; [ObservableProperty] - private IRelayCommand? click; + private string? icon = icon; - public WidgetButtonViewModel(ITemplateFactory templateFactory, - string? icon = null, - Action? action = null) : base(templateFactory) - { - this.icon = icon; - if (action is not null) - { - click = new RelayCommand(action); - } - } + [ObservableProperty] + private IRelayCommand? click = command; } \ No newline at end of file diff --git a/Hyperbar/Views/WidgetComponentViewModel.cs b/Hyperbar/Views/WidgetComponentViewModel.cs index 39dc706..47c3749 100644 --- a/Hyperbar/Views/WidgetComponentViewModel.cs +++ b/Hyperbar/Views/WidgetComponentViewModel.cs @@ -2,8 +2,19 @@ namespace Hyperbar; -public partial class WidgetComponentViewModel(ITemplateFactory templateFactory) : +public class ObservableViewModel(IServiceFactory serviceFactory, + IMediator mediator, + IDisposer disposer) : ObservableObject, + IDisposable +{ + public void Dispose() => disposer.Dispose(this); +} + +public partial class WidgetComponentViewModel(IServiceFactory serviceFactory, + IMediator mediator, + IDisposer disposer, + ITemplateFactory templateFactory) : ObservableViewModel(serviceFactory, mediator, disposer), IWidgetComponentViewModel, ITemplatedViewModel { diff --git a/Hyperbar/Views/WidgetContainerViewModel.cs b/Hyperbar/Views/WidgetContainerViewModel.cs index a35f702..f865b29 100644 --- a/Hyperbar/Views/WidgetContainerViewModel.cs +++ b/Hyperbar/Views/WidgetContainerViewModel.cs @@ -6,9 +6,10 @@ namespace Hyperbar; public partial class WidgetContainerViewModel(ITemplateFactory templateFactory, IServiceFactory serviceFactory, IMediator mediator, + IDisposer disposer, IEnumerable items, bool alternate) : - ObservableCollectionViewModel(serviceFactory, mediator, items), + ObservableCollectionViewModel(serviceFactory, mediator, disposer, items), ITemplatedViewModel { [ObservableProperty]