diff --git a/Wallet.Avalonia/AllNavigationView.axaml b/Wallet.Avalonia/AllNavigationView.axaml index 6ead957..5ec18e3 100644 --- a/Wallet.Avalonia/AllNavigationView.axaml +++ b/Wallet.Avalonia/AllNavigationView.axaml @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -33,4 +33,7 @@ + + + diff --git a/Wallet.Avalonia/App.axaml.cs b/Wallet.Avalonia/App.axaml.cs index 4484820..4c6ce6f 100644 --- a/Wallet.Avalonia/App.axaml.cs +++ b/Wallet.Avalonia/App.axaml.cs @@ -108,6 +108,7 @@ public partial class App : Application services.AddHandler(); services.AddHandler(); services.AddHandler(); + services.AddHandler(); services.AddHandler(); @@ -184,6 +185,8 @@ public partial class App : Application services.AddHandler(); + services.AddHandler(ServiceLifetime.Singleton); + services.AddHandler(); services.AddHandler(); services.AddHandler(); diff --git a/Wallet.Avalonia/ArchiveNavigationView.axaml b/Wallet.Avalonia/ArchiveNavigationView.axaml index 549b3a1..6a0a757 100644 --- a/Wallet.Avalonia/ArchiveNavigationView.axaml +++ b/Wallet.Avalonia/ArchiveNavigationView.axaml @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -33,4 +33,7 @@ + + + diff --git a/Wallet.Avalonia/CategoryNavigationView.axaml b/Wallet.Avalonia/CategoryNavigationView.axaml index 4865dfd..565f3e6 100644 --- a/Wallet.Avalonia/CategoryNavigationView.axaml +++ b/Wallet.Avalonia/CategoryNavigationView.axaml @@ -4,7 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:Wallet" x:DataType="vm:CategoryNavigationViewModel" - Content="{Binding Filter}" + Content="{Binding Value}" IsSelected="{Binding Selected}"> @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + diff --git a/Wallet.Avalonia/StarredNavigationView.axaml b/Wallet.Avalonia/StarredNavigationView.axaml index 7365659..ce76d48 100644 --- a/Wallet.Avalonia/StarredNavigationView.axaml +++ b/Wallet.Avalonia/StarredNavigationView.axaml @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -33,5 +33,8 @@ + + + diff --git a/Wallet/AllNavigationViewModel.cs b/Wallet/AllNavigationViewModel.cs index bdf6485..7427f5c 100644 --- a/Wallet/AllNavigationViewModel.cs +++ b/Wallet/AllNavigationViewModel.cs @@ -2,10 +2,20 @@ namespace Wallet; +[Notification(typeof(NotifyEventArgs>), "All")] public partial class AllNavigationViewModel(IServiceProvider provider, - IServiceFactory factory, - IMediator mediator, - IPublisher publisher, - ISubscription subscriber, - IDisposer disposer, - string filter) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, filter); \ No newline at end of file + IServiceFactory factory, IMediator mediator, + IPublisher publisher, ISubscription subscriber, IDisposer disposer, int key, string value) : + FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, key, value), + INotificationHandler>> +{ + public Task Handle(NotifyEventArgs> args) + { + if (args.Sender is Item item) + { + Key = item.Value; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Wallet/ArchiveEventArgs.cs b/Wallet/ArchiveEventArgs.cs index a996d3c..10f93b5 100644 --- a/Wallet/ArchiveEventArgs.cs +++ b/Wallet/ArchiveEventArgs.cs @@ -1,3 +1,3 @@ namespace Wallet; -public record ArchiveEventArgs(TValue Value); \ No newline at end of file +public record ArchiveEventArgs(TSender Sender); \ No newline at end of file diff --git a/Wallet/ArchiveItemHandler.cs b/Wallet/ArchiveItemHandler.cs index 23fea10..7e6a4ca 100644 --- a/Wallet/ArchiveItemHandler.cs +++ b/Wallet/ArchiveItemHandler.cs @@ -4,7 +4,8 @@ namespace Wallet; public class ArchiveItemHandler(IDecoratorService> decoratorService, ICache> cache, - IMediator mediator) : + IMediator mediator, + IPublisher publisher) : INotificationHandler> { public async Task Handle(ArchiveEventArgs args) @@ -21,6 +22,7 @@ public class ArchiveItemHandler(IDecoratorService> decorato bool>(new UpdateEventArgs<(Guid, int)>((id, 2))); cache.Remove(item); + publisher.Publish(Changed.As(item)); } } } diff --git a/Wallet/ArchiveNavigationViewModel.cs b/Wallet/ArchiveNavigationViewModel.cs index c97f641..aaf5545 100644 --- a/Wallet/ArchiveNavigationViewModel.cs +++ b/Wallet/ArchiveNavigationViewModel.cs @@ -2,10 +2,24 @@ namespace Wallet; +[Notification(typeof(NotifyEventArgs>), "Archive")] public partial class ArchiveNavigationViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - string name) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, name); \ No newline at end of file + int key, + string value) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, key, value), + INotificationHandler>> +{ + public Task Handle(NotifyEventArgs> args) + { + if (args.Sender is Item item) + { + Key = item.Value; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Wallet/CategoriesNavigationViewModel.cs b/Wallet/CategoriesNavigationViewModel.cs index 7b772d3..310964d 100644 --- a/Wallet/CategoriesNavigationViewModel.cs +++ b/Wallet/CategoriesNavigationViewModel.cs @@ -9,7 +9,5 @@ public partial class CategoriesNavigationViewModel(IServiceProvider provider, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - string name) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, name) -{ - -} \ No newline at end of file + int key, + string value) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, key, value); \ No newline at end of file diff --git a/Wallet/CategoryNavigationViewModel.cs b/Wallet/CategoryNavigationViewModel.cs index 479aec3..6159ac5 100644 --- a/Wallet/CategoryNavigationViewModel.cs +++ b/Wallet/CategoryNavigationViewModel.cs @@ -8,4 +8,4 @@ public partial class CategoryNavigationViewModel(IServiceProvider provider, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - string filter) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, filter); \ No newline at end of file + string value) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, 0, value); \ No newline at end of file diff --git a/Wallet/ConfirmCreateItemHandler.cs b/Wallet/ConfirmCreateItemHandler.cs index a987ed7..bf2e035 100644 --- a/Wallet/ConfirmCreateItemHandler.cs +++ b/Wallet/ConfirmCreateItemHandler.cs @@ -13,15 +13,19 @@ public class ConfirmCreateItemHandler(IMediator mediator, if (itemHeaderConfiguration.Service is ItemHeaderConfiguration headerConfiguration && itemConfigurationDecorator.Service is ItemConfiguration itemConfiguration) { - string? name = headerConfiguration?.Name; - if (name is not null) + if (headerConfiguration.Name is { Length: > 0 } name && + headerConfiguration.Category is { Length: > 0 } category) { Guid id = Guid.NewGuid(); - publisher.Publish(Created.As(new Item<(Guid, string)>((id, name)))); + + Item<(Guid, string)> item = new((id, name)); + publisher.Publish(Created.As(item)); await mediator.Handle, bool>(new CreateEventArgs<(Guid, string, string, - ItemConfiguration)>((id, name, "", itemConfiguration))); + ItemConfiguration)>((id, name, category, itemConfiguration))); + + publisher.Publish(Changed.As(item)); } } } diff --git a/Wallet/ConfirmUpdateItemHandler.cs b/Wallet/ConfirmUpdateItemHandler.cs index 1791a2b..e834f3a 100644 --- a/Wallet/ConfirmUpdateItemHandler.cs +++ b/Wallet/ConfirmUpdateItemHandler.cs @@ -29,6 +29,8 @@ public class ConfirmUpdateItemHandler(IDecoratorService> it await mediator.Handle>, bool>(new UpdateEventArgs>(new Item<(Guid, string, ItemConfiguration)>((id, name, itemConfiguration)))); + + publisher.Publish(Changed.As(item)); } } } diff --git a/Wallet/CountCategoriesHandler.cs b/Wallet/CountCategoriesHandler.cs new file mode 100644 index 0000000..46dfa59 --- /dev/null +++ b/Wallet/CountCategoriesHandler.cs @@ -0,0 +1,52 @@ +using Microsoft.EntityFrameworkCore; +using System.Linq; +using Toolkit.Foundation; +using Wallet.Data; + +namespace Wallet; + +public class CountCategoriesHandler(IDbContextFactory dbContextFactory) : + IHandler, IReadOnlyCollection<(string, int)>> +{ + public async Task> Handle(CountEventArgs args, + CancellationToken cancellationToken) + { + using WalletContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken); + + var stateCounts = await context.Items + .GroupBy(i => i.State) + .Select(g => new + { + g.Key, + Count = g.Count() + }) + .ToListAsync(cancellationToken: cancellationToken); + + var categoryCounts = await context.Items.Where(x => !string.IsNullOrEmpty(x.Category)) + .GroupBy(i => i.Category) + .Select(g => new + { + g.Key, + Count = g.Count() + }) + .ToListAsync(cancellationToken: cancellationToken); + + int allCount = stateCounts.Where(x => x.Key != 2).Sum(x => x.Count); + int favouritesCount = stateCounts.Where(x => x.Key == 1).Sum(x => x.Count); + int archiveCount = stateCounts.Where(x => x.Key == 2).Sum(x => x.Count); + + List<(string, int)> combinedCounts = + [ + new("Favourites", favouritesCount), + new("Archive", archiveCount), + new("All", allCount) + ]; + + foreach ((string key, int count) in categoryCounts.Select(x => (x.Key, x.Count))) + { + combinedCounts.Add((key, count)); + } + + return combinedCounts; + } +} diff --git a/Wallet/FavouriteEventArgs.cs b/Wallet/FavouriteEventArgs.cs index c929ddb..0ce6902 100644 --- a/Wallet/FavouriteEventArgs.cs +++ b/Wallet/FavouriteEventArgs.cs @@ -1,3 +1,3 @@ namespace Wallet; -public record FavouriteEventArgs(TValue Value); \ No newline at end of file +public record FavouriteEventArgs(TSender Sender); \ No newline at end of file diff --git a/Wallet/FavouriteItemHandler.cs b/Wallet/FavouriteItemHandler.cs index 63f62ac..6723c91 100644 --- a/Wallet/FavouriteItemHandler.cs +++ b/Wallet/FavouriteItemHandler.cs @@ -3,7 +3,8 @@ namespace Wallet; public class FavouriteItemHandler(IDecoratorService> decoratorService, - IMediator mediator) : + IMediator mediator, + IPublisher publisher) : INotificationHandler> { public async Task Handle(FavouriteEventArgs args) @@ -14,6 +15,8 @@ public class FavouriteItemHandler(IDecoratorService> decora { (Guid id, string name) = item.Value; await mediator.Handle, bool>(new UpdateEventArgs<(Guid, int)>((id, 1))); + + publisher.Publish(Changed.As(item)); } } catch diff --git a/Wallet/FilterNavigationViewModel.cs b/Wallet/FilterNavigationViewModel.cs index 29e00a3..ec08dd8 100644 --- a/Wallet/FilterNavigationViewModel.cs +++ b/Wallet/FilterNavigationViewModel.cs @@ -4,7 +4,7 @@ using Toolkit.Foundation; namespace Wallet; public partial class FilterNavigationViewModel : - ObservableCollection, + ObservableCollection, IWalletNavigationViewModel, INotificationHandler>, INotificationHandler> @@ -12,21 +12,19 @@ public partial class FilterNavigationViewModel : [ObservableProperty] private bool activated; - [ObservableProperty] - private string? filter; - [ObservableProperty] private bool selected; - public FilterNavigationViewModel(IServiceProvider provider, - IServiceFactory factory, - IMediator mediator, + public FilterNavigationViewModel(IServiceProvider provider, + IServiceFactory factory, + IMediator mediator, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - string? filter = null) : base(provider, factory, mediator, publisher, subscriber, disposer) + int key, + string value) : base(provider, factory, mediator, publisher, subscriber, disposer, key, value) { - Filter = filter; + } public Task Handle(DeactivatedEventArgs args) => @@ -37,7 +35,7 @@ public partial class FilterNavigationViewModel : } public partial class FilterNavigationViewModel : - ObservableCollection, + ObservableCollection, IWalletNavigationViewModel, INotificationHandler>, INotificationHandler> @@ -47,21 +45,19 @@ public partial class FilterNavigationViewModel : [ObservableProperty] private bool activated; - [ObservableProperty] - private string? filter; - [ObservableProperty] private bool selected; public FilterNavigationViewModel(IServiceProvider provider, IServiceFactory factory, - IMediator mediator, + IMediator mediator, IPublisher publisher, - ISubscription subscriber, + ISubscription subscriber, IDisposer disposer, - string? filter = null) : base(provider, factory, mediator, publisher, subscriber, disposer) + int key, + string value) : base(provider, factory, mediator, publisher, subscriber, disposer, key, value) { - Filter = filter; + } public Task Handle(DeactivatedEventArgs args) => diff --git a/Wallet/ItemChangedHandler.cs b/Wallet/ItemChangedHandler.cs new file mode 100644 index 0000000..213093c --- /dev/null +++ b/Wallet/ItemChangedHandler.cs @@ -0,0 +1,22 @@ +using Toolkit.Foundation; + +namespace Wallet; + +public class ItemChangedHandler(IMediator mediator, + IPublisher publisher) : + INotificationHandler>> +{ + public async Task Handle(ChangedEventArgs> args) + { + IReadOnlyCollection<(string, int)>? categoryCounts = await mediator.Handle, + IReadOnlyCollection<(string, int)>>(Count.As()); + + if (categoryCounts is { Count: > 0 } ) + { + foreach ((string key, int count) in categoryCounts) + { + publisher.Publish(Notify.As(new Item(count)), key); + } + } + } +} diff --git a/Wallet/ItemHeaderConfiguration.cs b/Wallet/ItemHeaderConfiguration.cs index 917e20b..65e8e00 100644 --- a/Wallet/ItemHeaderConfiguration.cs +++ b/Wallet/ItemHeaderConfiguration.cs @@ -3,4 +3,6 @@ public class ItemHeaderConfiguration { public string? Name { get; set; } + + public string? Category { get; set; } } \ No newline at end of file diff --git a/Wallet/ItemHeaderViewModel.cs b/Wallet/ItemHeaderViewModel.cs index d03ff5e..45b1f39 100644 --- a/Wallet/ItemHeaderViewModel.cs +++ b/Wallet/ItemHeaderViewModel.cs @@ -70,6 +70,7 @@ public partial class ItemHeaderViewModel : if (args.Sender is ItemCategory category) { Category = category.Value; + configuration.Category = category.Value; } return Task.CompletedTask; diff --git a/Wallet/QueryWalletHandler.cs b/Wallet/QueryWalletHandler.cs index 24fcc65..a196179 100644 --- a/Wallet/QueryWalletHandler.cs +++ b/Wallet/QueryWalletHandler.cs @@ -12,7 +12,7 @@ public class QueryWalletHandler(IDbContextFactory dbContextFactor Handle(QueryEventArgs> args,CancellationToken cancellationToken) { List<(Guid Id, string? Name, string Category, bool Favourite, bool Archived)> items = []; - if (args.Value is Wallet<(string, string)> Wallet) + if (args.Sender is Wallet<(string, string)> Wallet) { (string filter, string text) = Wallet.Sender; diff --git a/Wallet/StarredNavigationViewModel.cs b/Wallet/StarredNavigationViewModel.cs index c342159..4df0c65 100644 --- a/Wallet/StarredNavigationViewModel.cs +++ b/Wallet/StarredNavigationViewModel.cs @@ -2,10 +2,24 @@ namespace Wallet; +[Notification(typeof(NotifyEventArgs>), "Favourites")] public partial class StarredNavigationViewModel(IServiceProvider provider, IServiceFactory factory, IMediator mediator, IPublisher publisher, ISubscription subscriber, IDisposer disposer, - string name) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, name); \ No newline at end of file + int key, + string value) : FilterNavigationViewModel(provider, factory, mediator, publisher, subscriber, disposer, key, value), + INotificationHandler>> +{ + public Task Handle(NotifyEventArgs> args) + { + if (args.Sender is Item item) + { + Key = item.Value; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Wallet/UnarchiveEventArgs.cs b/Wallet/UnarchiveEventArgs.cs index 428c457..f37c0b6 100644 --- a/Wallet/UnarchiveEventArgs.cs +++ b/Wallet/UnarchiveEventArgs.cs @@ -1,3 +1,3 @@ namespace Wallet; -public record UnarchiveEventArgs(TValue Value); \ No newline at end of file +public record UnarchiveEventArgs(TSender Sender); \ No newline at end of file diff --git a/Wallet/UnarchiveItemHandler.cs b/Wallet/UnarchiveItemHandler.cs index 239f585..f0febe2 100644 --- a/Wallet/UnarchiveItemHandler.cs +++ b/Wallet/UnarchiveItemHandler.cs @@ -1,12 +1,11 @@ -using Wallet.Data; -using Microsoft.EntityFrameworkCore; -using System.Threading; -using Toolkit.Foundation; +using Toolkit.Foundation; namespace Wallet; public class UnarchiveItemHandler(IDecoratorService> decoratorService, - IDbContextFactory dbContextFactory) : + ICache> cache, + IMediator mediator, + IPublisher publisher) : INotificationHandler> { public async Task Handle(UnarchiveEventArgs args) @@ -17,12 +16,11 @@ public class UnarchiveItemHandler(IDecoratorService> decora { (Guid id, string name) = item.Value; - using WalletContext context = await dbContextFactory.CreateDbContextAsync(); - if (await context.FindAsync(id) is ItemEntry result) - { - result.State = 0; - await context.SaveChangesAsync(); - } + await mediator.Handle, + bool>(new UpdateEventArgs<(Guid, int)>((id, 0))); + + cache.Add(item); + publisher.Publish(Changed.As(item)); } } catch diff --git a/Wallet/UnfavouriteEventArgs.cs b/Wallet/UnfavouriteEventArgs.cs index 2726426..8d38c35 100644 --- a/Wallet/UnfavouriteEventArgs.cs +++ b/Wallet/UnfavouriteEventArgs.cs @@ -1,3 +1,3 @@ namespace Wallet; -public record UnfavouriteEventArgs(TValue Value); \ No newline at end of file +public record UnfavouriteEventArgs(TSender Sender); \ No newline at end of file diff --git a/Wallet/UnfavouriteItemHandler.cs b/Wallet/UnfavouriteItemHandler.cs index 70e309e..876d417 100644 --- a/Wallet/UnfavouriteItemHandler.cs +++ b/Wallet/UnfavouriteItemHandler.cs @@ -1,8 +1,10 @@ using Toolkit.Foundation; namespace Wallet; + public class UnfavouriteItemHandler(IDecoratorService> decoratorService, - IMediator mediator) : + IMediator mediator, + IPublisher publisher) : INotificationHandler> { public async Task Handle(UnfavouriteEventArgs args) @@ -13,6 +15,8 @@ public class UnfavouriteItemHandler(IDecoratorService> deco { (Guid id, string name) = item.Value; await mediator.Handle, bool>(new UpdateEventArgs<(Guid, int)>((id, 0))); + + publisher.Publish(Changed.As(item)); } } catch diff --git a/Wallet/UpdateItemStateHandler.cs b/Wallet/UpdateItemStateHandler.cs index 5cf35ab..9ef4641 100644 --- a/Wallet/UpdateItemStateHandler.cs +++ b/Wallet/UpdateItemStateHandler.cs @@ -12,15 +12,12 @@ public class UpdateItemStateHandler(IDbContextFactory dbContextFa { if (args.Value is (Guid id, int state)) { - await Task.Run(async () => + using WalletContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken); + if (await context.FindAsync(id) is ItemEntry result) { - using WalletContext context = await dbContextFactory.CreateDbContextAsync(cancellationToken); - if (await context.FindAsync(id) is ItemEntry result) - { - result.State = state; - await context.SaveChangesAsync(); - } - }, cancellationToken); + result.State = state; + await context.SaveChangesAsync(cancellationToken); + } } return false; diff --git a/Wallet/WalletNavigationViewModel.cs b/Wallet/WalletNavigationViewModel.cs index 8b5d2f0..9e5e64e 100644 --- a/Wallet/WalletNavigationViewModel.cs +++ b/Wallet/WalletNavigationViewModel.cs @@ -46,10 +46,10 @@ public partial class WalletNavigationViewModel : public Task Handle(OpenedEventArgs args) { - Add("All"); - Add("Starred"); - Add("Archive"); - Add("Categories"); + Add("All", 0); + Add("Starred", 0); + Add("Archive", 0); + Add("Categories", 0); Opened = true; return Task.CompletedTask;