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}");
}
});
});
services.AddHandler<QueryContainerHandler>(ServiceLifetime.Singleton);
services.AddHandler<OpenContainerHandler>();
@@ -78,10 +80,12 @@ public partial class App : Application
services.AddTemplate<OpenContainerViewModel, OpenView>("OpenContainer");
services.AddTemplate<ContainerViewModel, ContainerView>("Container");
services.AddHandler<ContainerViewModelHandler>();
services.AddScoped<ContainerViewModelConfiguration>();
services.AddTemplate<SearchHeaderViewModel, SearchHeaderView>("SearchHeader");
services.AddTemplate<ContainerViewModel, ContainerView>("Container");
services.AddHandler<EnumerateContainerViewModelHandler>();
services.AddTemplate<SearchContainerActionViewModel, SearchContainerActionView>();
services.AddTemplate<ContainerHeaderViewModel, ContainerHeaderView>("ContainerHeader");
services.AddTemplate<CreateItemActionViewModel, CreateItemActionView>();
-1
View File
@@ -1,6 +1,5 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using System.Threading;
using System.Threading.Tasks;
using Toolkit.Foundation;
@@ -79,9 +79,6 @@
<Compile Update="ContainerNavigationView.axaml.cs">
<DependentUpon>ContainerNavigationView.axaml</DependentUpon>
</Compile>
<Compile Update="SearchHeaderView.axaml.cs">
<DependentUpon>SearchHeaderView.axaml</DependentUpon>
</Compile>
<Compile Update="ContainerView.axaml.cs">
<DependentUpon>ContainerView.axaml</DependentUpon>
</Compile>
+1 -28
View File
@@ -4,32 +4,5 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Bitvault"
x:DataType="vm:ContainerHeaderViewModel">
<UserControl.Resources>
<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>
<ItemsControl ItemTemplate="{ReflectionBinding Template}" ItemsSource="{Binding}" />
</UserControl>
+13 -10
View File
@@ -5,17 +5,20 @@
xmlns:vm="using:Bitvault"
x:DataType="vm:ContainerViewModel">
<Grid ColumnDefinitions="320,Auto,*" RowDefinitions="Auto,*">
<ContentControl
<Grid
x:Name="Title"
Grid.Row="0"
Grid.Column="0"
Height="40"
Margin="4,4,0,0">
<Interaction.Behaviors>
<AttachedBehaviour>
<NavigateAction Region="{Binding $self}" Route="ContainerHeader" />
</AttachedBehaviour>
</Interaction.Behaviors>
</ContentControl>
Grid.ColumnSpan="3"
Height="44"
ColumnDefinitions="*, Auto">
<ContentControl Grid.Column="0" VerticalAlignment="Center">
<Interaction.Behaviors>
<AttachedBehaviour>
<NavigateAction Region="{Binding $self}" Route="ContainerHeader" />
</AttachedBehaviour>
</Interaction.Behaviors>
</ContentControl>
</Grid>
<ListBox
Grid.Row="1"
Grid.Column="0"
+12
View File
@@ -1,8 +1,20 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Windowing;
namespace Bitvault.Avalonia;
public partial class ContainerView : UserControl
{
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>
<Frame>
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Loaded">
<AttachedBehaviour>
<NavigateRegionAction Name="Main">
<NavigateAction Region="Main" Route="Container" />
</NavigateRegionAction>
</EventTriggerBehavior>
</AttachedBehaviour>
</Interaction.Behaviors>
</Frame>
</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,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Archive.As<Item>());
+1 -1
View File
@@ -8,7 +8,7 @@ public partial class ConfirmItemActionViewModel(IServiceProvider provider,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
+51 -11
View File
@@ -1,18 +1,16 @@
using Bitvault.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Toolkit.Foundation;
namespace Bitvault;
public class ConfirmItemHandler(IMediator mediator,
IDbContextFactory<ContainerDbContext> dbContextFactory,
IPublisher publisher) :
INotificationHandler<ConfirmEventArgs<Item>>
public class EditItemHander(IDbContextFactory<ContainerDbContext> dbContextFactory) :
IHandler<EditEventArgs<(int, ItemConfiguration)>, bool>
{
public async Task<bool> Handle(ConfirmEventArgs<Item> args,
public async Task<bool> Handle(EditEventArgs<(int, ItemConfiguration)> args,
CancellationToken cancellationToken)
{
await mediator.Handle<ConfirmEventArgs<Item>, bool>(args);
//if (args.Value is ItemConfiguration configuration)
//{
// try
@@ -29,9 +27,6 @@ public class ConfirmItemHandler(IMediator mediator,
// if (result is not null)
// {
// Item item = new() { Id = result.Entity.Id, Name = configuration.Name };
// publisher.Publish(Activated.As(item), cancellationToken);
// return true;
// }
// }
@@ -43,9 +38,54 @@ public class ConfirmItemHandler(IMediator mediator,
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;
namespace Bitvault;
+2 -1
View File
@@ -2,7 +2,8 @@
namespace Bitvault;
public record ContainerConfiguration : ComponentConfiguration
public record ContainerConfiguration :
ComponentConfiguration
{
public string? Name { get; set; }
+13 -11
View File
@@ -2,8 +2,8 @@
namespace Bitvault;
public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<string, IDisposable>,
INotificationHandler<RequestEventArgs<Filter<string>>>
public partial class ContainerHeaderViewModel :
ObservableCollection
{
public ContainerHeaderViewModel(IServiceProvider provider,
IServiceFactory factory,
@@ -15,18 +15,20 @@ public partial class ContainerHeaderViewModel : ObservableCollectionViewModel<st
{
Template = template;
Add<CreateItemActionViewModel>(scope: true);
Add<SearchContainerActionViewModel>();
//Add<CreateItemActionViewModel>(scope: true);
}
public IContentTemplate Template { get; set; }
public Task Handle(RequestEventArgs<Filter<string>> args)
{
if (args.Value is Filter<string> filter)
{
Value = filter.Value;
}
//public Task Handle(RequestEventArgs<Filter<string>> args)
//{
// if (args.Value is Filter<string> filter)
// {
// 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)
{
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);
await host.StartAsync();
}
}
}
}
+11 -11
View File
@@ -4,12 +4,12 @@ using Toolkit.Foundation;
namespace Bitvault;
public partial class ContainerNavigationViewModel :
ObservableCollectionViewModel<IContainerNavigationViewModel>,
ObservableCollection<IContainerNavigationViewModel>,
IMainNavigationViewModel,
INotificationHandler<OpenedEventArgs<Container>>,
INotificationHandler<ClosedEventArgs<Container>>,
INotificationHandler<ActivatedEventArgs<Container>>,
INotificationHandler<DeactivatedEventArgs<Container>>
INotificationHandler<OpenedEventArgs<ContainerToken>>,
INotificationHandler<ClosedEventArgs<ContainerToken>>,
INotificationHandler<ActivatedEventArgs<ContainerToken>>,
INotificationHandler<DeactivatedEventArgs<ContainerToken>>
{
[ObservableProperty]
private bool activated;
@@ -41,7 +41,7 @@ public partial class ContainerNavigationViewModel :
public IContentTemplate Template { get; set; }
public Task Handle(OpenedEventArgs<Container> args)
public Task Handle(OpenedEventArgs<ContainerToken> args)
{
Add<AllNavigationViewModel>("All");
Add<StarredNavigationViewModel>("Starred");
@@ -52,7 +52,7 @@ public partial class ContainerNavigationViewModel :
return Task.CompletedTask;
}
public Task Handle(ClosedEventArgs<Container> args)
public Task Handle(ClosedEventArgs<ContainerToken> args)
{
Opened = true;
Clear();
@@ -60,9 +60,9 @@ public partial class ContainerNavigationViewModel :
return Task.CompletedTask;
}
public Task Handle(DeactivatedEventArgs<Container> args) =>
Task.FromResult(Activated = false);
public Task Handle(DeactivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = false);
public Task Handle(ActivatedEventArgs<Container> args) =>
Task.FromResult(Activated = true);
public Task Handle(ActivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = true);
}
@@ -2,20 +2,20 @@
namespace Bitvault;
public record Container
public record ContainerToken
{
public Container(string name, string password)
public ContainerToken(string name, string password)
{
Name = name;
Password = password;
}
public Container(string password)
public ContainerToken(string password)
{
Password = password;
}
public Container()
public ContainerToken()
{
}
+28 -11
View File
@@ -1,8 +1,17 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.EntityFrameworkCore;
using System.Collections.ObjectModel;
using Toolkit.Foundation;
namespace Bitvault;
public class ItemCommandCollection : ObservableCollection
{
public void Add<TItem>(IDisposable diposer)
{
}
}
[Enumerate(nameof(ContainerViewModel))]
public partial class ContainerViewModel(IServiceProvider provider,
@@ -13,12 +22,10 @@ public partial class ContainerViewModel(IServiceProvider provider,
IDisposer disposer,
IContentTemplate template,
NamedComponent named,
string? filter = null) : ObservableCollectionViewModel<ItemNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<RequestEventArgs<Filter<string>>>
ContainerViewModelConfiguration configuration) : Toolkit.Foundation.ObservableCollection<ItemNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<NotifyEventArgs<Filter>>,
INotificationHandler<NotifyEventArgs<Search>>
{
[ObservableProperty]
private string? filter = filter;
[ObservableProperty]
private string named = $"{named}";
@@ -26,21 +33,31 @@ public partial class ContainerViewModel(IServiceProvider provider,
public override async Task OnActivated()
{
Publisher.Publish(Activated.As<Container>());
Publisher.Publish(Activated.As<ContainerToken>());
await base.OnActivated();
}
public override async Task OnDeactivated()
{
Publisher.Publish(Deactivated.As<Container>());
Publisher.Publish(Deactivated.As<ContainerToken>());
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();
}
@@ -48,5 +65,5 @@ public partial class ContainerViewModel(IServiceProvider provider,
}
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 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,
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)
{
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)
{
if (componentFactory.Create(name) is IComponentHost host)
@@ -8,5 +8,5 @@ public partial class CreateContainerNavigationViewModel(IServiceProvider provide
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer),
Observable(provider, factory, mediator, publisher, subscriber, disposer),
IMainNavigationViewModel;
+2 -2
View File
@@ -10,7 +10,7 @@ public partial class CreateContainerViewModel(IServiceProvider provider,
IMediator mediator,
ISubscription subscriber,
IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer),
Observable(provider, factory, mediator, publisher, subscriber, disposer),
IPrimaryConfirmation
{
[MaybeNull]
@@ -22,5 +22,5 @@ public partial class CreateContainerViewModel(IServiceProvider provider,
private string password;
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,
ISubscription subscriber,
IDisposer disposer,
NamedComponent named) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
NamedComponent named) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
+1 -1
View File
@@ -8,7 +8,7 @@ public partial class DeleteItemActionViewModel(IServiceProvider provider,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Delete.As<Item>());
+1 -1
View File
@@ -7,4 +7,4 @@ public partial class DismissItemActionViewModel(IServiceProvider provider,
IMediator mediator,
IPublisher publisher,
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,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
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;
public record Filter<TValue>(TValue? Value);
public record Filter(string? Value);
@@ -4,10 +4,10 @@ using Toolkit.Foundation;
namespace Bitvault;
public partial class FilterContainerNavigationViewModel : ObservableViewModel,
public partial class FilterContainerNavigationViewModel : Observable,
IContainerNavigationViewModel,
INotificationHandler<ActivatedEventArgs<Container>>,
INotificationHandler<DeactivatedEventArgs<Container>>
INotificationHandler<ActivatedEventArgs<ContainerToken>>,
INotificationHandler<DeactivatedEventArgs<ContainerToken>>
{
[ObservableProperty]
private bool activated;
@@ -29,12 +29,13 @@ public partial class FilterContainerNavigationViewModel : ObservableViewModel,
Filter = filter;
}
public Task Handle(DeactivatedEventArgs<Container> args) =>
Task.FromResult(Activated = false);
public Task Handle(DeactivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = false);
public Task Handle(ActivatedEventArgs<Container> args) =>
Task.FromResult(Activated = true);
public Task Handle(ActivatedEventArgs<ContainerToken> args) =>
Task.FromResult(Activated = true);
[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;
public partial class FooterViewModel :
ObservableCollectionViewModel<IMainNavigationViewModel>
ObservableCollection<IMainNavigationViewModel>
{
public FooterViewModel(IServiceProvider provider,
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;
public partial class IconViewModel : ObservableViewModel
public partial class IconViewModel : Observable
{
public IconViewModel(IServiceProvider provider,
IServiceFactory factory,
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault;
public partial class ItemCommandHeaderViewModel : ObservableCollectionViewModel
public partial class ItemCommandHeaderViewModel : ObservableCollection
{
public ItemCommandHeaderViewModel(IServiceProvider provider,
IServiceFactory factory,
+1 -1
View File
@@ -10,7 +10,7 @@ public partial class ItemHeaderViewModel(IServiceProvider provider,
ISubscription subscriber,
IDisposer disposer,
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<ConfirmEventArgs<Item>, ItemHeaderConfiguration>
{
+1 -1
View File
@@ -15,7 +15,7 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
string name,
string description,
bool selected) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer),
Observable(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<ArchiveEventArgs<Item>>,
ISelectable,
IRemovable
+1 -1
View File
@@ -4,7 +4,7 @@ using Toolkit.Foundation;
namespace Bitvault;
public partial class ItemViewModel :
ObservableCollectionViewModel<IDisposable>
ObservableCollection<IDisposable>
{
[ObservableProperty]
private int? id;
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Bitvault;
[Enumerate(nameof(MainViewModel))]
public partial class MainViewModel :
ObservableCollectionViewModel<IMainNavigationViewModel>
ObservableCollection<IMainNavigationViewModel>
{
[ObservableProperty]
private FooterViewModel footer;
+1 -1
View File
@@ -8,4 +8,4 @@ public class MainWindowViewModel(IServiceProvider provider,
IPublisher publisher,
ISubscription subscriber,
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,
ISubscription subscriber,
IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer),
Observable(provider, factory, mediator, publisher, subscriber, disposer),
IMainNavigationViewModel;
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Bitvault;
public partial class ManageViewModel :
ObservableCollectionViewModel,
ObservableCollection,
IMainNavigationViewModel
{
public ManageViewModel(IServiceProvider provider,
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Bitvault;
public partial class NoteViewModel : ObservableViewModel
public partial class NoteViewModel : Observable
{
public NoteViewModel(IServiceProvider provider,
IServiceFactory factory,
+3 -3
View File
@@ -6,12 +6,12 @@ namespace Bitvault;
public class OpenContainerHandler(ContainerConfiguration configuration,
ISecurityKeyFactory keyVaultFactory,
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)
{
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)
{
+3 -3
View File
@@ -10,7 +10,7 @@ public partial class OpenContainerViewModel(IServiceProvider provider,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) :
ObservableViewModel(provider, factory, mediator, publisher, subscriber, disposer)
Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
private string? password;
@@ -20,9 +20,9 @@ public partial class OpenContainerViewModel(IServiceProvider provider,
{
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;
public partial class PasswordViewModel : ObservableViewModel
public partial class PasswordViewModel : Observable
{
public PasswordViewModel(IServiceProvider provider,
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;
public partial class TextViewModel : ObservableViewModel
public partial class TextViewModel : Observable
{
public TextViewModel(IServiceProvider provider,
IServiceFactory factory,