It it now possible to create/edit item contents
This commit is contained in:
@@ -87,11 +87,12 @@ public partial class App : Application
|
||||
if (provider.GetRequiredService<IDecoratorService<LockerConnection>>()
|
||||
is IDecoratorService<LockerConnection> connection)
|
||||
{
|
||||
args.UseSqlite($"{connection.Value}");
|
||||
args.UseSqlite($"{connection.Service}");
|
||||
}
|
||||
});
|
||||
|
||||
services.AddHandler<QueryLockerHandler>();
|
||||
services.AddHandler<RequestItemHandler>();
|
||||
services.AddHandler<CreateItemHandler>();
|
||||
services.AddHandler<UpdateItemHander>();
|
||||
services.AddHandler<UpdateItemStateHandler>();
|
||||
|
||||
@@ -17,36 +17,13 @@
|
||||
<Interaction.Behaviors>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Read}">
|
||||
<AddClassAction ClassName="Read" RemoveIfExists="True" />
|
||||
<RemoveClassAction ClassName="Write" />
|
||||
</DataTriggerBehavior>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Write}">
|
||||
<AddClassAction ClassName="Write" RemoveIfExists="True" />
|
||||
<RemoveClassAction ClassName="Read" />
|
||||
</DataTriggerBehavior>
|
||||
</Interaction.Behaviors>
|
||||
<TextBox.Styles>
|
||||
<Style Selector="TextBox.Read">
|
||||
<Setter Property="IsReadOnly" Value="True" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Style Selector="^:pointerover">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextControlForegroundPointerOver}" />
|
||||
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
</Style>
|
||||
<Style Selector="^:focus">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextControlForegroundFocused}" />
|
||||
<Style Selector="^ /template/ TextBlock#PART_Watermark">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForegroundFocused}" />
|
||||
</Style>
|
||||
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
</Style>
|
||||
</Style>
|
||||
</Style>
|
||||
<Style Selector="TextBox.Write" />
|
||||
</TextBox.Styles>
|
||||
</TextBox>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
x:DataType="vm:ItemMaskedTextEntryViewModel"
|
||||
Header="{Binding Key}">
|
||||
<SettingsExpander.Footer>
|
||||
<TextBox MinWidth="264" Text="{Binding Value}" />
|
||||
<TextBox HorizontalAlignment="Right" Text="{Binding Value}">
|
||||
<Interaction.Behaviors>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Read}">
|
||||
<AddClassAction ClassName="Read" RemoveIfExists="True" />
|
||||
<RemoveClassAction ClassName="Write" />
|
||||
</DataTriggerBehavior>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Write}">
|
||||
<AddClassAction ClassName="Write" RemoveIfExists="True" />
|
||||
<RemoveClassAction ClassName="Read" />
|
||||
</DataTriggerBehavior>
|
||||
</Interaction.Behaviors>
|
||||
</TextBox>
|
||||
</SettingsExpander.Footer>
|
||||
</SettingsExpander>
|
||||
@@ -10,6 +10,15 @@
|
||||
MinWidth="264"
|
||||
Classes="revealPasswordButton"
|
||||
PasswordChar="●"
|
||||
Text="{Binding Value}" />
|
||||
Text="{Binding Value}">
|
||||
<Interaction.Behaviors>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Read}">
|
||||
<AddClassAction ClassName="Read" RemoveIfExists="True" />
|
||||
</DataTriggerBehavior>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Write}">
|
||||
<RemoveClassAction ClassName="Read" />
|
||||
</DataTriggerBehavior>
|
||||
</Interaction.Behaviors>
|
||||
</TextBox>
|
||||
</SettingsExpander.Footer>
|
||||
</SettingsExpander>
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
x:DataType="vm:ItemTextEntryViewModel"
|
||||
Header="{Binding Key}">
|
||||
<SettingsExpander.Footer>
|
||||
<TextBox MinWidth="264" Text="{Binding Value}" />
|
||||
<TextBox HorizontalAlignment="Right" Text="{Binding Value}">
|
||||
<Interaction.Behaviors>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Read}">
|
||||
<AddClassAction ClassName="Read" RemoveIfExists="True" />
|
||||
<RemoveClassAction ClassName="Write" />
|
||||
</DataTriggerBehavior>
|
||||
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Write}">
|
||||
<AddClassAction ClassName="Write" RemoveIfExists="True" />
|
||||
<RemoveClassAction ClassName="Read" />
|
||||
</DataTriggerBehavior>
|
||||
</Interaction.Behaviors>
|
||||
</TextBox>
|
||||
</SettingsExpander.Footer>
|
||||
</SettingsExpander>
|
||||
|
||||
@@ -4,6 +4,44 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Bitvault"
|
||||
x:DataType="vm:ItemViewModel">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="TextBox.Write">
|
||||
<Setter Property="MinWidth" Value="264" />
|
||||
</Style>
|
||||
<Style Selector="TextBox.Read">
|
||||
<Setter Property="MinWidth" Value="0" />
|
||||
<Setter Property="IsReadOnly" Value="True" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Opacity="0.7" Color="{DynamicResource TextFillColorPrimary}" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style Selector="^:pointerover">
|
||||
<Setter Property="Foreground">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Opacity="0.7" Color="{DynamicResource TextFillColorPrimary}" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
</Style>
|
||||
<Style Selector="^:focus">
|
||||
<Setter Property="Foreground">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Opacity="0.7" Color="{DynamicResource TextFillColorPrimary}" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
</Style>
|
||||
</Style>
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<Interaction.Behaviors>
|
||||
<AttachedBehaviour>
|
||||
<NavigateAction
|
||||
@@ -22,7 +60,7 @@
|
||||
</ConditionalExpression>
|
||||
</ConditionAction.Condition>
|
||||
<NavigateBackAction Region="Left" />
|
||||
</ConditionAction>
|
||||
</ConditionAction>
|
||||
</DataTriggerBehavior>
|
||||
</Interaction.Behaviors>
|
||||
<ScrollViewer Padding="12,12,12,0">
|
||||
|
||||
@@ -13,5 +13,5 @@ public record BlobEntry
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public record ItemEntry
|
||||
|
||||
public required string Category { get; set; }
|
||||
|
||||
public ICollection<TagEntry>? Tags { get; }
|
||||
public ICollection<TagEntry> Tags { get; set; } = new List<TagEntry>();
|
||||
|
||||
public ICollection<BlobEntry>? Blobs { get; }
|
||||
public ICollection<BlobEntry> Blobs { get; set; } = new List<BlobEntry>();
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class ArchiveItemHandler(IDecoratorService<Item<(Guid, string)>> decorato
|
||||
{
|
||||
try
|
||||
{
|
||||
if (decoratorService.Value is Item<(Guid, string)> item)
|
||||
if (decoratorService.Service is Item<(Guid, string)> item)
|
||||
{
|
||||
if (cache.Contains(item))
|
||||
{
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public class ConfirmCreateItemHandler(IMediator mediator,
|
||||
IDecoratorService<ItemConfiguration> decoratorItemConfiguration,
|
||||
IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
|
||||
IPublisher publisher) :
|
||||
INotificationHandler<ConfirmEventArgs<Item>>
|
||||
{
|
||||
public async Task Handle(ConfirmEventArgs<Item> args)
|
||||
{
|
||||
if (decoratorItemConfiguration.Value is ItemConfiguration configuration)
|
||||
if (itemConfigurationDecorator.Service is ItemConfiguration configuration)
|
||||
{
|
||||
string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>, string>(Confirm.As<ItemHeader>());
|
||||
if (name is not null)
|
||||
|
||||
@@ -2,32 +2,33 @@
|
||||
|
||||
namespace Bitvault;
|
||||
|
||||
public class ConfirmUpdateItemHandler(IDecoratorService<Item<(Guid, string)>> decoratorItem,
|
||||
IDecoratorService<ItemConfiguration> decoratorItemConfiguration,
|
||||
public class ConfirmUpdateItemHandler(IDecoratorService<Item<(Guid, string)>> itemDecorator,
|
||||
IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
|
||||
IMediator mediator,
|
||||
IPublisher publisher) :
|
||||
INotificationHandler<ConfirmEventArgs<Item>>
|
||||
{
|
||||
public async Task Handle(ConfirmEventArgs<Item> args)
|
||||
{
|
||||
string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>,
|
||||
string>(Confirm.As<ItemHeader>());
|
||||
|
||||
if (name is not null)
|
||||
if (itemDecorator?.Service is Item<(Guid, string)> item &&
|
||||
itemConfigurationDecorator.Service is ItemConfiguration configuration)
|
||||
{
|
||||
var dd = decoratorItemConfiguration;
|
||||
publisher.Publish(Notify.As(new ItemHeader<string>(name)));
|
||||
if (decoratorItem?.Value is Item<(Guid, string)> item)
|
||||
string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>,
|
||||
string>(Confirm.As<ItemHeader>());
|
||||
|
||||
if (name is not null)
|
||||
{
|
||||
publisher.Publish(Notify.As(new ItemHeader<string>(name)));
|
||||
|
||||
(Guid id, string _) = item.Value;
|
||||
|
||||
Item<(Guid, string)> newItem = new((id, name));
|
||||
publisher.Publish(Modified.As(item, newItem));
|
||||
|
||||
decoratorItem.Set(newItem);
|
||||
itemDecorator.Set(newItem);
|
||||
|
||||
await mediator.Handle<UpdateEventArgs<(Guid, string, ItemConfiguration)>, bool>(new UpdateEventArgs<(Guid, string,
|
||||
ItemConfiguration)>((id, name, new ItemConfiguration())));
|
||||
await mediator.Handle<UpdateEventArgs<Item<(Guid, string, ItemConfiguration)>>, bool>(new UpdateEventArgs<Item<(Guid, string,
|
||||
ItemConfiguration)>>(new Item<(Guid, string, ItemConfiguration)>((id, name, configuration))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Bitvault.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Bitvault;
|
||||
@@ -15,15 +17,24 @@ public class CreateItemHandler(IDbContextFactory<LockerContext> dbContextFactory
|
||||
{
|
||||
try
|
||||
{
|
||||
using LockerContext context = dbContextFactory.CreateDbContext();
|
||||
EntityEntry<ItemEntry>? result = null;
|
||||
|
||||
await Task.Run(async () =>
|
||||
string content = JsonSerializer.Serialize(configuration);
|
||||
ItemEntry itemEntry = new()
|
||||
{
|
||||
result = await context.AddAsync(new ItemEntry { Id = id, Name = name, Category = category }, cancellationToken);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
Id = id,
|
||||
Name = name,
|
||||
Category = category
|
||||
};
|
||||
|
||||
}, cancellationToken);
|
||||
itemEntry.Blobs.Add(new()
|
||||
{
|
||||
Data = Encoding.UTF8.GetBytes(content),
|
||||
DateTime = DateTime.Now,
|
||||
Type = 0,
|
||||
});
|
||||
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
EntityEntry<ItemEntry>? result = await context.AddAsync(itemEntry, cancellationToken);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
|
||||
@@ -7,36 +7,41 @@ namespace Bitvault;
|
||||
|
||||
public class CreateLockerHandler(ILockerFactory componentFactory,
|
||||
IPublisher publisher) :
|
||||
IHandler<CreateEventArgs<Locker>, bool>
|
||||
IHandler<CreateEventArgs<Locker<(string, string)>>, bool>
|
||||
{
|
||||
public async Task<bool> Handle(CreateEventArgs<Locker> args,
|
||||
public async Task<bool> Handle(CreateEventArgs<Locker<(string, string)>> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Value is Locker locker && locker.Name is { Length: > 0 } name &&
|
||||
locker.Password is { Length: > 0 } password)
|
||||
if (args.Value is Locker <(string, string)> locker)
|
||||
{
|
||||
if (componentFactory.Create(name) is IComponentHost host)
|
||||
if (locker.Value is (string name, string password) &&
|
||||
name is { Length: > 0 } &&
|
||||
password is { Length: > 0 })
|
||||
{
|
||||
ISecurityKeyFactory keyVaultFactory = host.Services.GetRequiredService<ISecurityKeyFactory>();
|
||||
IDecoratorService<SecurityKey> secureKeyStore = host.Services.GetRequiredService<IDecoratorService<SecurityKey>>();
|
||||
ILockerStorageFactory lockerStorageFactory = host.Services.GetRequiredService<ILockerStorageFactory>();
|
||||
|
||||
if (keyVaultFactory.Create(Encoding.UTF8.GetBytes(password)) is SecurityKey key)
|
||||
if (componentFactory.Create(name) is IComponentHost host)
|
||||
{
|
||||
secureKeyStore.Set(key);
|
||||
ISecurityKeyFactory keyVaultFactory = host.Services.GetRequiredService<ISecurityKeyFactory>();
|
||||
IDecoratorService<SecurityKey> secureKeyStore = host.Services.GetRequiredService<IDecoratorService<SecurityKey>>();
|
||||
ILockerStorageFactory lockerStorageFactory = host.Services.GetRequiredService<ILockerStorageFactory>();
|
||||
|
||||
if (await lockerStorageFactory.Create(name, key))
|
||||
if (keyVaultFactory.Create(Encoding.UTF8.GetBytes(password)) is SecurityKey key)
|
||||
{
|
||||
IWritableConfiguration<LockerConfiguration> configuration =
|
||||
host.Services.GetRequiredService<IWritableConfiguration<LockerConfiguration>>();
|
||||
secureKeyStore.Set(key);
|
||||
|
||||
configuration.Write(args => args.Key = $"{Convert.ToBase64String(key.Salt)}:{Convert.ToBase64String(key.EncryptedKey)}:{Convert.ToBase64String(key.DecryptedKey)}");
|
||||
host.Start();
|
||||
if (await lockerStorageFactory.Create(name, key))
|
||||
{
|
||||
IWritableConfiguration<LockerConfiguration> configuration =
|
||||
host.Services.GetRequiredService<IWritableConfiguration<LockerConfiguration>>();
|
||||
|
||||
publisher.Publish(Activated.As(host), cancellationToken);
|
||||
return true;
|
||||
configuration.Write(args => args.Key = $"{Convert.ToBase64String(key.Salt)}:{Convert.ToBase64String(key.EncryptedKey)}:{Convert.ToBase64String(key.DecryptedKey)}");
|
||||
host.Start();
|
||||
|
||||
publisher.Publish(Activated.As(host), cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,5 +22,5 @@ public partial class CreateLockerViewModel(IServiceProvider provider,
|
||||
private string password;
|
||||
|
||||
public async Task<bool> Confirm() =>
|
||||
await Mediator.Handle<CreateEventArgs<Locker>, bool>(Create.As(new Locker(Name, Password)));
|
||||
await Mediator.Handle<CreateEventArgs<Locker<(string, string)>>, bool>(Create.As(new Locker<(string, string)>((Name, Password))));
|
||||
}
|
||||
@@ -10,7 +10,7 @@ public class FavouriteItemHandler(IDecoratorService<Item<(Guid, string)>> decora
|
||||
{
|
||||
try
|
||||
{
|
||||
if (decoratorService.Value is Item<(Guid, string)> item)
|
||||
if (decoratorService.Service is Item<(Guid, string)> item)
|
||||
{
|
||||
(Guid id, string name) = item.Value;
|
||||
await mediator.Handle<UpdateEventArgs<(Guid, int)>, bool>(new UpdateEventArgs<(Guid, int)>((id, 1)));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Bitvault;
|
||||
@@ -13,7 +13,33 @@ public partial class ItemEntryViewModel(IServiceProvider provider,
|
||||
string? key = default,
|
||||
object? value = default) :
|
||||
Observable<string, object>(provider, factory, mediator, publisher, subscriber, disposer, key, value),
|
||||
IItemEntryViewModel
|
||||
IItemEntryViewModel,
|
||||
INotificationHandler<UpdateEventArgs<Item>>,
|
||||
INotificationHandler<ConfirmEventArgs<Item>>,
|
||||
INotificationHandler<CancelEventArgs<Item>>
|
||||
{
|
||||
protected override void OnValueChanged() => configuration.Value = Value;
|
||||
[ObservableProperty]
|
||||
private ItemState state = ItemState.Read;
|
||||
|
||||
protected override void OnValueChanged() =>
|
||||
configuration.Value = Value;
|
||||
|
||||
public Task Handle(UpdateEventArgs<Item> args) =>
|
||||
Task.FromResult(State = ItemState.Write);
|
||||
|
||||
public Task Handle(CancelEventArgs<Item> args)
|
||||
{
|
||||
Revert();
|
||||
|
||||
State = ItemState.Read;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Handle(ConfirmEventArgs<Item> args)
|
||||
{
|
||||
Commit();
|
||||
|
||||
State = ItemState.Read;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public partial class ItemViewModel :
|
||||
{
|
||||
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
|
||||
{
|
||||
Factory.Create<ConfirmItemActionViewModel>(State),
|
||||
Factory.Create<ConfirmItemActionViewModel>(),
|
||||
Factory.Create<DismissItemActionViewModel>(),
|
||||
})));
|
||||
}
|
||||
|
||||
+2
-20
@@ -1,23 +1,5 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public record Locker
|
||||
{
|
||||
public Locker(string name, string password)
|
||||
{
|
||||
Name = name;
|
||||
Password = password;
|
||||
}
|
||||
public record Locker<TValue>(TValue Value);
|
||||
|
||||
public Locker(string password)
|
||||
{
|
||||
Password = password;
|
||||
}
|
||||
|
||||
public Locker()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "";
|
||||
|
||||
public string? Password { get; } = "";
|
||||
}
|
||||
public record Locker;
|
||||
@@ -6,12 +6,14 @@ namespace Bitvault;
|
||||
public class OpenLockerHandler(IConfigurationDescriptor<LockerConfiguration> descriptor,
|
||||
ISecurityKeyFactory securityKeyFactory,
|
||||
ILockerStorageFactory lockerStorageFactory) :
|
||||
IHandler<ActivateEventArgs<Locker>, bool>
|
||||
IHandler<ActivateEventArgs<Locker<string>>, bool>
|
||||
{
|
||||
public async Task<bool> Handle(ActivateEventArgs<Locker> args,
|
||||
public async Task<bool> Handle(ActivateEventArgs<Locker<string>> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Value is Locker locker && descriptor.Name is { Length: > 0 } name && locker.Password is { Length: > 0 } password)
|
||||
if (args.Value is Locker<string> locker &&
|
||||
descriptor.Name is { Length: > 0 } name &&
|
||||
locker.Value is { Length: > 0 } password)
|
||||
{
|
||||
LockerConfiguration configuration = descriptor.Value;
|
||||
if (configuration.Key?.Split(':') is { Length: >= 2 } keyPart)
|
||||
|
||||
@@ -24,7 +24,7 @@ public partial class OpenLockerViewModel(IServiceProvider provider,
|
||||
{
|
||||
if (Password is { Length: > 0 })
|
||||
{
|
||||
if (await Mediator.Handle<ActivateEventArgs<Locker>, bool>(Activate.As(new Locker(Password))))
|
||||
if (await Mediator.Handle<ActivateEventArgs<Locker<string>>, bool>(Activate.As(new Locker<string>(Password))))
|
||||
{
|
||||
Publisher.Publish(Opened.As<Locker>());
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public record QueryItemConfiguration
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Bitvault;
|
||||
|
||||
public record QueryLockerConfiguration
|
||||
{
|
||||
public string? Filter { get; set; }
|
||||
|
||||
public string? Query { get; set; }
|
||||
}
|
||||
@@ -6,52 +6,50 @@ using Toolkit.Foundation;
|
||||
namespace Bitvault;
|
||||
|
||||
public class QueryLockerHandler(IDbContextFactory<LockerContext> dbContextFactory) :
|
||||
IHandler<RequestEventArgs<QueryLockerConfiguration>, IReadOnlyCollection<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)>>
|
||||
IHandler<QueryEventArgs<Locker<(string, string)>>, IReadOnlyCollection<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)>>
|
||||
{
|
||||
public async Task<IReadOnlyCollection<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)>> Handle(RequestEventArgs<QueryLockerConfiguration> args,
|
||||
CancellationToken cancellationToken)
|
||||
public async Task<IReadOnlyCollection<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)>>
|
||||
Handle(QueryEventArgs<Locker<(string, string)>> args,CancellationToken cancellationToken)
|
||||
{
|
||||
List<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)> items = [];
|
||||
|
||||
if (args.Value is QueryLockerConfiguration queryConfiguration)
|
||||
if (args.Value is Locker<(string, string)> locker)
|
||||
{
|
||||
(string filter, string text) = locker.Value;
|
||||
|
||||
ExpressionStarter<ItemEntry> predicate =
|
||||
PredicateBuilder.New<ItemEntry>(true);
|
||||
|
||||
if (queryConfiguration.Filter == "All")
|
||||
if (filter == "All")
|
||||
{
|
||||
predicate = predicate.And(x => x.State != 2);
|
||||
}
|
||||
|
||||
if (queryConfiguration.Filter == "Starred")
|
||||
if (filter == "Starred")
|
||||
{
|
||||
predicate = predicate.And(x => x.State != 2 && x.State == 1);
|
||||
}
|
||||
|
||||
if (queryConfiguration.Filter == "Archive")
|
||||
if (filter == "Archive")
|
||||
{
|
||||
predicate = predicate.And(x => x.State == 2);
|
||||
}
|
||||
|
||||
if (queryConfiguration.Query is { Length: > 0 } query)
|
||||
if (text is { Length: > 0 })
|
||||
{
|
||||
predicate = predicate.And(x => EF.Functions.Like(x.Name, $"%{query}%"));
|
||||
predicate = predicate.And(x => EF.Functions.Like(x.Name, $"%{text}%"));
|
||||
}
|
||||
|
||||
var results = await Task.Run(async () =>
|
||||
{
|
||||
using LockerContext context = dbContextFactory.CreateDbContext();
|
||||
return await context.Set<ItemEntry>()
|
||||
.Where(predicate)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.Name,
|
||||
x.Category,
|
||||
Favourite = x.State == 1,
|
||||
Archived = x.State == 2
|
||||
}).ToListAsync();
|
||||
});
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
var results = await context.Set<ItemEntry>()
|
||||
.Where(predicate)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.Name,
|
||||
x.Category,
|
||||
Favourite = x.State == 1,
|
||||
Archived = x.State == 2
|
||||
}).ToListAsync(cancellationToken: cancellationToken);
|
||||
|
||||
foreach (var result in results.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using Bitvault.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Bitvault;
|
||||
|
||||
public class RequestItemHandler(IDbContextFactory<LockerContext> dbContextFactory) :
|
||||
IHandler<RequestEventArgs<Item<Guid>>, (Guid, string, string?, string, ItemConfiguration?)>
|
||||
{
|
||||
public async Task<(Guid, string, string?, string, ItemConfiguration?)> Handle(RequestEventArgs<Item<Guid>> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Value is Item<Guid> item)
|
||||
{
|
||||
Guid id = item.Value;
|
||||
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
var result = await context.Set<ItemEntry>()
|
||||
.Where(x => x.Id == id)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.Name,
|
||||
x.Description,
|
||||
x.Category,
|
||||
Blob = x.Blobs
|
||||
.Where(b => b.Type == 0)
|
||||
.OrderByDescending(b => b.DateTime)
|
||||
.FirstOrDefault()
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
ItemConfiguration? configuration = null;
|
||||
if (result.Blob is BlobEntry blob && blob.Data is { Length: > 0 } data)
|
||||
{
|
||||
try
|
||||
{
|
||||
configuration = JsonSerializer.Deserialize<ItemConfiguration>(data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return (result.Id, result.Name, result.Description, result.Category, configuration);
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,52 @@
|
||||
using Toolkit.Foundation;
|
||||
using System.Reflection;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Bitvault;
|
||||
|
||||
public class SynchronizeItemContentViewModelHandler(IMediator mediator,
|
||||
public class SynchronizeItemContentViewModelHandler(IDecoratorService<Item<(Guid, string)>> itemDecorator,
|
||||
IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
|
||||
IMediator mediator,
|
||||
IServiceFactory serviceFactory,
|
||||
IPublisher publisher) :
|
||||
INotificationHandler<SynchronizeEventArgs<IItemEntryViewModel>>
|
||||
INotificationHandler<SynchronizeEventArgs<ItemSectionViewModel>>
|
||||
{
|
||||
public Task Handle(SynchronizeEventArgs<IItemEntryViewModel> args)
|
||||
public async Task Handle(SynchronizeEventArgs<ItemSectionViewModel> args)
|
||||
{
|
||||
//wModel>(false) is ItemHeaderViewModel viewModel)
|
||||
//{
|
||||
// publisher.Publish(Create.As<IItemEntryViewModel>(viewModel), nameof(ItemViewModel));
|
||||
//}
|
||||
if (itemDecorator.Service is Item<(Guid, string)> item)
|
||||
{
|
||||
if (item.Value is (Guid Id, _))
|
||||
{
|
||||
(_, _, _, _, ItemConfiguration? configuration) = await mediator.Handle<RequestEventArgs<Item<Guid>>, (Guid, string, string?, string,
|
||||
ItemConfiguration?)>(Request.As(new Item<Guid>(Id)));
|
||||
|
||||
return Task.CompletedTask;
|
||||
if (configuration is not null)
|
||||
{
|
||||
itemConfigurationDecorator.Set(configuration);
|
||||
foreach (ItemSectionConfiguration configurationSection in configuration.Sections)
|
||||
{
|
||||
string id = $"{nameof(ItemSection)}:{Guid.NewGuid()}";
|
||||
if (serviceFactory.Create<ItemSectionViewModel>(id)
|
||||
is ItemSectionViewModel sectionViewModel)
|
||||
{
|
||||
publisher.Publish(Create.As(sectionViewModel), nameof(ItemContentViewModel));
|
||||
foreach (IItemEntryConfiguration entryConfiguration in configurationSection.Entries)
|
||||
{
|
||||
Type messageType = typeof(CreateEventArgs<>).MakeGenericType(entryConfiguration.GetType());
|
||||
ConstructorInfo? constructor = messageType.GetConstructor([entryConfiguration.GetType(), typeof(object[])]);
|
||||
|
||||
if (constructor?.Invoke(new object[] { entryConfiguration, new object[] { sectionViewModel } }) is object message)
|
||||
{
|
||||
if (await mediator.Handle<object, IItemEntryViewModel?>(message,
|
||||
entryConfiguration.GetType().Name) is IItemEntryViewModel entryViewModel)
|
||||
{
|
||||
publisher.Publish(Create.As(entryViewModel), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,11 @@ public class SynchronizeItemViewModelHandler(IMediator mediator,
|
||||
cache.Clear();
|
||||
bool selected = true;
|
||||
|
||||
if (await mediator.Handle<RequestEventArgs<QueryLockerConfiguration>,
|
||||
IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)>>(Request.As(new QueryLockerConfiguration
|
||||
{
|
||||
Filter = configuration.Filter,
|
||||
Query = configuration.Query
|
||||
})) is IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)> results)
|
||||
IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)>? results =
|
||||
await mediator.Handle<QueryEventArgs<Locker<(string?, string?)>>,
|
||||
IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)>>(Query.As(new Locker<(string?, string?)>((configuration.Filter, configuration.Query))));
|
||||
|
||||
if (results is not null)
|
||||
{
|
||||
foreach ((Guid Id, string Name, string Category, bool Favourite, bool Archived) in results)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Bitvault.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Bitvault;
|
||||
@@ -12,19 +13,16 @@ public class UnarchiveItemHandler(IDecoratorService<Item<(Guid, string)>> decora
|
||||
{
|
||||
try
|
||||
{
|
||||
if (decoratorService.Value is Item<(Guid, string)> item)
|
||||
if (decoratorService.Service is Item<(Guid, string)> item)
|
||||
{
|
||||
(Guid id, string name) = item.Value;
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
if (await context.FindAsync<ItemEntry>(id) is ItemEntry result)
|
||||
{
|
||||
result.State = 0;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
});
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync();
|
||||
if (await context.FindAsync<ItemEntry>(id) is ItemEntry result)
|
||||
{
|
||||
result.State = 0;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -9,7 +9,7 @@ public class UnfavouriteItemHandler(IDecoratorService<Item<(Guid, string)>> deco
|
||||
{
|
||||
try
|
||||
{
|
||||
if (decoratorService.Value is Item<(Guid, string)> item)
|
||||
if (decoratorService.Service is Item<(Guid, string)> item)
|
||||
{
|
||||
(Guid id, string name) = item.Value;
|
||||
await mediator.Handle<UpdateEventArgs<(Guid, int)>, bool>(new UpdateEventArgs<(Guid, int)>((id, 0)));
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
using Bitvault.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
using Toolkit.Foundation;
|
||||
|
||||
namespace Bitvault;
|
||||
|
||||
public class UpdateItemHander(IDbContextFactory<LockerContext> dbContextFactory) :
|
||||
IHandler<UpdateEventArgs<(Guid, string, ItemConfiguration)>, bool>
|
||||
IHandler<UpdateEventArgs<Item<(Guid, string, ItemConfiguration)>>, bool>
|
||||
{
|
||||
public async Task<bool> Handle(UpdateEventArgs<(Guid, string, ItemConfiguration)> args,
|
||||
public async Task<bool> Handle(UpdateEventArgs<Item<(Guid, string, ItemConfiguration)>> args,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (args.Value is (Guid id, string name, ItemConfiguration configuration))
|
||||
if (args.Value is Item<(Guid, string, ItemConfiguration)> item)
|
||||
{
|
||||
(Guid id, string name, ItemConfiguration configuration) = item.Value;
|
||||
|
||||
try
|
||||
{
|
||||
using LockerContext context = dbContextFactory.CreateDbContext();
|
||||
ItemEntry? result = null;
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
ItemEntry? result = result = await context.Set<ItemEntry>().FindAsync([id], cancellationToken);
|
||||
|
||||
await Task.Run(async () =>
|
||||
if (result is not null)
|
||||
{
|
||||
result = await context.Set<ItemEntry>().FindAsync(id);
|
||||
if (result is not null)
|
||||
string content = JsonSerializer.Serialize(configuration);
|
||||
result.Blobs.Add(new()
|
||||
{
|
||||
result.Name = name;
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}, cancellationToken);
|
||||
Data = Encoding.UTF8.GetBytes(content),
|
||||
DateTime = DateTime.Now,
|
||||
Type = 0,
|
||||
});
|
||||
|
||||
result.Name = name;
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ public class UpdateItemStateHandler(IDbContextFactory<LockerContext> dbContextFa
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync();
|
||||
using LockerContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
if (await context.FindAsync<ItemEntry>(id) is ItemEntry result)
|
||||
{
|
||||
result.State = state;
|
||||
|
||||
Reference in New Issue
Block a user