Mass rename project for rebranding

This commit is contained in:
TheXamlGuy
2024-06-09 14:00:36 +01:00
parent 1f777e19cd
commit 45712d0a70
241 changed files with 310 additions and 366 deletions
+11
View File
@@ -0,0 +1,11 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class AddItemNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer),
IItemEntryViewModel;
+11
View File
@@ -0,0 +1,11 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class AllNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string filter) : FilterWalletNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, filter);
+10
View File
@@ -0,0 +1,10 @@
namespace Wallet;
public record Archive
{
public static ArchiveEventArgs<TValue> As<TValue>(TValue value) =>
new(value);
public static ArchiveEventArgs<TValue> As<TValue>() where TValue : new() =>
new(new TValue());
}
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record ArchiveEventArgs<TValue>(TValue Value);
+16
View File
@@ -0,0 +1,16 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class ArchiveItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer),
IRemovable
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Archive.As<Item>());
}
+32
View File
@@ -0,0 +1,32 @@
using Toolkit.Foundation;
namespace Wallet;
public class ArchiveItemHandler(IDecoratorService<Item<(Guid, string)>> decoratorService,
ICache<Item<(Guid, string)>> cache,
IMediator mediator) :
INotificationHandler<ArchiveEventArgs<Item>>
{
public async Task Handle(ArchiveEventArgs<Item> args)
{
try
{
if (decoratorService.Service is Item<(Guid, string)> item)
{
if (cache.Contains(item))
{
(Guid id, string name) = item.Value;
await mediator.Handle<UpdateEventArgs<(Guid, int)>,
bool>(new UpdateEventArgs<(Guid, int)>((id, 2)));
cache.Remove(item);
}
}
}
catch
{
}
}
}
+11
View File
@@ -0,0 +1,11 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class ArchiveNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string name) : FilterWalletNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, name);
+7
View File
@@ -0,0 +1,7 @@
namespace Wallet;
public record AttachmentEntryConfiguration :
ItemEntryConfiguration
{
}
+15
View File
@@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class BackActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
private int index = 0;
}
+11
View File
@@ -0,0 +1,11 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class CategoriesNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string name) : FilterWalletNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, name);
+10
View File
@@ -0,0 +1,10 @@
using Toolkit.Foundation;
namespace Wallet;
public record Closed
{
public static ChangedEventArgs<TValue> As<TValue>(TValue value) => new(value);
public static ChangedEventArgs<TValue> As<TValue>() where TValue : new() => new(new TValue());
}
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record ClosedEventArgs<TValue>(TValue? Value = default);
+25
View File
@@ -0,0 +1,25 @@
using Toolkit.Foundation;
namespace Wallet;
public class ConfirmCreateItemHandler(IMediator mediator,
IDecoratorService<ItemConfiguration> itemConfigurationDecorator,
IPublisher publisher) :
INotificationHandler<ConfirmEventArgs<Item>>
{
public async Task Handle(ConfirmEventArgs<Item> args)
{
if (itemConfigurationDecorator.Service is ItemConfiguration configuration)
{
string? name = await mediator.Handle<ConfirmEventArgs<ItemHeader>, string>(Confirm.As<ItemHeader>());
if (name is not null)
{
Guid id = Guid.NewGuid();
publisher.Publish(Created.As(new Item<(Guid, string)>((id, name))));
await mediator.Handle<CreateEventArgs<(Guid, string, string,
ItemConfiguration)>, bool>(new CreateEventArgs<(Guid, string, string, ItemConfiguration)>((id, name, "", configuration)));
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class ConfirmItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Confirm.As<Item>());
}
+35
View File
@@ -0,0 +1,35 @@
using Toolkit.Foundation;
namespace Wallet;
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)
{
if (itemDecorator?.Service is Item<(Guid, string)> item &&
itemConfigurationDecorator.Service is ItemConfiguration configuration)
{
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));
itemDecorator.Set(newItem);
await mediator.Handle<UpdateEventArgs<Item<(Guid, string, ItemConfiguration)>>, bool>(new UpdateEventArgs<Item<(Guid, string,
ItemConfiguration)>>(new Item<(Guid, string, ItemConfiguration)>((id, name, configuration))));
}
}
}
}
+19
View File
@@ -0,0 +1,19 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class CreateItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
NamedComponent named) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
private int index = 0;
[ObservableProperty]
private string named = $"{named}";
}
+51
View File
@@ -0,0 +1,51 @@
using Wallet.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Text;
using System.Text.Json;
using Toolkit.Foundation;
namespace Wallet;
public class CreateItemHandler(IDbContextFactory<WalletContext> dbContextFactory) :
IHandler<CreateEventArgs<(Guid, string, string, ItemConfiguration)>, bool>
{
public async Task<bool> Handle(CreateEventArgs<(Guid, string, string, ItemConfiguration)> args,
CancellationToken cancellationToken)
{
if (args.Value is (Guid id, string name, string category, ItemConfiguration configuration))
{
try
{
string content = JsonSerializer.Serialize(configuration);
ItemEntry itemEntry = new()
{
Id = id,
Name = name,
Category = category
};
itemEntry.Blobs.Add(new()
{
Data = Encoding.UTF8.GetBytes(content),
DateTime = DateTime.Now,
Type = 0,
});
using WalletContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken);
EntityEntry<ItemEntry>? result = await context.AddAsync(itemEntry, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
if (result is not null)
{
return true;
}
}
catch
{
}
}
return false;
}
}
+50
View File
@@ -0,0 +1,50 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text;
using Toolkit.Foundation;
namespace Wallet;
public class CreateWalletHandler(IWalletFactory componentFactory,
IPublisher publisher) :
IHandler<CreateEventArgs<Wallet<(string, string)>>, bool>
{
public async Task<bool> Handle(CreateEventArgs<Wallet<(string, string)>> args,
CancellationToken cancellationToken)
{
if (args.Value is Wallet <(string, string)> Wallet)
{
if (Wallet.Value is (string name, string password) &&
name is { Length: > 0 } &&
password is { Length: > 0 })
{
if (componentFactory.Create(name) is IComponentHost host)
{
ISecurityKeyFactory keyFactory = host.Services.GetRequiredService<ISecurityKeyFactory>();
IDecoratorService<SecurityKey> secureKeyStore = host.Services.GetRequiredService<IDecoratorService<SecurityKey>>();
IWalletStorageFactory WalletStorageFactory = host.Services.GetRequiredService<IWalletStorageFactory>();
if (keyFactory.Create(Encoding.UTF8.GetBytes(password)) is SecurityKey key)
{
secureKeyStore.Set(key);
if (await WalletStorageFactory.Create(name, key))
{
IWritableConfiguration<WalletConfiguration> configuration =
host.Services.GetRequiredService<IWritableConfiguration<WalletConfiguration>>();
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;
}
}
}
}
}
return false;
}
}
+12
View File
@@ -0,0 +1,12 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class CreateWalletNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer),
IMainNavigationViewModel;
+26
View File
@@ -0,0 +1,26 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Toolkit.Foundation;
namespace Wallet;
public partial class CreateWalletViewModel(IServiceProvider provider,
IServiceFactory factory,
IPublisher publisher,
IMediator mediator,
ISubscription subscriber,
IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer),
IPrimaryConfirmation
{
[MaybeNull]
[ObservableProperty]
private string name;
[MaybeNull]
[ObservableProperty]
private string password;
public async Task<bool> Confirm() =>
await Mediator.Handle<CreateEventArgs<Wallet<(string, string)>>, bool>(Create.As(new Wallet<(string, string)>((Name, Password))));
}
+7
View File
@@ -0,0 +1,7 @@
namespace Wallet;
public record CurrencyEntryConfiguration :
ItemEntryConfiguration
{
}
+7
View File
@@ -0,0 +1,7 @@
namespace Wallet;
public record DateEntryConfiguration :
ItemEntryConfiguration
{
}
+15
View File
@@ -0,0 +1,15 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class DateEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) : ItemEntryViewModel(provider, factory, mediator, publisher, subscriber, disposer, state, configuration, key, value, width);
+31
View File
@@ -0,0 +1,31 @@
using Toolkit.Foundation;
namespace Wallet;
public class DateEntryViewModelHandler(IServiceFactory serviceFactory) :
IHandler<CreateEventArgs<DateEntryConfiguration>, IItemEntryViewModel?>
{
public Task<IItemEntryViewModel?> Handle(CreateEventArgs<DateEntryConfiguration> args,
CancellationToken cancellationToken)
{
if (args.Value is DateEntryConfiguration configuration)
{
string? label = configuration.Label;
if (!DateTimeOffset.TryParse($"{configuration.Value}", out DateTimeOffset value))
{
value = DateTimeOffset.Now;
}
double? width = configuration.Width;
if (serviceFactory.Create<DateEntryViewModel>([.. args.Parameters, configuration, label, value, width])
is DateEntryViewModel viewModel)
{
return Task.FromResult<IItemEntryViewModel?>(viewModel);
}
}
return Task.FromResult<IItemEntryViewModel?>(default);
}
}
+15
View File
@@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class DeleteItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Delete.As<Item>());
}
+13
View File
@@ -0,0 +1,13 @@
using System.Text.RegularExpressions;
namespace Wallet;
public partial class DigitRule :
IPasswordRule
{
public int CalculateScore(string password) =>
Regex().IsMatch(password) ? 2 : 0;
[GeneratedRegex(@"\d")]
private static partial Regex Regex();
}
+15
View File
@@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class DismissItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Cancel.As<Item>());
}
+29
View File
@@ -0,0 +1,29 @@
using System.Text.RegularExpressions;
namespace Wallet;
public partial class DiversityBonusRule :
IPasswordRule
{
public int CalculateScore(string password)
{
bool hasUppercase = UppercaseRegex().IsMatch(password);
bool hasLowercase = LowercaseRegex().IsMatch(password);
bool hasDigit = DigitRegex().IsMatch(password);
bool hasSpecialChar = SpecialCharRegex().IsMatch(password);
return (hasUppercase && hasLowercase && hasDigit && hasSpecialChar) ? 2 : 0;
}
[GeneratedRegex(@"[A-Z]")]
private static partial Regex UppercaseRegex();
[GeneratedRegex(@"[a-z]")]
private static partial Regex LowercaseRegex();
[GeneratedRegex(@"\d")]
private static partial Regex DigitRegex();
[GeneratedRegex(@"[^a-zA-Z0-9]")]
private static partial Regex SpecialCharRegex();
}
+10
View File
@@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Wallet;
public record DropdownEntryConfiguration :
ItemEntryConfiguration
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IList<string> Values { get; set; } = new List<string>();
}
+39
View File
@@ -0,0 +1,39 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class DropdownEntryViewModel :
ItemEntryCollectionViewModel<DropdownValueViewModel>
{
public DropdownEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IEnumerable<DropdownValueViewModel> items,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width,
DropdownValueViewModel selectedItem) : base(provider, factory, mediator, publisher, subscriber, disposer, items, state, configuration, key, value, width)
{
SelectedItem = selectedItem;
}
public DropdownEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IEnumerable<DropdownValueViewModel> items,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) : base(provider, factory, mediator, publisher, subscriber, disposer, items, state, configuration, key, value, width)
{
}
}
+34
View File
@@ -0,0 +1,34 @@
using Toolkit.Foundation;
namespace Wallet;
public class DropdownEntryViewModelHandler(IServiceFactory serviceFactory) :
IHandler<CreateEventArgs<DropdownEntryConfiguration>, IItemEntryViewModel?>
{
public Task<IItemEntryViewModel?> Handle(CreateEventArgs<DropdownEntryConfiguration> args,
CancellationToken cancellationToken)
{
if (args.Value is DropdownEntryConfiguration configuration)
{
List<DropdownValueViewModel> values = [];
foreach (string item in configuration.Values)
{
values.Add(serviceFactory.Create<DropdownValueViewModel>(item));
}
string? label = configuration.Label;
object? value = configuration.Value ?? "";
double? width = configuration.Width;
DropdownValueViewModel? selected = values.FirstOrDefault(x => x.Value is not null && x.Value.Equals($"{value}"));
if (serviceFactory.Create<DropdownEntryViewModel>([values, .. args.Parameters, configuration, label, value, width, selected])
is DropdownEntryViewModel viewModel)
{
return Task.FromResult<IItemEntryViewModel?>(viewModel);
}
}
return Task.FromResult<IItemEntryViewModel?>(default);
}
}
+11
View File
@@ -0,0 +1,11 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class DropdownValueViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string? value = null) : Observable<string>(provider, factory, mediator, publisher, subscriber, disposer, value);
+15
View File
@@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class EditItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Update.As<Item>());
}
+16
View File
@@ -0,0 +1,16 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class EmptyItemCollectionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
NamedComponent named) : Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
private string named = $"{named}";
}
+10
View File
@@ -0,0 +1,10 @@
namespace Wallet;
public record Favourite
{
public static FavouriteEventArgs<TValue> As<TValue>(TValue value) =>
new(value);
public static FavouriteEventArgs<TValue> As<TValue>() where TValue : new() =>
new(new TValue());
}
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record FavouriteEventArgs<TValue>(TValue Value);
+29
View File
@@ -0,0 +1,29 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class FavouriteItemActionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
bool value = false) : Observable<bool>(provider, factory, mediator, publisher, subscriber, disposer, value),
IRemovable
{
[RelayCommand]
public void Invoke()
{
if (!Value)
{
Value = true;
Publisher.Publish(Favourite.As<Item>());
}
else
{
Value = false;
Publisher.Publish(Unfavourite.As<Item>());
}
}
}
+23
View File
@@ -0,0 +1,23 @@
using Toolkit.Foundation;
namespace Wallet;
public class FavouriteItemHandler(IDecoratorService<Item<(Guid, string)>> decoratorService,
IMediator mediator) :
INotificationHandler<FavouriteEventArgs<Item>>
{
public async Task Handle(FavouriteEventArgs<Item> args)
{
try
{
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)));
}
}
catch
{
}
}
}
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record Filter(string? Value);
+41
View File
@@ -0,0 +1,41 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class FilterWalletNavigationViewModel : Observable,
IWalletNavigationViewModel,
INotificationHandler<ActivatedEventArgs<Wallet>>,
INotificationHandler<DeactivatedEventArgs<Wallet>>
{
[ObservableProperty]
private bool activated;
[ObservableProperty]
private string? filter;
[ObservableProperty]
private bool selected;
public FilterWalletNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string? filter = null) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Filter = filter;
}
public Task Handle(DeactivatedEventArgs<Wallet> args) =>
Task.FromResult(Activated = false);
public Task Handle(ActivatedEventArgs<Wallet> args) =>
Task.FromResult(Activated = true);
[RelayCommand]
public void Invoke() => Publisher.Publish(Notify.As(new Filter(Filter)),
nameof(ItemCollectionViewModel));
}
+17
View File
@@ -0,0 +1,17 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class FooterViewModel :
ObservableCollection<IMainNavigationViewModel>
{
public FooterViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Add<ManageNavigationViewModel>();
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace Wallet;
public record HyperlinkEntryConfiguration :
ItemEntryConfiguration
{
}
+4
View File
@@ -0,0 +1,4 @@
namespace Wallet;
public interface IItemConfigurationCollection :
IReadOnlyDictionary<string, Func<ItemConfiguration>>;
+8
View File
@@ -0,0 +1,8 @@
namespace Wallet;
public interface IItemEntryConfiguration
{
string? Label { get; set; }
object? Value { get; set; }
}
+4
View File
@@ -0,0 +1,4 @@
namespace Wallet;
public interface IItemEntryViewModel :
IDisposable;
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public interface IMainNavigationViewModel : IDisposable;
+6
View File
@@ -0,0 +1,6 @@
namespace Wallet;
public interface IPasswordRule
{
int CalculateScore(string password);
}
+8
View File
@@ -0,0 +1,8 @@
namespace Wallet;
public interface ISecurityKeyFactory
{
SecurityKey? Create(byte[] phrase,
byte[]? encryptedKey = null,
byte[]? salt = null);
}
+5
View File
@@ -0,0 +1,5 @@
using Toolkit.Foundation;
namespace Wallet;
public interface IWalletComponent : IComponent;
+9
View File
@@ -0,0 +1,9 @@
using Toolkit.Foundation;
namespace Wallet
{
public interface IWalletFactory
{
IComponentHost? Create(string name);
}
}
+5
View File
@@ -0,0 +1,5 @@
using Toolkit.Foundation;
namespace Wallet;
public interface IWalletHost : IComponentHost;
+9
View File
@@ -0,0 +1,9 @@
using Toolkit.Foundation;
namespace Wallet;
public interface IWalletHostCollection :
IEnumerable<IComponentHost>
{
void Add(IComponentHost host);
}
+7
View File
@@ -0,0 +1,7 @@
using Toolkit.Foundation;
namespace Wallet;
public interface IWalletNavigationViewModel :
ISelectable,
IDisposable;
+6
View File
@@ -0,0 +1,6 @@
namespace Wallet;
public interface IWalletStorageFactory
{
Task<bool> Create(string name, SecurityKey key);
}
+15
View File
@@ -0,0 +1,15 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class IconViewModel : Observable
{
public IconViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace Wallet;
public record ImageEntryConfiguration :
ItemEntryConfiguration
{
}
+5
View File
@@ -0,0 +1,5 @@
namespace Wallet;
public record Item<TValue>(TValue Value);
public record Item;
+5
View File
@@ -0,0 +1,5 @@
namespace Wallet;
public record ItemCategory<TValue>(TValue Value);
public record ItemCategory;
+28
View File
@@ -0,0 +1,28 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
[Notification(typeof(CreateEventArgs<ItemCategoryNavigationViewModel>), nameof(ItemCategoryCollectionViewModel))]
public partial class ItemCategoryCollectionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template) :
ObservableCollection<ItemCategoryNavigationViewModel>(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
private IContentTemplate template = template;
public override Task OnActivated()
{
Publisher.Publish(Notify.As(Factory.Create<WalletCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<BackActionViewModel>(),
})));
return base.OnActivated();
}
}
+27
View File
@@ -0,0 +1,27 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemCategoryNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string name,
bool selected = false) :
Observable(provider, factory, mediator, publisher, subscriber, disposer),
ISelectable,
IRemovable
{
[ObservableProperty]
private string name = name;
[ObservableProperty]
private bool selected = selected;
[RelayCommand]
public void Invoke() => Publisher.Publish(Notify.As(new ItemCategory<string>(Name)));
}
+75
View File
@@ -0,0 +1,75 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
[Notification(typeof(CreateEventArgs<ItemNavigationViewModel>), nameof(ItemCollectionViewModel))]
[Notification(typeof(InsertEventArgs<ItemNavigationViewModel>), nameof(ItemCollectionViewModel))]
[Notification(typeof(MoveToEventArgs<ItemNavigationViewModel>), nameof(ItemCollectionViewModel))]
[Notification(typeof(NotifyEventArgs<Search<string>>), nameof(ItemCollectionViewModel))]
public partial class ItemCollectionViewModel :
ObservableCollection<ItemNavigationViewModel>,
INotificationHandler<NotifyEventArgs<Filter>>,
INotificationHandler<NotifyEventArgs<Search<string>>>,
IBackStack
{
[ObservableProperty]
public string? named;
private WalletViewModelConfiguration configuration;
public ItemCollectionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template,
NamedComponent named,
WalletViewModelConfiguration configuration,
string? filter = null) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
Named = $"{named}";
this.configuration = configuration with { Filter = filter };
}
public IContentTemplate Template { get; set; }
public Task Handle(NotifyEventArgs<Filter> args)
{
if (args.Value is Filter filter)
{
configuration = configuration with { Filter = filter.Value };
Fetch(true);
}
return Task.CompletedTask;
}
public Task Handle(NotifyEventArgs<Search<string>> args)
{
if (args.Value is Search<string> search)
{
configuration = configuration with { Query = search.Value };
Fetch(true);
}
return Task.CompletedTask;
}
public override Task OnActivated()
{
Publisher.Publish(Notify.As(Factory.Create<WalletCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<CreateItemActionViewModel>(),
Factory.Create<SearchWalletActionViewModel>(),
})));
return base.OnActivated();
}
protected override SynchronizeExpression BuildAggregateExpression() =>
new(Synchronize.As<ItemNavigationViewModel, WalletViewModelConfiguration>(configuration));
}
+6
View File
@@ -0,0 +1,6 @@
using System.Collections.ObjectModel;
namespace Wallet;
public class ItemCommandHeaderCollection(IList<IDisposable> list) :
ReadOnlyCollection<IDisposable>(list);
+31
View File
@@ -0,0 +1,31 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemCommandHeaderViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template) :
ObservableCollection(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<NotifyEventArgs<ItemCommandHeaderCollection>>
{
public IContentTemplate Template { get; set; } = template;
public Task Handle(NotifyEventArgs<ItemCommandHeaderCollection> args)
{
Clear();
if (args.Value is ItemCommandHeaderCollection commandCollection)
{
foreach (IDisposable command in commandCollection)
{
Add(command);
}
}
return Task.CompletedTask;
}
}
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
using System.Collections.ObjectModel;
namespace Wallet;
public class ItemConfigurationCollection(IDictionary<string, Func<ItemConfiguration>> dictionary) :
ReadOnlyDictionary<string, Func<ItemConfiguration>>(dictionary),
IItemConfigurationCollection;
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record ItemContent;
+30
View File
@@ -0,0 +1,30 @@
using Toolkit.Foundation;
namespace Wallet;
[Notification(typeof(CreateEventArgs<ItemSectionViewModel>), nameof(ItemContentViewModel))]
public partial class ItemContentViewModel(IServiceProvider provider,
IServiceFactory factory, IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template) :
ObservableCollection<ItemSectionViewModel>(provider, factory, mediator, publisher, subscriber, disposer),
IItemEntryViewModel,
INotificationHandler<NotifyEventArgs<ItemCategory<string>>>
{
public IContentTemplate Template { get; set; } = template;
public Task Handle(NotifyEventArgs<ItemCategory<string>> args)
{
if (args.Value is ItemCategory<string> category)
{
if (category.Value is string value)
{
Fetch(() => new SynchronizeExpression(new SynchronizeEventArgs<IItemEntryViewModel, string>(value)), true);
}
}
return Task.CompletedTask;
}
}
+36
View File
@@ -0,0 +1,36 @@
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Wallet;
public class ItemCreatedHandler(IServiceProvider serviceProvider,
ICache<Item<(Guid, string)>> cache,
IPublisher publisher) :
INotificationHandler<CreatedEventArgs<Item<(Guid, string)>>>
{
public Task Handle(CreatedEventArgs<Item<(Guid, string)>> args)
{
if (args.Value is Item<(Guid, string)> item)
{
(Guid id, string name) = item.Value;
IServiceScope serviceScope = serviceProvider.CreateScope();
IServiceFactory serviceFactory = serviceScope.ServiceProvider.GetRequiredService<IServiceFactory>();
IDecoratorService<Item<(Guid, string)>> decoratorService = serviceScope.ServiceProvider.GetRequiredService<IDecoratorService<Item<(Guid, string)>>>();
if (serviceFactory.Create<ItemNavigationViewModel>(id, name, "Description", true)
is ItemNavigationViewModel viewModel)
{
cache.Add(item);
int index = cache.IndexOf(item);
decoratorService.Set(item);
publisher.Publish(Insert.As(index, viewModel),
nameof(ItemCollectionViewModel));
}
}
return Task.CompletedTask;
}
}
+87
View File
@@ -0,0 +1,87 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemEntryCollectionViewModel<TItem> :
ObservableCollection<TItem, string, object>,
IItemEntryViewModel,
INotificationHandler<UpdateEventArgs<Item>>,
INotificationHandler<ConfirmEventArgs<Item>>,
INotificationHandler<CancelEventArgs<Item>>
where TItem : notnull,
IDisposable
{
[ObservableProperty]
private ItemState state;
private readonly ItemEntryConfiguration configuration;
public ItemEntryCollectionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) : base(provider, factory, mediator, publisher, subscriber, disposer, key, value)
{
this.configuration = configuration;
State = state;
Width = width;
}
[ObservableProperty]
private double width;
public ItemEntryCollectionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IEnumerable<TItem> items,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) : base(provider, factory, mediator, publisher, subscriber, disposer, items, key, value)
{
this.configuration = configuration;
State = state;
Width = width;
}
protected override void OnValueChanged()
{
if (configuration is not null)
{
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;
}
}
+28
View File
@@ -0,0 +1,28 @@
using System.Text.Json.Serialization;
namespace Wallet;
[JsonDerivedType(typeof(DropdownEntryConfiguration), typeDiscriminator: "Dropdown")]
[JsonDerivedType(typeof(MaskedTextEntryConfiguration), typeDiscriminator: "MaskedText")]
[JsonDerivedType(typeof(NumberEntryConfiguration), typeDiscriminator: "Number")]
[JsonDerivedType(typeof(PasswordEntryConfiguration), typeDiscriminator: "Password")]
[JsonDerivedType(typeof(TextEntryConfiguration), typeDiscriminator: "Text")]
[JsonDerivedType(typeof(ImageEntryConfiguration), typeDiscriminator: "Image")]
[JsonDerivedType(typeof(AttachmentEntryConfiguration), typeDiscriminator: "Attachment")]
[JsonDerivedType(typeof(MultilineTextEntryConfiguration), typeDiscriminator: "MultilineText")]
[JsonDerivedType(typeof(CurrencyEntryConfiguration), typeDiscriminator: "Currency")]
[JsonDerivedType(typeof(DateEntryConfiguration), typeDiscriminator: "Date")]
[JsonDerivedType(typeof(HyperlinkEntryConfiguration), typeDiscriminator: "Hyperlink")]
[JsonDerivedType(typeof(PinEntryConfiguration), typeDiscriminator: "Pin")]
public record ItemEntryConfiguration :
IItemEntryConfiguration
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Label { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public object? Value { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public double? Width { get; set; } = 296;
}
+50
View File
@@ -0,0 +1,50 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) :
Observable<string, object>(provider, factory, mediator, publisher, subscriber, disposer, key, value),
IItemEntryViewModel,
INotificationHandler<UpdateEventArgs<Item>>,
INotificationHandler<ConfirmEventArgs<Item>>,
INotificationHandler<CancelEventArgs<Item>>
{
[ObservableProperty]
private ItemState state = state;
[ObservableProperty]
private double width = width;
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;
}
protected override void OnValueChanged() =>
configuration.Value = Value;
}
+5
View File
@@ -0,0 +1,5 @@
namespace Wallet;
public record ItemHeader<TValue>(TValue Value);
public record ItemHeader;
+74
View File
@@ -0,0 +1,74 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemHeaderViewModel :
Observable<string, string>,
INotificationHandler<UpdateEventArgs<Item>>,
INotificationHandler<ConfirmEventArgs<Item>>,
INotificationHandler<CancelEventArgs<Item>>,
IHandler<ValidationEventArgs<ItemHeader>, bool>,
IHandler<ConfirmEventArgs<ItemHeader>, string?>,
INotificationHandler<NotifyEventArgs<ItemCategory<string>>>
{
[ObservableProperty]
private string? category;
[ObservableProperty]
private ItemState state;
public ItemHeaderViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
string key,
string value) : base(provider, factory, mediator, publisher, subscriber, disposer, key, value)
{
State = state;
Value = value;
Track(nameof(Value), () => Value, newValue => Value = newValue);
}
public Task<bool> Handle(ValidationEventArgs<ItemHeader> args,
CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
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;
}
public Task Handle(NotifyEventArgs<ItemCategory<string>> args)
{
if (args.Value is ItemCategory<string> category)
{
Category = category.Value;
}
return Task.CompletedTask;
}
public Task<string> Handle(ConfirmEventArgs<ItemHeader> args,
CancellationToken cancellationToken) => Task.FromResult(Value);
}
+38
View File
@@ -0,0 +1,38 @@
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Wallet;
public class ItemModifiedHandler(IServiceProvider serviceProvider,
IPublisher publisher) :
INotificationHandler<ModifiedEventArgs<Item<(Guid, string)>>>
{
public Task Handle(ModifiedEventArgs<Item<(Guid, string)>> args)
{
Item<(Guid, string)> oldItem = args.OldView;
Item<(Guid, string)> newItem = args.NewValue;
ICache<Item<(Guid, string)>> cache = serviceProvider.GetRequiredService<ICache<Item<(Guid, string)>>>();
if (cache.TryGetValue(oldItem, out Item<(Guid, string)>? cachedItem))
{
if (cachedItem is not null)
{
IServiceScope serviceScope = serviceProvider.CreateScope();
IDecoratorService<Item<(Guid, string)>> decoratorService = serviceScope.ServiceProvider.GetRequiredService<IDecoratorService<Item<(Guid, string)>>>();
int oldIndex = cache.IndexOf(cachedItem);
cache.Remove(cachedItem);
cache.Add(newItem);
int newIndex = cache.IndexOf(newItem);
decoratorService.Set(newItem);
publisher.Publish(MoveTo.As<ItemNavigationViewModel>(oldIndex, newIndex),
nameof(ItemCollectionViewModel));
}
}
return Task.CompletedTask;
}
}
+78
View File
@@ -0,0 +1,78 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template,
NamedComponent named,
Guid id,
string name = "",
string description = "",
string category = "",
bool selected = false,
bool favourite = false,
bool archived = false) :
Observable(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<ArchiveEventArgs<Item>>,
INotificationHandler<UnarchiveEventArgs<Item>>,
INotificationHandler<FavouriteEventArgs<Item>>,
INotificationHandler<UnfavouriteEventArgs<Item>>,
INotificationHandler<NotifyEventArgs<ItemHeader<string>>>,
IKeyed<Guid>,
ISelectable,
IRemovable
{
[ObservableProperty]
private bool archived = archived;
[ObservableProperty]
private string? category = category;
[ObservableProperty]
private string? description = description;
[ObservableProperty]
private bool favourite = favourite;
[ObservableProperty]
private Guid id = id;
[ObservableProperty]
private string? name = name;
[ObservableProperty]
private string named = $"{named}";
[ObservableProperty]
private bool selected = selected;
public IContentTemplate Template { get; set; } = template;
public Task Handle(ArchiveEventArgs<Item> args) =>
Task.Run(Dispose);
public Task Handle(UnarchiveEventArgs<Item> args) =>
Task.Run(Dispose);
public Task Handle(FavouriteEventArgs<Item> args) =>
Task.FromResult(Favourite = true);
public Task Handle(UnfavouriteEventArgs<Item> args) =>
Task.FromResult(Favourite = false);
public Task Handle(NotifyEventArgs<ItemHeader<string>> args)
{
if (args.Value is ItemHeader<string> header)
{
Name = header.Value;
}
return Task.CompletedTask;
}
}
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record ItemSection;
+6
View File
@@ -0,0 +1,6 @@
namespace Wallet;
public record ItemSectionConfiguration
{
public IList<ItemEntryConfiguration> Entries { get; set; } = new List<ItemEntryConfiguration>();
}
+19
View File
@@ -0,0 +1,19 @@
using Toolkit.Foundation;
namespace Wallet;
[Notification(typeof(CreateEventArgs<IItemEntryViewModel>), nameof(Id))]
public partial class ItemSectionViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template,
string id) : ObservableCollection<IItemEntryViewModel>(provider, factory, mediator, publisher, subscriber, disposer),
IKeyed<string>
{
public string Id => id;
public IContentTemplate Template { get; set; } = template;
}
+8
View File
@@ -0,0 +1,8 @@
namespace Wallet;
public enum ItemState
{
New,
Read,
Write
}
+138
View File
@@ -0,0 +1,138 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class ItemViewModel :
ObservableCollection,
INotificationHandler<ConfirmEventArgs<Item>>,
INotificationHandler<UpdateEventArgs<Item>>,
INotificationHandler<CancelEventArgs<Item>>
{
[ObservableProperty]
private bool archived;
[ObservableProperty]
private bool favourite;
[ObservableProperty]
private ItemState state;
[ObservableProperty]
private string named;
[ObservableProperty]
private string name;
[ObservableProperty]
private bool fromCategory;
public ItemViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template,
NamedComponent named,
ItemState state = ItemState.Read,
bool fromCategory = false,
string name = "",
bool favourite = false,
bool archived = false) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
Named = $"{named}";
State = state;
FromCategory = fromCategory;
Favourite = favourite;
Archived = archived;
Name = name;
Add<ItemHeaderViewModel>("", name, state);
Add<ItemContentViewModel>();
}
public IContentTemplate Template { get; set; }
public Task Handle(UpdateEventArgs<Item> args)
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<ConfirmItemActionViewModel>(),
Factory.Create<DismissItemActionViewModel>(),
})));
State = ItemState.Write;
return Task.CompletedTask;
}
public override void Dispose()
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new
List<IDisposable>())));
base.Dispose();
}
public Task Handle(CancelEventArgs<Item> args)
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<EditItemActionViewModel>(),
Factory.Create<ArchiveItemActionViewModel>(),
})));
State = ItemState.Read;
return Task.CompletedTask;
}
public Task Handle(ConfirmEventArgs<Item> args)
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<FavouriteItemActionViewModel>(Favourite),
Factory.Create<EditItemActionViewModel>(),
Factory.Create<ArchiveItemActionViewModel>(),
})));
Publisher.Publish(Confirm.As<Item>(),
State is ItemState.New ? nameof(ItemState.New) : nameof(ItemState.Write));
State = ItemState.Read;
return Task.CompletedTask;
}
public override Task OnActivated()
{
if (Archived)
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<UnarchiveItemActionViewModel>(),
})));
}
else
{
if (State is ItemState.Write or ItemState.New)
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<ConfirmItemActionViewModel>(),
Factory.Create<DismissItemActionViewModel>(),
})));
}
else
{
Publisher.Publish(Notify.As(Factory.Create<ItemCommandHeaderCollection>(new List<IDisposable>
{
Factory.Create<FavouriteItemActionViewModel>(Favourite),
Factory.Create<EditItemActionViewModel>(),
Factory.Create<ArchiveItemActionViewModel>(),
})));
}
}
return base.OnActivated();
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Wallet;
public class LengthRule :
IPasswordRule
{
public int CalculateScore(string password) =>
password.Length >= 8 ? 5 : 0;
}
+14
View File
@@ -0,0 +1,14 @@
using System.Text.RegularExpressions;
namespace Wallet;
public partial class LowercaseRule :
IPasswordRule
{
public int CalculateScore(string password) =>
Regex().IsMatch(password) ? 2 : 0;
[GeneratedRegex(@"[a-z]")]
private static partial Regex Regex();
}
+28
View File
@@ -0,0 +1,28 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Wallet;
[Notification(typeof(CreateEventArgs<IMainNavigationViewModel>), nameof(MainViewModel))]
public partial class MainViewModel :
ObservableCollection<IMainNavigationViewModel>
{
[ObservableProperty]
private FooterViewModel footer;
public MainViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template,
FooterViewModel footer) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
Footer = footer;
}
public IContentTemplate Template { get; set; }
}
+11
View File
@@ -0,0 +1,11 @@
using Toolkit.Foundation;
namespace Wallet;
public class MainWindowViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer);
+12
View File
@@ -0,0 +1,12 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class ManageNavigationViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer) :
Observable(provider, factory, mediator, publisher, subscriber, disposer),
IMainNavigationViewModel;
+23
View File
@@ -0,0 +1,23 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class ManageViewModel :
ObservableCollection,
IMainNavigationViewModel
{
public ManageViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
Add<CreateWalletNavigationViewModel>();
}
public IContentTemplate Template { get; set; }
}
+10
View File
@@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Wallet;
public record MaskedTextEntryConfiguration :
ItemEntryConfiguration
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Pattern { get; set; }
}
+21
View File
@@ -0,0 +1,21 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Toolkit.Foundation;
namespace Wallet;
public partial class MaskedTextEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
ItemEntryConfiguration configuration,
string pattern,
string key,
object value,
double width) : ItemEntryViewModel(provider, factory, mediator, publisher, subscriber, disposer, state, configuration, key, value, width)
{
[ObservableProperty]
private string pattern = pattern;
}
+26
View File
@@ -0,0 +1,26 @@
using Toolkit.Foundation;
namespace Wallet;
public class MaskedTextEntryViewModelHandler(IServiceFactory serviceFactory) :
IHandler<CreateEventArgs<MaskedTextEntryConfiguration>, IItemEntryViewModel?>
{
public Task<IItemEntryViewModel?> Handle(CreateEventArgs<MaskedTextEntryConfiguration> args,
CancellationToken cancellationToken)
{
if (args.Value is MaskedTextEntryConfiguration configuration)
{
string? label = configuration.Label;
object? value = configuration.Value ?? "";
double? width = configuration.Width;
if (serviceFactory.Create<MaskedTextEntryViewModel>([.. args.Parameters, configuration, configuration.Pattern, label, value, width])
is MaskedTextEntryViewModel viewModel)
{
return Task.FromResult<IItemEntryViewModel?>(viewModel);
}
}
return Task.FromResult<IItemEntryViewModel?>(default);
}
}
@@ -0,0 +1,7 @@
namespace Wallet;
public record MultilineTextEntryConfiguration :
ItemEntryConfiguration
{
}
+16
View File
@@ -0,0 +1,16 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class MultilineTextEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) : ItemEntryViewModel(provider, factory, mediator, publisher, subscriber, disposer, state, configuration, key, value, width);
@@ -0,0 +1,27 @@
using Toolkit.Foundation;
namespace Wallet;
public class MultilineTextEntryViewModelHandler(IServiceFactory serviceFactory) :
IHandler<CreateEventArgs<MultilineTextEntryConfiguration>, IItemEntryViewModel?>
{
public Task<IItemEntryViewModel?> Handle(CreateEventArgs<MultilineTextEntryConfiguration> args,
CancellationToken cancellationToken)
{
if (args.Value is MultilineTextEntryConfiguration configuration)
{
string? label = configuration.Label;
object? value = configuration.Value ?? "";
double? width = configuration.Width;
if (serviceFactory.Create<MultilineTextEntryViewModel>([.. args.Parameters, configuration, label, value, width])
is MultilineTextEntryViewModel viewModel)
{
return Task.FromResult<IItemEntryViewModel?>(viewModel);
}
}
return Task.FromResult<IItemEntryViewModel?>(default);
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Wallet;
public record NumberEntryConfiguration :
ItemEntryConfiguration
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
}
+36
View File
@@ -0,0 +1,36 @@
using System.Text;
using Toolkit.Foundation;
namespace Wallet;
public class OpenWalletHandler(IConfigurationDescriptor<WalletConfiguration> descriptor,
ISecurityKeyFactory securityKeyFactory,
IWalletStorageFactory WalletStorageFactory) :
IHandler<ActivateEventArgs<Wallet<string>>, bool>
{
public async Task<bool> Handle(ActivateEventArgs<Wallet<string>> args,
CancellationToken cancellationToken)
{
if (args.Value is Wallet<string> Wallet &&
descriptor.Name is { Length: > 0 } name &&
Wallet.Value is { Length: > 0 } password)
{
WalletConfiguration configuration = descriptor.Value;
if (configuration.Key?.Split(':') is { Length: >= 2 } keyPart)
{
byte[]? salt = Convert.FromBase64String(keyPart[0]);
byte[]? encryptedKey = Convert.FromBase64String(keyPart[1]);
if (securityKeyFactory.Create(Encoding.UTF8.GetBytes(password), encryptedKey, salt) is SecurityKey key)
{
if (await WalletStorageFactory.Create(name, key))
{
return true;
}
}
}
}
return false;
}
}
+33
View File
@@ -0,0 +1,33 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Wallet;
public partial class OpenWalletViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
string name) :
Observable(provider, factory, mediator, publisher, subscriber, disposer)
{
[ObservableProperty]
private string? name = name;
[ObservableProperty]
private string? password;
[RelayCommand]
private async Task Invoke()
{
if (Password is { Length: > 0 })
{
if (await Mediator.Handle<ActivateEventArgs<Wallet<string>>, bool>(Activate.As(new Wallet<string>(Password))))
{
Publisher.Publish(Opened.As<Wallet>());
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Wallet;
public record Opened
{
public static OpenedEventArgs<TValue> As<TValue>(TValue value) => new(value);
public static OpenedEventArgs<TValue> As<TValue>() where TValue : new() => new(new TValue());
}
+3
View File
@@ -0,0 +1,3 @@
namespace Wallet;
public record OpenedEventArgs<TValue>(TValue? Value = default);
+7
View File
@@ -0,0 +1,7 @@
namespace Wallet;
public record PasswordEntryConfiguration :
ItemEntryConfiguration
{
}
+15
View File
@@ -0,0 +1,15 @@
using Toolkit.Foundation;
namespace Wallet;
public partial class PasswordEntryViewModel(IServiceProvider provider,
IServiceFactory factory,
IMediator mediator,
IPublisher publisher,
ISubscription subscriber,
IDisposer disposer,
ItemState state,
ItemEntryConfiguration configuration,
string key,
object value,
double width) : ItemEntryViewModel(provider, factory, mediator, publisher, subscriber, disposer, state, configuration, key, value, width);
+26
View File
@@ -0,0 +1,26 @@
using Toolkit.Foundation;
namespace Wallet;
public class PasswordEntryViewModelHandler(IServiceFactory serviceFactory) :
IHandler<CreateEventArgs<PasswordEntryConfiguration>, IItemEntryViewModel?>
{
public Task<IItemEntryViewModel?> Handle(CreateEventArgs<PasswordEntryConfiguration> args,
CancellationToken cancellationToken)
{
if (args.Value is PasswordEntryConfiguration configuration)
{
string? label = configuration.Label;
object? value = configuration.Value ?? "";
double? width = configuration.Width;
if (serviceFactory.Create<PasswordEntryViewModel>([.. args.Parameters, configuration, label, value, width])
is PasswordEntryViewModel viewModel)
{
return Task.FromResult<IItemEntryViewModel?>(viewModel);
}
}
return Task.FromResult<IItemEntryViewModel?>(default);
}
}

Some files were not shown because too many files have changed in this diff Show More