This commit is contained in:
TheXamlGuy
2024-05-18 20:40:12 +01:00
parent 46fe35fea4
commit 0f9998bc1d
54 changed files with 368 additions and 241 deletions
+8 -4
View File
@@ -66,7 +66,9 @@ public partial class App : Application
{ {
args.UseSqlite($"{connection.Value}"); args.UseSqlite($"{connection.Value}");
} }
}); });
services.AddHandler<QueryContainerHandler>(ServiceLifetime.Singleton);
services.AddHandler<OpenContainerHandler>(); services.AddHandler<OpenContainerHandler>();
@@ -78,10 +80,12 @@ public partial class App : Application
services.AddTemplate<OpenContainerViewModel, OpenView>("OpenContainer"); services.AddTemplate<OpenContainerViewModel, OpenView>("OpenContainer");
services.AddTemplate<ContainerViewModel, ContainerView>("Container"); services.AddScoped<ContainerViewModelConfiguration>();
services.AddHandler<ContainerViewModelHandler>();
services.AddTemplate<SearchHeaderViewModel, SearchHeaderView>("SearchHeader"); services.AddTemplate<ContainerViewModel, ContainerView>("Container");
services.AddHandler<EnumerateContainerViewModelHandler>();
services.AddTemplate<SearchContainerActionViewModel, SearchContainerActionView>();
services.AddTemplate<ContainerHeaderViewModel, ContainerHeaderView>("ContainerHeader"); services.AddTemplate<ContainerHeaderViewModel, ContainerHeaderView>("ContainerHeader");
services.AddTemplate<CreateItemActionViewModel, CreateItemActionView>(); services.AddTemplate<CreateItemActionViewModel, CreateItemActionView>();
-1
View File
@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Toolkit.Foundation; using Toolkit.Foundation;
@@ -79,9 +79,6 @@
<Compile Update="ContainerNavigationView.axaml.cs"> <Compile Update="ContainerNavigationView.axaml.cs">
<DependentUpon>ContainerNavigationView.axaml</DependentUpon> <DependentUpon>ContainerNavigationView.axaml</DependentUpon>
</Compile> </Compile>
<Compile Update="SearchHeaderView.axaml.cs">
<DependentUpon>SearchHeaderView.axaml</DependentUpon>
</Compile>
<Compile Update="ContainerView.axaml.cs"> <Compile Update="ContainerView.axaml.cs">
<DependentUpon>ContainerView.axaml</DependentUpon> <DependentUpon>ContainerView.axaml</DependentUpon>
</Compile> </Compile>
+1 -28
View File
@@ -4,32 +4,5 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Bitvault" xmlns:vm="using:Bitvault"
x:DataType="vm:ContainerHeaderViewModel"> x:DataType="vm:ContainerHeaderViewModel">
<UserControl.Resources> <ItemsControl ItemTemplate="{ReflectionBinding Template}" ItemsSource="{Binding}" />
<x:Double x:Key="ButtonWidth">40</x:Double>
<x:Double x:Key="ButtonHeight">36</x:Double>
<SolidColorBrush x:Key="ButtonBackground" Color="{DynamicResource SubtleFillColorTransparent}" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{DynamicResource SubtleFillColorSecondary}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="{DynamicResource SubtleFillColorTertiary}" />
<Thickness x:Key="ButtonBorderThemeThickness">0</Thickness>
</UserControl.Resources>
<Grid ColumnDefinitions="*,Auto">
<TextBlock
Grid.Column="0"
Margin="16,0,0,0"
VerticalAlignment="Center"
Text="{Binding Value}"
Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<ItemsControl
Grid.Column="1"
Margin="0,0,4,0"
VerticalAlignment="Center"
ItemTemplate="{ReflectionBinding Template}"
ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</UserControl> </UserControl>
+13 -10
View File
@@ -5,17 +5,20 @@
xmlns:vm="using:Bitvault" xmlns:vm="using:Bitvault"
x:DataType="vm:ContainerViewModel"> x:DataType="vm:ContainerViewModel">
<Grid ColumnDefinitions="320,Auto,*" RowDefinitions="Auto,*"> <Grid ColumnDefinitions="320,Auto,*" RowDefinitions="Auto,*">
<ContentControl <Grid
x:Name="Title"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.ColumnSpan="3"
Height="40" Height="44"
Margin="4,4,0,0"> ColumnDefinitions="*, Auto">
<Interaction.Behaviors> <ContentControl Grid.Column="0" VerticalAlignment="Center">
<AttachedBehaviour> <Interaction.Behaviors>
<NavigateAction Region="{Binding $self}" Route="ContainerHeader" /> <AttachedBehaviour>
</AttachedBehaviour> <NavigateAction Region="{Binding $self}" Route="ContainerHeader" />
</Interaction.Behaviors> </AttachedBehaviour>
</ContentControl> </Interaction.Behaviors>
</ContentControl>
</Grid>
<ListBox <ListBox
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
+12
View File
@@ -1,8 +1,20 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Windowing;
namespace Bitvault.Avalonia; namespace Bitvault.Avalonia;
public partial class ContainerView : UserControl public partial class ContainerView : UserControl
{ {
public ContainerView() => InitializeComponent(); public ContainerView() => InitializeComponent();
protected override void OnLoaded(RoutedEventArgs args)
{
base.OnLoaded(args);
if (VisualRoot is AppWindow appWindow)
{
Title.ColumnDefinitions[1].Width = new GridLength(appWindow.TitleBar.RightInset, GridUnitType.Pixel);
}
}
} }
+2 -2
View File
@@ -14,11 +14,11 @@
</NavigationView.Resources> </NavigationView.Resources>
<Frame> <Frame>
<Interaction.Behaviors> <Interaction.Behaviors>
<EventTriggerBehavior EventName="Loaded"> <AttachedBehaviour>
<NavigateRegionAction Name="Main"> <NavigateRegionAction Name="Main">
<NavigateAction Region="Main" Route="Container" /> <NavigateAction Region="Main" Route="Container" />
</NavigateRegionAction> </NavigateRegionAction>
</EventTriggerBehavior> </AttachedBehaviour>
</Interaction.Behaviors> </Interaction.Behaviors>
</Frame> </Frame>
</NavigationView> </NavigationView>
@@ -0,0 +1,34 @@
<UserControl
x:Class="Bitvault.Avalonia.SearchContainerActionView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Bitvault"
x:DataType="vm:SearchContainerActionViewModel">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="TextControlBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="TextControlBorderBrushPointerOver" ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="TextControlBorderBrushFocused" ResourceKey="ControlStrokeColorDefaultBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="TextControlBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="TextControlBorderBrushPointerOver" ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="TextControlBorderBrushFocused" ResourceKey="ControlStrokeColorDefaultBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<AutoCompleteBox
MaxWidth="500"
CornerRadius="16"
Text="{Binding Value}"
Watermark="Search">
<Interaction.Behaviors>
<KeyBindingTriggerBehaviour Gesture="Enter">
<InvokeCommandAction Command="{Binding InvokeCommand}" />
</KeyBindingTriggerBehaviour>
</Interaction.Behaviors>
</AutoCompleteBox>
</UserControl>
@@ -0,0 +1,8 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class SearchContainerActionView : UserControl
{
public SearchContainerActionView() => InitializeComponent();
}
-6
View File
@@ -1,6 +0,0 @@
<UserControl
x:Class="Bitvault.Avalonia.SearchHeaderView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<AutoCompleteBox VerticalAlignment="Center" />
</UserControl>
@@ -1,8 +0,0 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class SearchHeaderView : UserControl
{
public SearchHeaderView() => InitializeComponent();
}
+1 -1
View File
@@ -8,7 +8,7 @@ public partial class ArchiveItemActionViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
[RelayCommand] [RelayCommand]
public void Invoke() => Publisher.Publish(Archive.As<Item>()); public void Invoke() => Publisher.Publish(Archive.As<Item>());
+1 -1
View File
@@ -8,7 +8,7 @@ public partial class ConfirmItemActionViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
[RelayCommand] [RelayCommand]
+51 -11
View File
@@ -1,18 +1,16 @@
using Bitvault.Data; using Bitvault.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class ConfirmItemHandler(IMediator mediator, public class EditItemHander(IDbContextFactory<ContainerDbContext> dbContextFactory) :
IDbContextFactory<ContainerDbContext> dbContextFactory, IHandler<EditEventArgs<(int, ItemConfiguration)>, bool>
IPublisher publisher) :
INotificationHandler<ConfirmEventArgs<Item>>
{ {
public async Task<bool> Handle(ConfirmEventArgs<Item> args, public async Task<bool> Handle(EditEventArgs<(int, ItemConfiguration)> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
await mediator.Handle<ConfirmEventArgs<Item>, bool>(args);
//if (args.Value is ItemConfiguration configuration) //if (args.Value is ItemConfiguration configuration)
//{ //{
// try // try
@@ -29,9 +27,6 @@ public class ConfirmItemHandler(IMediator mediator,
// if (result is not null) // if (result is not null)
// { // {
// Item item = new() { Id = result.Entity.Id, Name = configuration.Name };
// publisher.Publish(Activated.As(item), cancellationToken);
// return true; // return true;
// } // }
// } // }
@@ -43,9 +38,54 @@ public class ConfirmItemHandler(IMediator mediator,
return false; return false;
} }
}
public async Task Handle(ConfirmEventArgs<Item> args) public class CreateItemHander(IDbContextFactory<ContainerDbContext> dbContextFactory) :
IHandler<CreateEventArgs<ItemConfiguration>, (bool, int)>
{
public async Task<(bool, int)> Handle(CreateEventArgs<ItemConfiguration> args,
CancellationToken cancellationToken)
{ {
ItemHeaderConfiguration? headerConfiguration = await mediator.Handle<ConfirmEventArgs<Item>, ItemHeaderConfiguration>(args); if (args.Value is ItemConfiguration configuration)
{
try
{
using ContainerDbContext context = dbContextFactory.CreateDbContext();
EntityEntry<ItemEntry>? result = null;
await Task.Run(async () =>
{
result = await context.AddAsync(new ItemEntry { Name = configuration.Name }, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
}, cancellationToken);
if (result is not null)
{
return (false, -1);
}
}
catch
{
}
}
return (false, -1);
}
}
public class ConfirmItemHandler(IMediator mediator,
IValueStore<Item> valueStore,
IPublisher publisher) :
INotificationHandler<ConfirmEventArgs<Item>>
{
public async Task Handle(ConfirmEventArgs<Item> args)
{
(bool result, int index) result = await mediator.Handle<CreateEventArgs<ItemConfiguration>,
(bool, int)>(new CreateEventArgs<ItemConfiguration>(new ItemConfiguration()));
ItemHeaderConfiguration? configuration = await mediator.Handle<ConfirmEventArgs<Item>,
ItemHeaderConfiguration>(args);
} }
} }
+1 -2
View File
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
+2 -1
View File
@@ -2,7 +2,8 @@
namespace Bitvault; namespace Bitvault;
public record ContainerConfiguration : ComponentConfiguration public record ContainerConfiguration :
ComponentConfiguration
{ {
public string? Name { get; set; } public string? Name { get; set; }
+13 -11
View File
@@ -2,8 +2,8 @@
namespace Bitvault; namespace Bitvault;
public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<string, IDisposable>, public partial class ContainerHeaderViewModel :
INotificationHandler<RequestEventArgs<Filter<string>>> ObservableCollection
{ {
public ContainerHeaderViewModel(IServiceProvider provider, public ContainerHeaderViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
@@ -15,18 +15,20 @@ public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<st
{ {
Template = template; Template = template;
Add<CreateItemActionViewModel>(scope: true); Add<SearchContainerActionViewModel>();
//Add<CreateItemActionViewModel>(scope: true);
} }
public IContentTemplate Template { get; set; } public IContentTemplate Template { get; set; }
public Task Handle(RequestEventArgs<Filter<string>> args) //public Task Handle(RequestEventArgs<Filter<string>> args)
{ //{
if (args.Value is Filter<string> filter) // if (args.Value is Filter<string> filter)
{ // {
Value = filter.Value; // Value = filter.Value;
} // }
return Task.CompletedTask; // return Task.CompletedTask;
} //}
} }
+3 -2
View File
@@ -10,12 +10,13 @@ public class ContainerInitializer(IEnumerable<IConfigurationDescriptor<Container
{ {
foreach (IConfigurationDescriptor<ContainerConfiguration> configuration in configurations) foreach (IConfigurationDescriptor<ContainerConfiguration> configuration in configurations)
{ {
if (componentFactory.Create<IContainerComponent, ContainerConfiguration>(configuration.Section, configuration.Value) is IComponentHost host) if (componentFactory.Create<IContainerComponent,
ContainerConfiguration>(configuration.Section, configuration.Value)
is IComponentHost host)
{ {
vaults.Add(host); vaults.Add(host);
await host.StartAsync(); await host.StartAsync();
} }
} }
} }
} }
+11 -11
View File
@@ -4,12 +4,12 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public partial class ContainerNavigationViewModel : public partial class ContainerNavigationViewModel :
ObservableCollectionViewModel<IContainerNavigationViewModel>, ObservableCollection<IContainerNavigationViewModel>,
IMainNavigationViewModel, IMainNavigationViewModel,
INotificationHandler<OpenedEventArgs<Container>>, INotificationHandler<OpenedEventArgs<ContainerToken>>,
INotificationHandler<ClosedEventArgs<Container>>, INotificationHandler<ClosedEventArgs<ContainerToken>>,
INotificationHandler<ActivatedEventArgs<Container>>, INotificationHandler<ActivatedEventArgs<ContainerToken>>,
INotificationHandler<DeactivatedEventArgs<Container>> INotificationHandler<DeactivatedEventArgs<ContainerToken>>
{ {
[ObservableProperty] [ObservableProperty]
private bool activated; private bool activated;
@@ -41,7 +41,7 @@ public partial class ContainerNavigationViewModel :
public IContentTemplate Template { get; set; } public IContentTemplate Template { get; set; }
public Task Handle(OpenedEventArgs<Container> args) public Task Handle(OpenedEventArgs<ContainerToken> args)
{ {
Add<AllNavigationViewModel>("All"); Add<AllNavigationViewModel>("All");
Add<StarredNavigationViewModel>("Starred"); Add<StarredNavigationViewModel>("Starred");
@@ -52,7 +52,7 @@ public partial class ContainerNavigationViewModel :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(ClosedEventArgs<Container> args) public Task Handle(ClosedEventArgs<ContainerToken> args)
{ {
Opened = true; Opened = true;
Clear(); Clear();
@@ -60,9 +60,9 @@ public partial class ContainerNavigationViewModel :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(DeactivatedEventArgs<Container> args) => public Task Handle(DeactivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = false); Task.FromResult(Activated = false);
public Task Handle(ActivatedEventArgs<Container> args) => public Task Handle(ActivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = true); Task.FromResult(Activated = true);
} }
@@ -2,20 +2,20 @@
namespace Bitvault; namespace Bitvault;
public record Container public record ContainerToken
{ {
public Container(string name, string password) public ContainerToken(string name, string password)
{ {
Name = name; Name = name;
Password = password; Password = password;
} }
public Container(string password) public ContainerToken(string password)
{ {
Password = password; Password = password;
} }
public Container() public ContainerToken()
{ {
} }
+28 -11
View File
@@ -1,8 +1,17 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.EntityFrameworkCore;
using System.Collections.ObjectModel;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class ItemCommandCollection : ObservableCollection
{
public void Add<TItem>(IDisposable diposer)
{
}
}
[Enumerate(nameof(ContainerViewModel))] [Enumerate(nameof(ContainerViewModel))]
public partial class ContainerViewModel(IServiceProvider provider, public partial class ContainerViewModel(IServiceProvider provider,
@@ -13,12 +22,10 @@ public partial class ContainerViewModel(IServiceProvider provider,
IDisposer disposer, IDisposer disposer,
IContentTemplate template, IContentTemplate template,
NamedComponent named, NamedComponent named,
string? filter = null) : ObservableCollectionViewModel<ItemNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer), ContainerViewModelConfiguration configuration) : Toolkit.Foundation.ObservableCollection<ItemNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<RequestEventArgs<Filter<string>>> INotificationHandler<NotifyEventArgs<Filter>>,
INotificationHandler<NotifyEventArgs<Search>>
{ {
[ObservableProperty]
private string? filter = filter;
[ObservableProperty] [ObservableProperty]
private string named = $"{named}"; private string named = $"{named}";
@@ -26,21 +33,31 @@ public partial class ContainerViewModel(IServiceProvider provider,
public override async Task OnActivated() public override async Task OnActivated()
{ {
Publisher.Publish(Activated.As<Container>()); Publisher.Publish(Activated.As<ContainerToken>());
await base.OnActivated(); await base.OnActivated();
} }
public override async Task OnDeactivated() public override async Task OnDeactivated()
{ {
Publisher.Publish(Deactivated.As<Container>()); Publisher.Publish(Deactivated.As<ContainerToken>());
await base.OnDeactivated(); await base.OnDeactivated();
} }
public Task Handle(RequestEventArgs<Filter<string>> args) public Task Handle(NotifyEventArgs<Filter> args)
{ {
if (args.Value is Filter<string> filter) if (args.Value is Filter filter)
{ {
Filter = filter.Value; configuration = configuration with { Filter = filter.Value };
Enumerate();
}
return Task.CompletedTask;
}
public Task Handle(NotifyEventArgs<Search> args)
{
if (args.Value is Search search)
{
configuration = configuration with { Query = search.Value };
Enumerate(); Enumerate();
} }
@@ -48,5 +65,5 @@ public partial class ContainerViewModel(IServiceProvider provider,
} }
protected override IEnumerate PrepareEnumeration(object? key) => protected override IEnumerate PrepareEnumeration(object? key) =>
EnumerateEventArgs<ItemNavigationViewModel>.With(new ContainerViewModelConfiguration { Filter = Filter }) with { Key = key }; EnumerateEventArgs<ItemNavigationViewModel>.With(configuration) with { Key = key };
} }
+3 -1
View File
@@ -2,5 +2,7 @@
public record ContainerViewModelConfiguration public record ContainerViewModelConfiguration
{ {
public string? Filter { get; set; } public string? Filter { get; set; } = "All";
public string? Query { get; set; }
} }
-67
View File
@@ -1,67 +0,0 @@
using Bitvault.Data;
using LinqKit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Bitvault;
public class ContainerViewModelHandler(IDbContextFactory<ContainerDbContext> dbContextFactory,
IServiceProvider serviceProvider,
ICache<Item> cache,
IPublisher publisher) :
INotificationHandler<Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration>>
{
public async Task Handle(Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration> args)
{
if (args.Options is ContainerViewModelConfiguration configuration)
{
cache.Clear();
ExpressionStarter<ItemEntry> predicate = PredicateBuilder.New<ItemEntry>(true);
if (configuration.Filter == "All")
{
predicate = predicate.And(x => x.State != 3);
}
if (configuration.Filter == "Starred")
{
predicate = predicate.And(x => x.State != 3 && x.State == 2);
}
if (configuration.Filter == "Archive")
{
predicate = predicate.And(x => x.State == 3);
}
var results = await Task.Run(async () =>
{
using ContainerDbContext context = dbContextFactory.CreateDbContext();
return await context.Set<ItemEntry>().Where(predicate).Select(x => new
{
x.Id,
x.Name
}).OrderBy(x => x.Name).ToListAsync();
});
bool selected = true;
foreach (var result in results)
{
IServiceScope serviceScope = serviceProvider.CreateScope();
IServiceFactory serviceFactory = serviceScope.ServiceProvider.GetRequiredService<IServiceFactory>();
IValueStore<Item> valueStore = serviceScope.ServiceProvider.GetRequiredService<IValueStore<Item>>();
if (serviceFactory.Create<ItemNavigationViewModel>(result.Id, result.Name, "Description " + 1, selected) is ItemNavigationViewModel viewModel)
{
Item item = new() { Id = result.Id, Name = result.Name };
valueStore.Set(item);
publisher.Publish(Create.As(viewModel), nameof(ContainerViewModel));
}
selected = false;
}
}
}
}
+3 -3
View File
@@ -7,12 +7,12 @@ namespace Bitvault;
public class CreateContainerHandler(IContainerFactory componentFactory, public class CreateContainerHandler(IContainerFactory componentFactory,
IPublisher publisher) : IPublisher publisher) :
IHandler<CreateEventArgs<Container>, bool> IHandler<CreateEventArgs<ContainerToken>, bool>
{ {
public async Task<bool> Handle(CreateEventArgs<Container> args, public async Task<bool> Handle(CreateEventArgs<ContainerToken> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (args.Value is Container container && container.Name is { Length: > 0 } name && if (args.Value is ContainerToken container && container.Name is { Length: > 0 } name &&
container.Password is { Length: > 0 } password) container.Password is { Length: > 0 } password)
{ {
if (componentFactory.Create(name) is IComponentHost host) if (componentFactory.Create(name) is IComponentHost host)
@@ -8,5 +8,5 @@ public partial class CreateContainerNavigationViewModel(IServiceProvider provide
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer), Observable(provider, factory, mediator, publisher, subscriber, disposer),
IMainNavigationViewModel; IMainNavigationViewModel;
+2 -2
View File
@@ -10,7 +10,7 @@ public partial class CreateContainerViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer), Observable(provider, factory, mediator, publisher, subscriber, disposer),
IPrimaryConfirmation IPrimaryConfirmation
{ {
[MaybeNull] [MaybeNull]
@@ -22,5 +22,5 @@ public partial class CreateContainerViewModel(IServiceProvider provider,
private string password; private string password;
public async Task<bool> Confirm() => public async Task<bool> Confirm() =>
await Mediator.Handle<CreateEventArgs<Container>, bool>(Create.As(new Container(Name, Password))); await Mediator.Handle<CreateEventArgs<ContainerToken>, bool>(Create.As(new ContainerToken(Name, Password)));
} }
+1 -1
View File
@@ -9,7 +9,7 @@ public partial class CreateItemActionViewModel(IServiceProvider provider,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer, IDisposer disposer,
NamedComponent named) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) NamedComponent named) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
[ObservableProperty] [ObservableProperty]
+1 -1
View File
@@ -8,7 +8,7 @@ public partial class DeleteItemActionViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
[RelayCommand] [RelayCommand]
public void Invoke() => Publisher.Publish(Delete.As<Item>()); public void Invoke() => Publisher.Publish(Delete.As<Item>());
+1 -1
View File
@@ -7,4 +7,4 @@ public partial class DismissItemActionViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer); IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer);
+2 -3
View File
@@ -8,9 +8,8 @@ public partial class EditItemActionViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
[RelayCommand] [RelayCommand]
public void Invoke() => Publisher.Publish(Edit.As<Item>()); public void Invoke() => Publisher.Publish(Edit.As<Item>());
} }
@@ -0,0 +1,45 @@
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Bitvault;
public class EnumerateContainerViewModelHandler(IMediator mediator,
IServiceProvider serviceProvider,
ICache<Item> cache,
IPublisher publisher) :
INotificationHandler<Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration>>
{
public async Task Handle(Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration> args)
{
if (args.Options is ContainerViewModelConfiguration configuration)
{
cache.Clear();
bool selected = true;
if (await mediator.Handle<RequestEventArgs<QueryContainerConfiguration>,
IReadOnlyCollection<(int Id, string? Name)>>(Request.As(new QueryContainerConfiguration
{
Filter = configuration.Filter,
Query = configuration.Query
})) is IReadOnlyCollection<(int Id, string? Name)> results)
{
foreach ((int Id, string? Name) in results)
{
IServiceScope serviceScope = serviceProvider.CreateScope();
IServiceFactory serviceFactory = serviceScope.ServiceProvider.GetRequiredService<IServiceFactory>();
IValueStore<Item> valueStore = serviceScope.ServiceProvider.GetRequiredService<IValueStore<Item>>();
if (serviceFactory.Create<ItemNavigationViewModel>(Id, Name, "Description " + 1, selected) is ItemNavigationViewModel viewModel)
{
Item item = new() { Id = Id, Name = Name };
valueStore.Set(item);
publisher.Publish(Create.As(viewModel), nameof(ContainerViewModel));
}
selected = false;
}
}
}
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
namespace Bitvault; namespace Bitvault;
public record Filter<TValue>(TValue? Value); public record Filter(string? Value);
@@ -4,10 +4,10 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public partial class FilterContainerNavigationViewModel : ObservableViewModel, public partial class FilterContainerNavigationViewModel : Observable,
IContainerNavigationViewModel, IContainerNavigationViewModel,
INotificationHandler<ActivatedEventArgs<Container>>, INotificationHandler<ActivatedEventArgs<ContainerToken>>,
INotificationHandler<DeactivatedEventArgs<Container>> INotificationHandler<DeactivatedEventArgs<ContainerToken>>
{ {
[ObservableProperty] [ObservableProperty]
private bool activated; private bool activated;
@@ -29,12 +29,13 @@ public partial class FilterContainerNavigationViewModel : ObservableViewModel,
Filter = filter; Filter = filter;
} }
public Task Handle(DeactivatedEventArgs<Container> args) => public Task Handle(DeactivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = false); Task.FromResult(Activated = false);
public Task Handle(ActivatedEventArgs<Container> args) => public Task Handle(ActivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = true); Task.FromResult(Activated = true);
[RelayCommand] [RelayCommand]
public void Invoke() => Publisher.Publish(Request.As(new Filter<string>(Filter)), nameof(ContainerViewModel)); public void Invoke() => Publisher.Publish(Notify.As(new Filter(Filter)),
nameof(ContainerViewModel));
} }
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Bitvault; namespace Bitvault;
public partial class FooterViewModel : public partial class FooterViewModel :
ObservableCollectionViewModel<IMainNavigationViewModel> ObservableCollection<IMainNavigationViewModel>
{ {
public FooterViewModel(IServiceProvider provider, public FooterViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
-6
View File
@@ -1,6 +0,0 @@
using Toolkit.Foundation;
namespace Bitvault;
public interface IItemViewModel :
IValueInvoker<ItemConfiguration>;
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault; namespace Bitvault;
public partial class IconViewModel : ObservableViewModel public partial class IconViewModel : Observable
{ {
public IconViewModel(IServiceProvider provider, public IconViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault; namespace Bitvault;
public partial class ItemCommandHeaderViewModel : ObservableCollectionViewModel public partial class ItemCommandHeaderViewModel : ObservableCollection
{ {
public ItemCommandHeaderViewModel(IServiceProvider provider, public ItemCommandHeaderViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
+1 -1
View File
@@ -10,7 +10,7 @@ public partial class ItemHeaderViewModel(IServiceProvider provider,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer, IDisposer disposer,
bool immutable, bool immutable,
string? value = null) : ObservableViewModel<string, string>(provider, factory, mediator, publisher, subscriber, disposer, value), string? value = null) : Observable<string, string>(provider, factory, mediator, publisher, subscriber, disposer, value),
IHandler<ValidationEventArgs<Item>, bool>, IHandler<ValidationEventArgs<Item>, bool>,
IHandler<ConfirmEventArgs<Item>, ItemHeaderConfiguration> IHandler<ConfirmEventArgs<Item>, ItemHeaderConfiguration>
{ {
+1 -1
View File
@@ -15,7 +15,7 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
string name, string name,
string description, string description,
bool selected) : bool selected) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer), Observable(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<ArchiveEventArgs<Item>>, INotificationHandler<ArchiveEventArgs<Item>>,
ISelectable, ISelectable,
IRemovable IRemovable
+1 -1
View File
@@ -4,7 +4,7 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public partial class ItemViewModel : public partial class ItemViewModel :
ObservableCollectionViewModel<IDisposable> ObservableCollection<IDisposable>
{ {
[ObservableProperty] [ObservableProperty]
private int? id; private int? id;
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Bitvault;
[Enumerate(nameof(MainViewModel))] [Enumerate(nameof(MainViewModel))]
public partial class MainViewModel : public partial class MainViewModel :
ObservableCollectionViewModel<IMainNavigationViewModel> ObservableCollection<IMainNavigationViewModel>
{ {
[ObservableProperty] [ObservableProperty]
private FooterViewModel footer; private FooterViewModel footer;
+1 -1
View File
@@ -8,4 +8,4 @@ public class MainWindowViewModel(IServiceProvider provider,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer); Observable(provider, factory, mediator, publisher, subscriber, disposer);
+1 -1
View File
@@ -8,5 +8,5 @@ public partial class ManageNavigationViewModel(IServiceProvider provider,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer), Observable(provider, factory, mediator, publisher, subscriber, disposer),
IMainNavigationViewModel; IMainNavigationViewModel;
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Bitvault; namespace Bitvault;
public partial class ManageViewModel : public partial class ManageViewModel :
ObservableCollectionViewModel, ObservableCollection,
IMainNavigationViewModel IMainNavigationViewModel
{ {
public ManageViewModel(IServiceProvider provider, public ManageViewModel(IServiceProvider provider,
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault; namespace Bitvault;
public partial class NoteViewModel : ObservableViewModel public partial class NoteViewModel : Observable
{ {
public NoteViewModel(IServiceProvider provider, public NoteViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
+3 -3
View File
@@ -6,12 +6,12 @@ namespace Bitvault;
public class OpenContainerHandler(ContainerConfiguration configuration, public class OpenContainerHandler(ContainerConfiguration configuration,
ISecurityKeyFactory keyVaultFactory, ISecurityKeyFactory keyVaultFactory,
IContainerStorageFactory vaultStorage) : IContainerStorageFactory vaultStorage) :
IHandler<ActivateEventArgs<Container>, bool> IHandler<ActivateEventArgs<ContainerToken>, bool>
{ {
public async Task<bool> Handle(ActivateEventArgs<Container> args, public async Task<bool> Handle(ActivateEventArgs<ContainerToken> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (args.Value is Container container && configuration.Name is { Length: > 0 } name && container.Password is { Length: > 0 } password) if (args.Value is ContainerToken container && configuration.Name is { Length: > 0 } name && container.Password is { Length: > 0 } password)
{ {
if (configuration.Key?.Split(':') is { Length: >= 2 } keyPart) if (configuration.Key?.Split(':') is { Length: >= 2 } keyPart)
{ {
+3 -3
View File
@@ -10,7 +10,7 @@ public partial class OpenContainerViewModel(IServiceProvider provider,
IPublisher publisher, IPublisher publisher,
ISubscription subscriber, ISubscription subscriber,
IDisposer disposer) : IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) Observable(provider, factory, mediator, publisher, subscriber, disposer)
{ {
[ObservableProperty] [ObservableProperty]
private string? password; private string? password;
@@ -20,9 +20,9 @@ public partial class OpenContainerViewModel(IServiceProvider provider,
{ {
if (Password is { Length: > 0 }) if (Password is { Length: > 0 })
{ {
if (await Mediator.Handle<ActivateEventArgs<Container>, bool>(Activate.As(new Container(Password)))) if (await Mediator.Handle<ActivateEventArgs<ContainerToken>, bool>(Activate.As(new ContainerToken(Password))))
{ {
Publisher.Publish(Opened.As<Container>()); Publisher.Publish(Opened.As<ContainerToken>());
} }
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault; namespace Bitvault;
public partial class PasswordViewModel : ObservableViewModel public partial class PasswordViewModel : Observable
{ {
public PasswordViewModel(IServiceProvider provider, public PasswordViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
+8
View File
@@ -0,0 +1,8 @@
namespace Bitvault;
public record QueryContainerConfiguration
{
public string? Filter { get; set; }
public string? Query { get; set; }
}
+59
View File
@@ -0,0 +1,59 @@
using Bitvault.Data;
using LinqKit;
using Microsoft.EntityFrameworkCore;
using Toolkit.Foundation;
namespace Bitvault;
public class QueryContainerHandler(IDbContextFactory<ContainerDbContext> dbContextFactory) :
IHandler<RequestEventArgs<QueryContainerConfiguration>, IReadOnlyCollection<(int Id, string? Name)>>
{
public async Task<IReadOnlyCollection<(int Id, string? Name)>> Handle(RequestEventArgs<QueryContainerConfiguration> args,
CancellationToken cancellationToken)
{
List<(int Id, string? Name)> items = [];
if (args.Value is QueryContainerConfiguration queryConfiguration)
{
ExpressionStarter<ItemEntry> predicate =
PredicateBuilder.New<ItemEntry>(true);
if (queryConfiguration.Filter == "All")
{
predicate = predicate.And(x => x.State != 3);
}
if (queryConfiguration.Filter == "Starred")
{
predicate = predicate.And(x => x.State != 3 && x.State == 2);
}
if (queryConfiguration.Filter == "Archive")
{
predicate = predicate.And(x => x.State == 3);
}
if (queryConfiguration.Query is { Length: > 0} query)
{
predicate = predicate.And(x => EF.Functions.Like(x.Name, $"%{query}%"));
}
var results = await Task.Run(async () =>
{
using ContainerDbContext context = dbContextFactory.CreateDbContext();
return await context.Set<ItemEntry>().Where(predicate).Select(x => new
{
x.Id,
x.Name
}).OrderBy(x => x.Name).ToListAsync();
});
foreach (var result in results)
{
items.Add(new(result.Id, result.Name));
}
}
return items;
}
}
+3
View File
@@ -0,0 +1,3 @@
namespace Bitvault;
public record Search(string? Value);
@@ -0,0 +1,17 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Bitvault;
public partial class SearchContainerActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable<string>(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Notify.As(new Search(Value)),
nameof(ContainerViewModel));
}
-10
View File
@@ -1,10 +0,0 @@
using Toolkit.Foundation;
namespace Bitvault;
public partial class SearchHeaderViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : ObservableViewModel<string>(provider, factory, mediator, publisher, subscriber, disposer);
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault; namespace Bitvault;
public partial class TextViewModel : ObservableViewModel public partial class TextViewModel : Observable
{ {
public TextViewModel(IServiceProvider provider, public TextViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,