Get favourites working

This commit is contained in:
TheXamlGuy
2024-05-19 17:52:15 +01:00
parent 68cdbc5519
commit 5ded5897c1
22 changed files with 232 additions and 30 deletions
+1 -2
View File
@@ -2,8 +2,7 @@
x:Class="Bitvault.Avalonia.App"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
RequestedThemeVariant="Default">
xmlns:ui="using:FluentAvalonia.UI.Controls">
<Application.Styles>
<ThemeResources PreferSystemTheme="True" PreferUserAccentColor="True" />
<Style Selector="ui|SettingsExpanderItem">
+3 -1
View File
@@ -94,6 +94,7 @@ public partial class App : Application
services.AddTemplate<ItemCommandHeaderViewModel, ItemCommandHeaderView>("ItemCommandHeader");
services.AddTemplate<FavouriteItemActionViewModel, FavouriteItemActionView>();
services.AddTemplate<ConfirmItemActionViewModel, ConfirmItemActionView>();
services.AddTemplate<DismissItemActionViewModel, DismissItemActionView>();
services.AddTemplate<ArchiveItemActionViewModel, ArchiveItemActionView>();
@@ -108,7 +109,8 @@ public partial class App : Application
services.AddHandler<ConfirmItemHandler>(ServiceLifetime.Scoped);
services.AddHandler<ArchiveItemHandler>(ServiceLifetime.Scoped);
services.AddHandler<UnarchiveItemHandler>(ServiceLifetime.Scoped);
services.AddHandler<FavouriteItemHandler>(ServiceLifetime.Scoped);
services.AddHandler<UnfavouriteItemHandler>(ServiceLifetime.Scoped);
});
})!);
@@ -17,6 +17,6 @@
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="16"
Foreground="{DynamicResource IconForegroundBrush}"
Text="&#xE066;" />
Text="&#xE073;" />
</Button>
</UserControl>
@@ -14,8 +14,8 @@
<TextBlock
VerticalAlignment="Center"
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="18"
FontSize="16"
Foreground="{DynamicResource IconForegroundBrush}"
Text="&#xF295;" />
Text="&#xE3EA;" />
</Button>
</UserControl>
@@ -16,6 +16,6 @@
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="16"
Foreground="{DynamicResource IconForegroundBrush}"
Text="&#xF36A;" />
Text="&#xE607;" />
</Button>
</UserControl>
+1 -1
View File
@@ -16,6 +16,6 @@
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="16"
Foreground="{DynamicResource IconForegroundBrush}"
Text="&#xE5B3;" />
Text="&#xE759;" />
</Button>
</UserControl>
@@ -0,0 +1,30 @@
<UserControl
x:Class="Bitvault.Avalonia.FavouriteItemActionView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Bitvault"
x:DataType="vm:FavouriteItemActionViewModel">
<Button
Width="{StaticResource ButtonWidth}"
Height="{StaticResource ButtonHeight}"
VerticalAlignment="Center"
Command="{Binding InvokeCommand}"
ToolTip.Tip="Edit">
<Grid>
<TextBlock
VerticalAlignment="Center"
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="16"
Foreground="{DynamicResource IconForegroundBrush}"
IsVisible="{Binding !Value}"
Text="&#xEF61;" />
<TextBlock
VerticalAlignment="Center"
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="16"
Foreground="{DynamicResource IconForegroundBrush}"
IsVisible="{Binding Value}"
Text="&#xEF60;" />
</Grid>
</Button>
</UserControl>
@@ -0,0 +1,8 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class FavouriteItemActionView : UserControl
{
public FavouriteItemActionView() => InitializeComponent();
}
+14 -1
View File
@@ -5,6 +5,9 @@
xmlns:vm="using:Bitvault"
x:DataType="vm:ItemNavigationViewModel"
IsSelected="{Binding Selected}">
<ListBoxItem.Resources>
<SolidColorBrush x:Key="StarredIconForegroundBrush" Color="#FFEDB120" />
</ListBoxItem.Resources>
<Interaction.Behaviors>
<DataTriggerBehavior Binding="{Binding Selected}" Value="True">
<NavigateAction
@@ -17,6 +20,7 @@
Scope="self">
<NavigateAction.ParameterBindings>
<ParameterBinding Key="Archived" Value="{Binding Archived}" />
<ParameterBinding Key="Favourite" Value="{Binding Favourite}" />
</NavigateAction.ParameterBindings>
</NavigateAction>
</DataTriggerBehavior>
@@ -25,8 +29,17 @@
<PersonPicture
Grid.Column="0"
Height="34"
Margin="0"
DisplayName="{Binding Name}" />
<TextBlock
Grid.Column="0"
Margin="0,0,-2,10"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
FontFamily="{DynamicResource FluentThemeFontFamily}"
FontSize="16"
Foreground="{DynamicResource StarredIconForegroundBrush}"
IsVisible="{Binding Favourite}"
Text="&#xEF60;" />
<StackPanel Grid.Column="1" Margin="12,12,6,12">
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
<TextBlock Opacity="0.7" Text="{Binding Name}" />
@@ -18,19 +18,19 @@ public class AggerateContainerViewModelHandler(IMediator mediator,
bool selected = true;
if (await mediator.Handle<RequestEventArgs<QueryContainerConfiguration>,
IReadOnlyCollection<(int Id, string? Name)>>(Request.As(new QueryContainerConfiguration
IReadOnlyCollection<(int Id, string? Name, bool Favourite, bool Archived)>>(Request.As(new QueryContainerConfiguration
{
Filter = configuration.Filter,
Query = configuration.Query
})) is IReadOnlyCollection<(int Id, string? Name)> results)
})) is IReadOnlyCollection<(int Id, string? Name, bool Favourite, bool Archived)> results)
{
foreach ((int Id, string? Name) in results)
foreach ((int Id, string? Name, bool Favourite, bool Archived) in results)
{
IServiceScope serviceScope = serviceProvider.CreateScope();
IServiceFactory serviceFactory = serviceScope.ServiceProvider.GetRequiredService<IServiceFactory>();
IValueStore<Item> valueStore = serviceScope.ServiceProvider.GetRequiredService<IValueStore<Item>>();
if (serviceFactory.Create<ItemNavigationViewModel>(Id, Name, "Description", selected, configuration.Filter == "Archive") is ItemNavigationViewModel viewModel)
if (serviceFactory.Create<ItemNavigationViewModel>(Id, Name, "Description", selected, Favourite, Archived) is ItemNavigationViewModel viewModel)
{
Item item = new() { Id = Id, Name = Name };
valueStore.Set(item);
+1 -2
View File
@@ -13,5 +13,4 @@ public partial class ArchiveItemActionViewModel(IServiceProvider provider,
{
[RelayCommand]
public void Invoke() => Publisher.Publish(Archive.As<Item>());
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Bitvault;
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 Bitvault;
public record FavouriteEventArgs<TValue>(TValue Value);
+30
View File
@@ -0,0 +1,30 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Bitvault;
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>());
}
}
}
+34
View File
@@ -0,0 +1,34 @@
using Bitvault.Data;
using Microsoft.EntityFrameworkCore;
using Toolkit.Foundation;
namespace Bitvault;
public class FavouriteItemHandler(IValueStore<Item> valueStore,
IDbContextFactory<ContainerDbContext> dbContextFactory) :
INotificationHandler<FavouriteEventArgs<Item>>
{
public async Task Handle(FavouriteEventArgs<Item> args)
{
try
{
if (valueStore.Value is Item item)
{
await Task.Run(async () =>
{
using ContainerDbContext context = await dbContextFactory.CreateDbContextAsync();
if (await context.FindAsync<ItemEntry>(item.Id) is ItemEntry result)
{
result.State = 1;
await context.SaveChangesAsync();
}
});
}
}
catch
{
}
}
}
+16 -10
View File
@@ -15,10 +15,13 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
string name,
string description,
bool selected,
bool favourite,
bool archived) :
Observable(provider, factory, mediator, publisher, subscriber, disposer),
INotificationHandler<ArchiveEventArgs<Item>>,
INotificationHandler<UnarchiveEventArgs<Item>>,
INotificationHandler<FavouriteEventArgs<Item>>,
INotificationHandler<UnfavouriteEventArgs<Item>>,
ISelectable,
IRemovable
{
@@ -28,6 +31,9 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
[ObservableProperty]
private string? description = description;
[ObservableProperty]
private bool favourite = favourite;
[ObservableProperty]
private int id = id;
@@ -42,15 +48,15 @@ public partial class ItemNavigationViewModel(IServiceProvider provider,
public IContentTemplate Template { get; set; } = template;
public Task Handle(ArchiveEventArgs<Item> args)
{
Dispose();
return Task.CompletedTask;
}
public Task Handle(ArchiveEventArgs<Item> args) =>
Task.Run(Dispose);
public Task Handle(UnarchiveEventArgs<Item> args)
{
Dispose();
return Task.CompletedTask;
}
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);
}
+2
View File
@@ -18,6 +18,7 @@ public partial class ItemViewModel :
ISubscription subscriber,
IDisposer disposer,
IContentTemplate template,
bool favourite = false,
bool archived = false) : base(provider, factory, mediator, publisher, subscriber, disposer)
{
Template = template;
@@ -27,6 +28,7 @@ public partial class ItemViewModel :
{
Publisher.Publish(Notify.As(Factory.Create<CommandCollection>(new List<IDisposable>
{
Factory.Create<FavouriteItemActionViewModel>(favourite),
Factory.Create<EditItemActionViewModel>(),
Factory.Create<ArchiveItemActionViewModel>(),
})));
+7 -5
View File
@@ -6,12 +6,12 @@ using Toolkit.Foundation;
namespace Bitvault;
public class QueryContainerHandler(IDbContextFactory<ContainerDbContext> dbContextFactory) :
IHandler<RequestEventArgs<QueryContainerConfiguration>, IReadOnlyCollection<(int Id, string? Name)>>
IHandler<RequestEventArgs<QueryContainerConfiguration>, IReadOnlyCollection<(int Id, string? Name, bool Favourite, bool Archived)>>
{
public async Task<IReadOnlyCollection<(int Id, string? Name)>> Handle(RequestEventArgs<QueryContainerConfiguration> args,
public async Task<IReadOnlyCollection<(int Id, string? Name, bool Favourite, bool Archived)>> Handle(RequestEventArgs<QueryContainerConfiguration> args,
CancellationToken cancellationToken)
{
List<(int Id, string? Name)> items = [];
List<(int Id, string? Name, bool Favourite, bool Archived)> items = [];
if (args.Value is QueryContainerConfiguration queryConfiguration)
{
@@ -44,13 +44,15 @@ public class QueryContainerHandler(IDbContextFactory<ContainerDbContext> dbConte
return await context.Set<ItemEntry>().Where(predicate).Select(x => new
{
x.Id,
x.Name
x.Name,
Favourite = x.State == 1,
Archived = x.State == 2
}).OrderBy(x => x.Name).ToListAsync();
});
foreach (var result in results)
{
items.Add(new(result.Id, result.Name));
items.Add(new(result.Id, result.Name, result.Favourite, result.Archived));
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Bitvault;
public record Unfavourite
{
public static UnfavouriteEventArgs<TValue> As<TValue>(TValue value) =>
new(value);
public static UnfavouriteEventArgs<TValue> As<TValue>() where TValue : new() =>
new(new TValue());
}
+3
View File
@@ -0,0 +1,3 @@
namespace Bitvault;
public record UnfavouriteEventArgs<TValue>(TValue Value);
@@ -0,0 +1,17 @@
using CommunityToolkit.Mvvm.Input;
using Toolkit.Foundation;
namespace Bitvault;
public partial class UnfavouriteItemActionViewModel(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(Unfavourite.As<Item>());
}
+34
View File
@@ -0,0 +1,34 @@
using Bitvault.Data;
using Microsoft.EntityFrameworkCore;
using Toolkit.Foundation;
namespace Bitvault;
public class UnfavouriteItemHandler(IValueStore<Item> valueStore,
IDbContextFactory<ContainerDbContext> dbContextFactory) :
INotificationHandler<UnfavouriteEventArgs<Item>>
{
public async Task Handle(UnfavouriteEventArgs<Item> args)
{
try
{
if (valueStore.Value is Item item)
{
await Task.Run(async () =>
{
using ContainerDbContext context = await dbContextFactory.CreateDbContextAsync();
if (await context.FindAsync<ItemEntry>(item.Id) is ItemEntry result)
{
result.State = 0;
await context.SaveChangesAsync();
}
});
}
}
catch
{
}
}
}