It it now possible to create/edit item contents

This commit is contained in:
TheXamlGuy
2024-06-06 20:42:23 +01:00
parent 1302145f1c
commit c399e8c05c
30 changed files with 331 additions and 180 deletions
+2 -1
View File
@@ -87,11 +87,12 @@ public partial class App : Application
if (provider.GetRequiredService<IDecoratorService<LockerConnection>>() if (provider.GetRequiredService<IDecoratorService<LockerConnection>>()
is IDecoratorService<LockerConnection> connection) is IDecoratorService<LockerConnection> connection)
{ {
args.UseSqlite($"{connection.Value}"); args.UseSqlite($"{connection.Service}");
} }
}); });
services.AddHandler<QueryLockerHandler>(); services.AddHandler<QueryLockerHandler>();
services.AddHandler<RequestItemHandler>();
services.AddHandler<CreateItemHandler>(); services.AddHandler<CreateItemHandler>();
services.AddHandler<UpdateItemHander>(); services.AddHandler<UpdateItemHander>();
services.AddHandler<UpdateItemStateHandler>(); services.AddHandler<UpdateItemStateHandler>();
+2 -25
View File
@@ -17,36 +17,13 @@
<Interaction.Behaviors> <Interaction.Behaviors>
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Read}"> <DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Read}">
<AddClassAction ClassName="Read" RemoveIfExists="True" /> <AddClassAction ClassName="Read" RemoveIfExists="True" />
<RemoveClassAction ClassName="Write" />
</DataTriggerBehavior> </DataTriggerBehavior>
<DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Write}"> <DataTriggerBehavior Binding="{Binding State}" Value="{x:Static vm:ItemState.Write}">
<AddClassAction ClassName="Write" RemoveIfExists="True" />
<RemoveClassAction ClassName="Read" /> <RemoveClassAction ClassName="Read" />
</DataTriggerBehavior> </DataTriggerBehavior>
</Interaction.Behaviors> </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> </TextBox>
</StackPanel> </StackPanel>
</UserControl> </UserControl>
@@ -6,6 +6,17 @@
x:DataType="vm:ItemMaskedTextEntryViewModel" x:DataType="vm:ItemMaskedTextEntryViewModel"
Header="{Binding Key}"> Header="{Binding Key}">
<SettingsExpander.Footer> <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.Footer>
</SettingsExpander> </SettingsExpander>
+10 -1
View File
@@ -10,6 +10,15 @@
MinWidth="264" MinWidth="264"
Classes="revealPasswordButton" Classes="revealPasswordButton"
PasswordChar="&#x25CF;" PasswordChar="&#x25CF;"
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.Footer>
</SettingsExpander> </SettingsExpander>
+12 -1
View File
@@ -6,6 +6,17 @@
x:DataType="vm:ItemTextEntryViewModel" x:DataType="vm:ItemTextEntryViewModel"
Header="{Binding Key}"> Header="{Binding Key}">
<SettingsExpander.Footer> <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.Footer>
</SettingsExpander> </SettingsExpander>
+38
View File
@@ -4,6 +4,44 @@
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:ItemViewModel"> 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> <Interaction.Behaviors>
<AttachedBehaviour> <AttachedBehaviour>
<NavigateAction <NavigateAction
+1 -1
View File
@@ -13,5 +13,5 @@ public record BlobEntry
[Key] [Key]
public int Id { get; set; } public int Id { get; set; }
public DateTimeOffset DateTime { get; set; } public DateTime DateTime { get; set; }
} }
+2 -2
View File
@@ -17,7 +17,7 @@ public record ItemEntry
public required string Category { get; set; } 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>();
} }
+1 -1
View File
@@ -11,7 +11,7 @@ public class ArchiveItemHandler(IDecoratorService<Item<(Guid, string)>> decorato
{ {
try try
{ {
if (decoratorService.Value is Item<(Guid, string)> item) if (decoratorService.Service is Item<(Guid, string)> item)
{ {
if (cache.Contains(item)) if (cache.Contains(item))
{ {
+2 -2
View File
@@ -3,13 +3,13 @@
namespace Bitvault; namespace Bitvault;
public class ConfirmCreateItemHandler(IMediator mediator, public class ConfirmCreateItemHandler(IMediator mediator,
IDecoratorService<ItemConfiguration> decoratorItemConfiguration, IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
IPublisher publisher) : IPublisher publisher) :
INotificationHandler<ConfirmEventArgs<Item>> INotificationHandler<ConfirmEventArgs<Item>>
{ {
public async Task Handle(ConfirmEventArgs<Item> args) 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>()); string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>, string>(Confirm.As<ItemHeader>());
if (name is not null) if (name is not null)
+9 -8
View File
@@ -2,32 +2,33 @@
namespace Bitvault; namespace Bitvault;
public class ConfirmUpdateItemHandler(IDecoratorService<Item<(Guid, string)>> decoratorItem, public class ConfirmUpdateItemHandler(IDecoratorService<Item<(Guid, string)>> itemDecorator,
IDecoratorService<ItemConfiguration> decoratorItemConfiguration, IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
IMediator mediator, IMediator mediator,
IPublisher publisher) : IPublisher publisher) :
INotificationHandler<ConfirmEventArgs<Item>> INotificationHandler<ConfirmEventArgs<Item>>
{ {
public async Task Handle(ConfirmEventArgs<Item> args) public async Task Handle(ConfirmEventArgs<Item> args)
{
if (itemDecorator?.Service is Item<(Guid, string)> item &&
itemConfigurationDecorator.Service is ItemConfiguration configuration)
{ {
string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>, string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>,
string>(Confirm.As<ItemHeader>()); string>(Confirm.As<ItemHeader>());
if (name is not null) if (name is not null)
{ {
var dd = decoratorItemConfiguration;
publisher.Publish(Notify.As(new ItemHeader<string>(name))); publisher.Publish(Notify.As(new ItemHeader<string>(name)));
if (decoratorItem?.Value is Item<(Guid, string)> item)
{
(Guid id, string _) = item.Value; (Guid id, string _) = item.Value;
Item<(Guid, string)> newItem = new((id, name)); Item<(Guid, string)> newItem = new((id, name));
publisher.Publish(Modified.As(item, newItem)); publisher.Publish(Modified.As(item, newItem));
decoratorItem.Set(newItem); itemDecorator.Set(newItem);
await mediator.Handle<UpdateEventArgs<(Guid, string, ItemConfiguration)>, bool>(new UpdateEventArgs<(Guid, string, await mediator.Handle<UpdateEventArgs<Item<(Guid, string, ItemConfiguration)>>, bool>(new UpdateEventArgs<Item<(Guid, string,
ItemConfiguration)>((id, name, new ItemConfiguration()))); ItemConfiguration)>>(new Item<(Guid, string, ItemConfiguration)>((id, name, configuration))));
} }
} }
} }
+18 -7
View File
@@ -1,6 +1,8 @@
using Bitvault.Data; using Bitvault.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Text;
using System.Text.Json;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
@@ -15,15 +17,24 @@ public class CreateItemHandler(IDbContextFactory<LockerContext> dbContextFactory
{ {
try try
{ {
using LockerContext context = dbContextFactory.CreateDbContext(); string content = JsonSerializer.Serialize(configuration);
EntityEntry<ItemEntry>? result = null; ItemEntry itemEntry = new()
await Task.Run(async () =>
{ {
result = await context.AddAsync(new ItemEntry { Id = id, Name = name, Category = category }, cancellationToken); Id = id,
await context.SaveChangesAsync(cancellationToken); 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) if (result is not null)
{ {
+9 -4
View File
@@ -7,13 +7,16 @@ namespace Bitvault;
public class CreateLockerHandler(ILockerFactory componentFactory, public class CreateLockerHandler(ILockerFactory componentFactory,
IPublisher publisher) : 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) CancellationToken cancellationToken)
{ {
if (args.Value is Locker locker && locker.Name is { Length: > 0 } name && if (args.Value is Locker <(string, string)> locker)
locker.Password is { Length: > 0 } password) {
if (locker.Value is (string name, string password) &&
name is { Length: > 0 } &&
password is { Length: > 0 })
{ {
if (componentFactory.Create(name) is IComponentHost host) if (componentFactory.Create(name) is IComponentHost host)
{ {
@@ -38,6 +41,8 @@ public class CreateLockerHandler(ILockerFactory componentFactory,
} }
} }
} }
}
} }
return false; return false;
+1 -1
View File
@@ -22,5 +22,5 @@ public partial class CreateLockerViewModel(IServiceProvider provider,
private string password; private string password;
public async Task<bool> Confirm() => 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))));
} }
+1 -1
View File
@@ -10,7 +10,7 @@ public class FavouriteItemHandler(IDecoratorService<Item<(Guid, string)>> decora
{ {
try try
{ {
if (decoratorService.Value is Item<(Guid, string)> item) if (decoratorService.Service is Item<(Guid, string)> item)
{ {
(Guid id, string name) = item.Value; (Guid id, string name) = item.Value;
await mediator.Handle<UpdateEventArgs<(Guid, int)>, bool>(new UpdateEventArgs<(Guid, int)>((id, 1))); await mediator.Handle<UpdateEventArgs<(Guid, int)>, bool>(new UpdateEventArgs<(Guid, int)>((id, 1)));
+29 -3
View File
@@ -1,4 +1,4 @@
using System.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
@@ -13,7 +13,33 @@ public partial class ItemEntryViewModel(IServiceProvider provider,
string? key = default, string? key = default,
object? value = default) : object? value = default) :
Observable<string, object>(provider, factory, mediator, publisher, subscriber, disposer, key, value), 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;
}
} }
+1 -1
View File
@@ -118,7 +118,7 @@ public partial class ItemViewModel :
{ {
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable> Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{ {
Factory.Create<ConfirmItemActionViewModel>(State), Factory.Create<ConfirmItemActionViewModel>(),
Factory.Create<DismissItemActionViewModel>(), Factory.Create<DismissItemActionViewModel>(),
}))); })));
} }
+2 -20
View File
@@ -1,23 +1,5 @@
namespace Bitvault; namespace Bitvault;
public record Locker public record Locker<TValue>(TValue Value);
{
public Locker(string name, string password)
{
Name = name;
Password = password;
}
public Locker(string password) public record Locker;
{
Password = password;
}
public Locker()
{
}
public string Name { get; } = "";
public string? Password { get; } = "";
}
+5 -3
View File
@@ -6,12 +6,14 @@ namespace Bitvault;
public class OpenLockerHandler(IConfigurationDescriptor<LockerConfiguration> descriptor, public class OpenLockerHandler(IConfigurationDescriptor<LockerConfiguration> descriptor,
ISecurityKeyFactory securityKeyFactory, ISecurityKeyFactory securityKeyFactory,
ILockerStorageFactory lockerStorageFactory) : 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) 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; LockerConfiguration configuration = descriptor.Value;
if (configuration.Key?.Split(':') is { Length: >= 2 } keyPart) if (configuration.Key?.Split(':') is { Length: >= 2 } keyPart)
+1 -1
View File
@@ -24,7 +24,7 @@ public partial class OpenLockerViewModel(IServiceProvider provider,
{ {
if (Password is { Length: > 0 }) 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>()); Publisher.Publish(Opened.As<Locker>());
} }
-6
View File
@@ -1,6 +0,0 @@
namespace Bitvault;
public record QueryItemConfiguration
{
public int Id { get; set; }
}
-8
View File
@@ -1,8 +0,0 @@
namespace Bitvault;
public record QueryLockerConfiguration
{
public string? Filter { get; set; }
public string? Query { get; set; }
}
+14 -16
View File
@@ -6,42 +6,41 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class QueryLockerHandler(IDbContextFactory<LockerContext> dbContextFactory) : 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, public async Task<IReadOnlyCollection<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)>>
CancellationToken cancellationToken) Handle(QueryEventArgs<Locker<(string, string)>> args,CancellationToken cancellationToken)
{ {
List<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)> items = []; List<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)> items = [];
if (args.Value is Locker<(string, string)> locker)
if (args.Value is QueryLockerConfiguration queryConfiguration)
{ {
(string filter, string text) = locker.Value;
ExpressionStarter<ItemEntry> predicate = ExpressionStarter<ItemEntry> predicate =
PredicateBuilder.New<ItemEntry>(true); PredicateBuilder.New<ItemEntry>(true);
if (queryConfiguration.Filter == "All") if (filter == "All")
{ {
predicate = predicate.And(x => x.State != 2); predicate = predicate.And(x => x.State != 2);
} }
if (queryConfiguration.Filter == "Starred") if (filter == "Starred")
{ {
predicate = predicate.And(x => x.State != 2 && x.State == 1); predicate = predicate.And(x => x.State != 2 && x.State == 1);
} }
if (queryConfiguration.Filter == "Archive") if (filter == "Archive")
{ {
predicate = predicate.And(x => x.State == 2); 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 = await dbContextFactory.CreateDbContextAsync(cancellationToken);
{ var results = await context.Set<ItemEntry>()
using LockerContext context = dbContextFactory.CreateDbContext();
return await context.Set<ItemEntry>()
.Where(predicate) .Where(predicate)
.Select(x => new .Select(x => new
{ {
@@ -50,8 +49,7 @@ public class QueryLockerHandler(IDbContextFactory<LockerContext> dbContextFactor
x.Category, x.Category,
Favourite = x.State == 1, Favourite = x.State == 1,
Archived = x.State == 2 Archived = x.State == 2
}).ToListAsync(); }).ToListAsync(cancellationToken: cancellationToken);
});
foreach (var result in results.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)) foreach (var result in results.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase))
{ {
+55
View File
@@ -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; namespace Bitvault;
public class SynchronizeItemContentViewModelHandler(IMediator mediator, public class SynchronizeItemContentViewModelHandler(IDecoratorService<Item<(Guid, string)>> itemDecorator,
IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
IMediator mediator,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher) : 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) if (itemDecorator.Service is Item<(Guid, string)> item)
//{ {
// publisher.Publish(Create.As<IItemEntryViewModel>(viewModel), nameof(ItemViewModel)); 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);
}
}
}
}
}
}
}
}
} }
} }
+5 -6
View File
@@ -17,12 +17,11 @@ public class SynchronizeItemViewModelHandler(IMediator mediator,
cache.Clear(); cache.Clear();
bool selected = true; bool selected = true;
if (await mediator.Handle<RequestEventArgs<QueryLockerConfiguration>, IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)>? results =
IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)>>(Request.As(new QueryLockerConfiguration 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))));
Filter = configuration.Filter,
Query = configuration.Query if (results is not null)
})) is IReadOnlyCollection<(Guid Id, string Name, string Category, bool Favourite, bool Archived)> results)
{ {
foreach ((Guid Id, string Name, string Category, bool Favourite, bool Archived) in results) foreach ((Guid Id, string Name, string Category, bool Favourite, bool Archived) in results)
{ {
+3 -5
View File
@@ -1,5 +1,6 @@
using Bitvault.Data; using Bitvault.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Threading;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
@@ -12,19 +13,16 @@ public class UnarchiveItemHandler(IDecoratorService<Item<(Guid, string)>> decora
{ {
try try
{ {
if (decoratorService.Value is Item<(Guid, string)> item) if (decoratorService.Service is Item<(Guid, string)> item)
{ {
(Guid id, string name) = item.Value; (Guid id, string name) = item.Value;
await Task.Run(async () =>
{
using LockerContext context = await dbContextFactory.CreateDbContextAsync();
using LockerContext context = await dbContextFactory.CreateDbContextAsync();
if (await context.FindAsync<ItemEntry>(id) is ItemEntry result) if (await context.FindAsync<ItemEntry>(id) is ItemEntry result)
{ {
result.State = 0; result.State = 0;
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
});
} }
} }
catch catch
+1 -1
View File
@@ -9,7 +9,7 @@ public class UnfavouriteItemHandler(IDecoratorService<Item<(Guid, string)>> deco
{ {
try try
{ {
if (decoratorService.Value is Item<(Guid, string)> item) if (decoratorService.Service is Item<(Guid, string)> item)
{ {
(Guid id, string name) = item.Value; (Guid id, string name) = item.Value;
await mediator.Handle<UpdateEventArgs<(Guid, int)>, bool>(new UpdateEventArgs<(Guid, int)>((id, 0))); await mediator.Handle<UpdateEventArgs<(Guid, int)>, bool>(new UpdateEventArgs<(Guid, int)>((id, 0)));
+17 -9
View File
@@ -1,31 +1,39 @@
using Bitvault.Data; using Bitvault.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Text.Json;
using System.Text;
using Toolkit.Foundation; using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public class UpdateItemHander(IDbContextFactory<LockerContext> dbContextFactory) : 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) 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 try
{ {
using LockerContext context = dbContextFactory.CreateDbContext(); using LockerContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
ItemEntry? result = null; ItemEntry? result = result = await context.Set<ItemEntry>().FindAsync([id], cancellationToken);
await Task.Run(async () =>
{
result = await context.Set<ItemEntry>().FindAsync(id);
if (result is not null) if (result is not null)
{ {
string content = JsonSerializer.Serialize(configuration);
result.Blobs.Add(new()
{
Data = Encoding.UTF8.GetBytes(content),
DateTime = DateTime.Now,
Type = 0,
});
result.Name = name; result.Name = name;
await context.SaveChangesAsync(cancellationToken); await context.SaveChangesAsync(cancellationToken);
} }
}, cancellationToken);
if (result is not null) if (result is not null)
{ {
+1 -1
View File
@@ -14,7 +14,7 @@ public class UpdateItemStateHandler(IDbContextFactory<LockerContext> dbContextFa
{ {
await Task.Run(async () => 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) if (await context.FindAsync<ItemEntry>(id) is ItemEntry result)
{ {
result.State = state; result.State = state;