Ensure containers show up in the menu when they are created

This commit is contained in:
TheXamlGuy
2024-05-12 17:09:59 +01:00
parent 0ae3f60334
commit 975d5e6894
32 changed files with 211 additions and 174 deletions
+5 -1
View File
@@ -15,9 +15,13 @@
ToolTip.Tip="Add item"> ToolTip.Tip="Add item">
<Interaction.Behaviors> <Interaction.Behaviors>
<EventTriggerBehavior EventName="Click"> <EventTriggerBehavior EventName="Click">
<NavigateAction
Context="{Binding Named, StringFormat='{}{0}:ContentHeader'}"
Route="ItemCommandHeader"
Scope="self" />
<NavigateAction <NavigateAction
Context="{Binding Named, StringFormat='{}{0}:Content'}" Context="{Binding Named, StringFormat='{}{0}:Content'}"
Route="AddItem" Route="Item"
Scope="self" /> Scope="self" />
</EventTriggerBehavior> </EventTriggerBehavior>
</Interaction.Behaviors> </Interaction.Behaviors>
@@ -1,8 +0,0 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class AddItemCommandHeaderView : UserControl
{
public AddItemCommandHeaderView() => InitializeComponent();
}
-18
View File
@@ -1,18 +0,0 @@
<UserControl
x:Class="Bitvault.Avalonia.AddItemView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Bitvault"
x:DataType="vm:AddItemViewModel">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Loaded">
<NavigateAction
Context="{Binding Named, StringFormat='{}{0}:ContentHeader'}"
Route="AddVaultContentCommandHeader"
Scope="self" />
</EventTriggerBehavior>
</Interaction.Behaviors>
<ScrollViewer Padding="12,12,12,0">
<ItemsControl ItemTemplate="{ReflectionBinding Template}" ItemsSource="{Binding}" />
</ScrollViewer>
</UserControl>
-8
View File
@@ -1,8 +0,0 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class AddItemView : UserControl
{
public AddItemView() => InitializeComponent();
}
+8 -7
View File
@@ -35,6 +35,8 @@ public partial class App : Application
services.AddTemplate<MainWindowViewModel, MainWindow>("MainWindow"); services.AddTemplate<MainWindowViewModel, MainWindow>("MainWindow");
} }
services.AddHandler<ContainerActivatedHandler>();
services.AddTransient<IContainerComponent> (provider => Component.Create<ContainerComponent>(provider, args => services.AddTransient<IContainerComponent> (provider => Component.Create<ContainerComponent>(provider, args =>
{ {
args.AddServices(services => args.AddServices(services =>
@@ -47,14 +49,14 @@ public partial class App : Application
services.AddTransient<IKeyDeriver, KeyDeriver>(); services.AddTransient<IKeyDeriver, KeyDeriver>();
services.AddTransient<ISecurityKeyFactory, SecurityKeyFactory>(); services.AddTransient<ISecurityKeyFactory, SecurityKeyFactory>();
services.AddTransient<IContainer, ContainerFactory>(); services.AddTransient<IContainerFactory, ContainerFactory>();
services.TryAddSingleton<IContainer<SecurityKey>, Container<SecurityKey>>(); services.TryAddSingleton<IValueStore<SecurityKey>, ValueStore<SecurityKey>>();
services.TryAddSingleton<IContainer<ContainerConnection>, Container<ContainerConnection>>(); services.TryAddSingleton<IValueStore<ContainerConnection>, ValueStore<ContainerConnection>>();
services.AddDbContextFactory<ContainerDbContext>((provider, args) => services.AddDbContextFactory<ContainerDbContext>((provider, args) =>
{ {
if (provider.GetRequiredService<IContainer<ContainerConnection>>() if (provider.GetRequiredService<IValueStore<ContainerConnection>>()
is IContainer<ContainerConnection> connection) is IValueStore<ContainerConnection> connection)
{ {
args.UseSqlite($"{connection.Value}"); args.UseSqlite($"{connection.Value}");
} }
@@ -80,8 +82,7 @@ public partial class App : Application
services.AddTemplate<ItemNavigationViewModel, ItemNavigationView>(); services.AddTemplate<ItemNavigationViewModel, ItemNavigationView>();
services.AddTemplate<ItemViewModel, ItemView>("Item"); services.AddTemplate<ItemViewModel, ItemView>("Item");
services.AddTemplate<AddItemViewModel, AddItemView>("AddItem"); services.AddTemplate<ItemCommandHeaderViewModel, ItemCommandHeaderView>("ItemCommandHeader");
services.AddTemplate<AddItemCommandHeaderViewModel, AddItemCommandHeaderView>("AddVaultContentCommandHeader");
services.AddTemplate<ConfirmItemActionViewModel, ConfirmItemActionView>(); services.AddTemplate<ConfirmItemActionViewModel, ConfirmItemActionView>();
services.AddTemplate<DismissItemActionViewModel, DismissItemActionView>(); services.AddTemplate<DismissItemActionViewModel, DismissItemActionView>();
+2 -5
View File
@@ -43,11 +43,8 @@
<Compile Update="AddItemActionView.axaml.cs"> <Compile Update="AddItemActionView.axaml.cs">
<DependentUpon>AddItemActionView.axaml</DependentUpon> <DependentUpon>AddItemActionView.axaml</DependentUpon>
</Compile> </Compile>
<Compile Update="AddItemCommandHeaderView.axaml.cs"> <Compile Update="ItemCommandHeaderView.axaml.cs">
<DependentUpon>AddItemCommandHeaderView.axaml</DependentUpon> <DependentUpon>ItemCommandHeaderView.axaml</DependentUpon>
</Compile>
<Compile Update="AddItemView.axaml.cs">
<DependentUpon>AddItemView.axaml</DependentUpon>
</Compile> </Compile>
<Compile Update="ConfirmItemActionView.axaml.cs"> <Compile Update="ConfirmItemActionView.axaml.cs">
<DependentUpon>ConfirmItemActionView.axaml</DependentUpon> <DependentUpon>ConfirmItemActionView.axaml</DependentUpon>
@@ -1,9 +1,9 @@
<UserControl <UserControl
x:Class="Bitvault.Avalonia.AddItemCommandHeaderView" x:Class="Bitvault.Avalonia.ItemCommandHeaderView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
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:AddItemCommandHeaderViewModel"> x:DataType="vm:ItemCommandHeaderViewModel">
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<x:Double x:Key="ButtonWidth">40</x:Double> <x:Double x:Key="ButtonWidth">40</x:Double>
@@ -0,0 +1,8 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class ItemCommandHeaderView : UserControl
{
public ItemCommandHeaderView() => InitializeComponent();
}
+11 -4
View File
@@ -3,15 +3,22 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
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:Name="Name"
x:CompileBindings="False"
x:DataType="vm:ItemNavigationViewModel"> x:DataType="vm:ItemNavigationViewModel">
<Interaction.Behaviors> <Interaction.Behaviors>
<DataTriggerBehavior Binding="{Binding Selected}" Value="True"> <DataTriggerBehavior Binding="{Binding Selected}" Value="True">
<NavigateAction Context="{Binding Named, StringFormat='{}{0}:Content'}" Route="Item" /> <NavigateAction
Context="{Binding Named, StringFormat='{}{0}:ContentHeader'}"
Route="ItemCommandHeader"
Scope="self" />
<NavigateAction Context="{Binding Named, StringFormat='{}{0}:Content'}" Route="Item">
<NavigateAction.ParameterBindings>
<ParameterBinding Key="Id" Value="{Binding Id}" />
<ParameterBinding Key="Name" Value="{Binding Name}" />
</NavigateAction.ParameterBindings>
</NavigateAction>
</DataTriggerBehavior> </DataTriggerBehavior>
</Interaction.Behaviors> </Interaction.Behaviors>
<Grid ColumnDefinitions="Auto,*"> <Grid Background="Transparent" ColumnDefinitions="Auto,*">
<PersonPicture <PersonPicture
Grid.Column="0" Grid.Column="0"
Height="34" Height="34"
+6 -2
View File
@@ -1,6 +1,10 @@
<UserControl <UserControl
x:Class="Bitvault.Avalonia.ItemView" x:Class="Bitvault.Avalonia.ItemView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Vault content placeholder xmlns:vm="using:Bitvault"
x:DataType="vm:ItemViewModel">
<ScrollViewer Padding="12,12,12,0">
<ItemsControl ItemTemplate="{ReflectionBinding Template}" ItemsSource="{Binding}" />
</ScrollViewer>
</UserControl> </UserControl>
-40
View File
@@ -1,40 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Bitvault;
public partial class AddItemViewModel :
ObservableCollectionViewModel<IItemViewModel>,
INotificationHandler<Confirm<Item>>
{
[ObservableProperty]
private string named;
public AddItemViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IContentTemplate template,
NamedComponent named) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
Named = $"{named}";
Add<ItemHeaderViewModel>();
}
public IContentTemplate Template { get; set; }
public async Task Handle(Confirm<Item> args, CancellationToken cancellationToken = default)
{
ItemConfiguration configuration = new();
foreach (IItemViewModel item in this)
{
item.Invoke(configuration);
}
await Mediator.Handle<Create<ItemConfiguration>, bool>(Create.As(configuration), cancellationToken);
}
}
+1
View File
@@ -6,6 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="LinqKit.Microsoft.EntityFrameworkCore" Version="8.1.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0-preview.3.24172.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0-preview.3.24172.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+6 -6
View File
@@ -2,25 +2,25 @@
namespace Bitvault; namespace Bitvault;
public record SecureStorage<TValue>(TValue? Value = default); public record Container<TValue>(TValue? Value = default);
public record Vault public record Container
{ {
public Vault(string name, string password) public Container(string name, string password)
{ {
Name = name; Name = name;
Password = password; Password = password;
} }
public Vault(string password) public Container(string password)
{ {
Password = password; Password = password;
} }
public static SecureStorage<TValue> As<TValue>(TValue value) => new(value); public static Container<TValue> As<TValue>(TValue value) => new(value);
public static SecureStorage<TValue> As<TValue>() where TValue : new() => new(new TValue()); public static Container<TValue> As<TValue>() where TValue : new() => new(new TValue());
[MaybeNull] [MaybeNull]
+26
View File
@@ -0,0 +1,26 @@
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Bitvault;
public record ContainerActivatedHandler(IPublisher publisher) :
INotificationHandler<Activated<IComponentHost>>
{
public async Task Handle(Activated<IComponentHost> args, CancellationToken cancellationToken = default)
{
if (args.Value is IComponentHost container)
{
if (container.Services.GetRequiredService<ContainerConfiguration>() is ContainerConfiguration configuration)
{
if (container.Services.GetRequiredService<IServiceFactory>() is IServiceFactory factory)
{
if (factory.Create<ContainerNavigationViewModel>(configuration.Name) is ContainerNavigationViewModel viewModel)
{
await publisher.Publish(new Create<IMainNavigationViewModel>(viewModel),
nameof(MainViewModel), cancellationToken);
}
}
}
}
}
}
+1 -1
View File
@@ -7,4 +7,4 @@ public record ContainerConfiguration : ComponentConfiguration
public string? Name { get; set; } public string? Name { get; set; }
public string? Key { get; set; } public string? Key { get; set; }
} }
+2 -2
View File
@@ -6,10 +6,10 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class ContainerFactory(IContainer<ContainerConnection> connection, public class ContainerFactory(IValueStore<ContainerConnection> connection,
IHostEnvironment environment, IHostEnvironment environment,
IServiceProvider provider) : IServiceProvider provider) :
IContainer IContainerFactory
{ {
public async Task<bool> Create(string name, public async Task<bool> Create(string name,
SecurityKey key) SecurityKey key)
+2 -2
View File
@@ -3,7 +3,7 @@
namespace Bitvault; namespace Bitvault;
public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<string, IDisposable>, public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<string, IDisposable>,
INotificationHandler<SecureStorage<Filter<string>>> INotificationHandler<Container<Filter<string>>>
{ {
public ContainerHeaderViewModel(IServiceProvider provider, public ContainerHeaderViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
@@ -20,7 +20,7 @@ public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<st
public IContentTemplate Template { get; set; } public IContentTemplate Template { get; set; }
public Task Handle(SecureStorage<Filter<string>> args, public Task Handle(Container<Filter<string>> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (args.Value is Filter<string> filter) if (args.Value is Filter<string> filter)
+8 -8
View File
@@ -6,10 +6,10 @@ namespace Bitvault;
public partial class ContainerNavigationViewModel : public partial class ContainerNavigationViewModel :
ObservableCollectionViewModel<IContainerNavigationViewModel>, ObservableCollectionViewModel<IContainerNavigationViewModel>,
IMainNavigationViewModel, IMainNavigationViewModel,
INotificationHandler<SecureStorage<Opened>>, INotificationHandler<Container<Opened>>,
INotificationHandler<SecureStorage<Closed>>, INotificationHandler<Container<Closed>>,
INotificationHandler<SecureStorage<Activated>>, INotificationHandler<Container<Activated>>,
INotificationHandler<SecureStorage<Deactivated>> INotificationHandler<Container<Deactivated>>
{ {
[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(SecureStorage<Opened> args, public Task Handle(Container<Opened> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
Add<AllNavigationViewModel>("All"); Add<AllNavigationViewModel>("All");
@@ -53,7 +53,7 @@ public partial class ContainerNavigationViewModel :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(SecureStorage<Closed> args, public Task Handle(Container<Closed> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
Opened = true; Opened = true;
@@ -62,11 +62,11 @@ public partial class ContainerNavigationViewModel :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(SecureStorage<Deactivated> args, public Task Handle(Container<Deactivated> args,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
Task.FromResult(Activated = false); Task.FromResult(Activated = false);
public Task Handle(SecureStorage<Activated> args, public Task Handle(Container<Activated> args,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
Task.FromResult(Activated = true); Task.FromResult(Activated = true);
} }
+4 -4
View File
@@ -14,7 +14,7 @@ public partial class ContainerViewModel(IServiceProvider provider,
IContentTemplate template, IContentTemplate template,
NamedComponent named, NamedComponent named,
string? filter = null) : ObservableCollectionViewModel<ItemNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer), string? filter = null) : ObservableCollectionViewModel<ItemNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<SecureStorage<Filter<string>>> INotificationHandler<Container<Filter<string>>>
{ {
[ObservableProperty] [ObservableProperty]
private string? filter = filter; private string? filter = filter;
@@ -26,17 +26,17 @@ public partial class ContainerViewModel(IServiceProvider provider,
public override async Task Activated() public override async Task Activated()
{ {
await Publisher.Publish(Vault.As<Activated>()); await Publisher.Publish(Container.As<Activated>());
await base.Activated(); await base.Activated();
} }
public override async Task Deactivated() public override async Task Deactivated()
{ {
await Publisher.Publish(Vault.As<Deactivated>()); await Publisher.Publish(Container.As<Deactivated>());
await base.Deactivated(); await base.Deactivated();
} }
public async Task Handle(SecureStorage<Filter<string>> args, public async Task Handle(Container<Filter<string>> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (args.Value is Filter<string> filter) if (args.Value is Filter<string> filter)
+40 -15
View File
@@ -1,34 +1,59 @@
using Bitvault.Data; using Bitvault.Data;
using LinqKit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class ContainerViewModelHandler(IDbContextFactory<ContainerDbContext> dbContextFactory, public class ContainerViewModelHandler(IDbContextFactory<ContainerDbContext> dbContextFactory,
IServiceFactory factory, IServiceProvider serviceProvider,
IPublisher publisher) : IPublisher publisher) :
INotificationHandler<Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration>> INotificationHandler<Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration>>
{ {
public async Task Handle(Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration> args, public async Task Handle(Enumerate<ItemNavigationViewModel, ContainerViewModelConfiguration> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var items = await Task.Run(async () => if (args.Options is ContainerViewModelConfiguration configuration)
{ {
using ContainerDbContext context = dbContextFactory.CreateDbContext(); ExpressionStarter<Data.Item> predicate = PredicateBuilder.New<Data.Item>(true);
return await context.Set<Data.Item>().Select(x => new
{
x.Name,
x.State
}).Where(x => x.State != 3).ToListAsync();
}, cancellationToken); if (configuration.Filter == "All")
foreach (var item in items)
{
if (factory.Create<ItemNavigationViewModel>(item.Name, "Description " + 1) is ItemNavigationViewModel viewModel)
{ {
await publisher.Publish(new Create<ItemNavigationViewModel>(viewModel), predicate = predicate.And(x => x.State != 3);
nameof(ContainerViewModel), cancellationToken); }
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 items = await Task.Run(async () =>
{
using ContainerDbContext context = dbContextFactory.CreateDbContext();
return await context.Set<Data.Item>().Where(predicate).Select(x => new
{
x.Id,
x.Name
}).ToListAsync();
}, cancellationToken);
foreach (var item in items)
{
IServiceScope serviceScope = serviceProvider.CreateScope();
IServiceFactory serviceFactory = serviceScope.ServiceProvider.GetRequiredService<IServiceFactory>();
if (serviceFactory.Create<ItemNavigationViewModel>(item.Id, item.Name, "Description " + 1) is ItemNavigationViewModel viewModel)
{
await publisher.Publish(new Create<ItemNavigationViewModel>(viewModel),
nameof(ContainerViewModel), cancellationToken);
}
} }
} }
} }
+11 -9
View File
@@ -5,26 +5,27 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class CreateContainerHandler(IContainerComponentFactory componentFactory) : public class CreateContainerHandler(IContainerComponentFactory componentFactory,
IHandler<Create<Vault>, bool> IPublisher publisher) :
IHandler<Create<Container>, bool>
{ {
public async Task<bool> Handle(Create<Vault> args, public async Task<bool> Handle(Create<Container> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (args.Value is Vault vault && vault.Name is { Length: > 0 } name && if (args.Value is Container container && container.Name is { Length: > 0 } name &&
vault.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)
{ {
ISecurityKeyFactory keyVaultFactory = host.Services.GetRequiredService<ISecurityKeyFactory>(); ISecurityKeyFactory keyVaultFactory = host.Services.GetRequiredService<ISecurityKeyFactory>();
IContainer<SecurityKey> vaultKeyContainer = host.Services.GetRequiredService<IContainer<SecurityKey>>(); IValueStore<SecurityKey> secureKeyStore = host.Services.GetRequiredService<IValueStore<SecurityKey>>();
IContainer vaultStorage = host.Services.GetRequiredService<IContainer>(); IContainerFactory containerFactory = host.Services.GetRequiredService<IContainerFactory>();
if (keyVaultFactory.Create(Encoding.UTF8.GetBytes(password)) is SecurityKey key) if (keyVaultFactory.Create(Encoding.UTF8.GetBytes(password)) is SecurityKey key)
{ {
vaultKeyContainer.Set(key); secureKeyStore.Set(key);
if (await vaultStorage.Create(name, key)) if (await containerFactory.Create(name, key))
{ {
IWritableConfiguration<ContainerConfiguration> configuration = IWritableConfiguration<ContainerConfiguration> configuration =
host.Services.GetRequiredService<IWritableConfiguration<ContainerConfiguration>>(); host.Services.GetRequiredService<IWritableConfiguration<ContainerConfiguration>>();
@@ -32,6 +33,7 @@ public class CreateContainerHandler(IContainerComponentFactory componentFactory)
configuration.Write(args => args.Key = $"{Convert.ToBase64String(key.Salt)}:{Convert.ToBase64String(key.EncryptedKey)}:{Convert.ToBase64String(key.DecryptedKey)}"); configuration.Write(args => args.Key = $"{Convert.ToBase64String(key.Salt)}:{Convert.ToBase64String(key.EncryptedKey)}:{Convert.ToBase64String(key.DecryptedKey)}");
host.Start(); host.Start();
await publisher.Publish(Activated.As(host), cancellationToken);
return true; return true;
} }
} }
+1 -1
View File
@@ -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<Create<Vault>, bool>(Create.As(new Vault(Name, Password))); await Mediator.Handle<Create<Container>, bool>(Create.As(new Container(Name, Password)));
} }
@@ -6,8 +6,8 @@ namespace Bitvault;
public partial class FilterContainerNavigationViewModel : ObservableViewModel, public partial class FilterContainerNavigationViewModel : ObservableViewModel,
IContainerNavigationViewModel, IContainerNavigationViewModel,
INotificationHandler<SecureStorage<Activated>>, INotificationHandler<Container<Activated>>,
INotificationHandler<SecureStorage<Deactivated>> INotificationHandler<Container<Deactivated>>
{ {
[ObservableProperty] [ObservableProperty]
private bool activated; private bool activated;
@@ -29,14 +29,14 @@ public partial class FilterContainerNavigationViewModel : ObservableViewModel,
Filter = filter; Filter = filter;
} }
public Task Handle(SecureStorage<Deactivated> args, public Task Handle(Container<Deactivated> args,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
Task.FromResult(Activated = false); Task.FromResult(Activated = false);
public Task Handle(SecureStorage<Activated> args, public Task Handle(Container<Activated> args,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
Task.FromResult(Activated = true); Task.FromResult(Activated = true);
[RelayCommand] [RelayCommand]
public async Task Invoke() => await Publisher.Publish(Vault.As(new Filter<string>(Filter))); public async Task Invoke() => await Publisher.Publish(Container.As(new Filter<string>(Filter)));
} }
@@ -1,6 +1,6 @@
namespace Bitvault; namespace Bitvault;
public interface IContainer public interface IContainerFactory
{ {
Task<bool> Create(string name, SecurityKey key); Task<bool> Create(string name, SecurityKey key);
} }
@@ -2,9 +2,9 @@
namespace Bitvault; namespace Bitvault;
public partial class AddItemCommandHeaderViewModel : ObservableCollectionViewModel public partial class ItemCommandHeaderViewModel : ObservableCollectionViewModel
{ {
public AddItemCommandHeaderViewModel(IServiceProvider provider, public ItemCommandHeaderViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
-1
View File
@@ -17,7 +17,6 @@ public class ItemConfigurationHandler(IDbContextFactory<ContainerDbContext> dbCo
await Task.Run(async () => await Task.Run(async () =>
{ {
using ContainerDbContext context = dbContextFactory.CreateDbContext(); using ContainerDbContext context = dbContextFactory.CreateDbContext();
await context.AddAsync(new Data.Item { Name = configuration.Name }, cancellationToken); await context.AddAsync(new Data.Item { Name = configuration.Name }, cancellationToken);
await context.SaveChangesAsync(cancellationToken); await context.SaveChangesAsync(cancellationToken);
}, cancellationToken); }, cancellationToken);
+3 -1
View File
@@ -7,9 +7,11 @@ public partial class ItemHeaderViewModel(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
ISubscriber subscriber, ISubscriber subscriber,
IDisposer disposer) : ObservableViewModel<string, string>(provider, factory, mediator, publisher, subscriber, disposer), IDisposer disposer,
string? value = null) : ObservableViewModel<string, string>(provider, factory, mediator, publisher, subscriber, disposer, value),
IItemViewModel IItemViewModel
{ {
public void Invoke(ItemConfiguration args) => public void Invoke(ItemConfiguration args) =>
args.Name = Value; args.Name = Value;
} }
+4
View File
@@ -11,6 +11,7 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
IDisposer disposer, IDisposer disposer,
IContentTemplate template, IContentTemplate template,
NamedComponent named, NamedComponent named,
int id,
string name, string name,
string description) : string description) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer) ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
@@ -18,6 +19,9 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
[ObservableProperty] [ObservableProperty]
private string? description = description; private string? description = description;
[ObservableProperty]
private int id = id;
[ObservableProperty] [ObservableProperty]
private string? name = name; private string? name = name;
+42 -11
View File
@@ -1,15 +1,46 @@
using Toolkit.Foundation; using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class ItemViewModel(IServiceProvider provider, public partial class ItemViewModel :
IServiceFactory factory, ObservableCollectionViewModel<IItemViewModel>,
IMediator mediator, INotificationHandler<Confirm<Item>>
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IContentTemplate template) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
{ {
public IContentTemplate Template { get; set; } = template; [ObservableProperty]
} private int? id;
[ObservableProperty]
private string named;
public ItemViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer,
IContentTemplate template,
NamedComponent named,
int? id = null,
string? name = null) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
Named = $"{named}";
Id = id;
Add<ItemHeaderViewModel>(name ?? "");
}
public IContentTemplate Template { get; set; }
public async Task Handle(Confirm<Item> args, CancellationToken cancellationToken = default)
{
ItemConfiguration configuration = new();
foreach (IItemViewModel item in this)
{
item.Invoke(configuration);
}
await Mediator.Handle<Create<ItemConfiguration>, bool>(Create.As(configuration), cancellationToken);
}
}
+4 -4
View File
@@ -4,17 +4,17 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class MainViewModelHandler(IPublisher publisher, public class MainViewModelHandler(IPublisher publisher,
IContainerHostCollection vaults) : IContainerHostCollection containers) :
INotificationHandler<Enumerate<IMainNavigationViewModel>> INotificationHandler<Enumerate<IMainNavigationViewModel>>
{ {
public async Task Handle(Enumerate<IMainNavigationViewModel> args, public async Task Handle(Enumerate<IMainNavigationViewModel> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
foreach (IComponentHost vault in vaults) foreach (IComponentHost container in containers)
{ {
if (vault.Services.GetRequiredService<ContainerConfiguration>() is ContainerConfiguration configuration) if (container.Services.GetRequiredService<ContainerConfiguration>() is ContainerConfiguration configuration)
{ {
if (vault.Services.GetRequiredService<IServiceFactory>() is IServiceFactory factory) if (container.Services.GetRequiredService<IServiceFactory>() is IServiceFactory factory)
{ {
if (factory.Create<ContainerNavigationViewModel>(configuration.Name) is ContainerNavigationViewModel viewModel) if (factory.Create<ContainerNavigationViewModel>(configuration.Name) is ContainerNavigationViewModel viewModel)
{ {
+4 -4
View File
@@ -5,13 +5,13 @@ namespace Bitvault;
public class OpenContainerHandler(ContainerConfiguration configuration, public class OpenContainerHandler(ContainerConfiguration configuration,
ISecurityKeyFactory keyVaultFactory, ISecurityKeyFactory keyVaultFactory,
IContainer vaultStorage) : IContainerFactory vaultStorage) :
IHandler<Open<Vault>, bool> IHandler<Open<Container>, bool>
{ {
public async Task<bool> Handle(Open<Vault> args, public async Task<bool> Handle(Open<Container> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (args.Value is Vault vault && configuration.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password) if (args.Value is Container 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)
{ {
+2 -2
View File
@@ -20,9 +20,9 @@ public partial class OpenContainerViewModel(IServiceProvider provider,
{ {
if (Password is { Length: > 0 }) if (Password is { Length: > 0 })
{ {
if (await Mediator.Handle<Open<Vault>, bool>(Open.As(new Vault(Password)))) if (await Mediator.Handle<Open<Container>, bool>(Open.As(new Container(Password))))
{ {
await Publisher.Publish(Vault.As<Opened>()); await Publisher.Publish(Container.As<Opened>());
} }
} }
} }