Removed Breadcrumb service

Breadcrumb control handles breadcrumb status
Layout improvements
Added the ability to delete an entry from the group menu
This commit is contained in:
Geoffroy BONNEVILLE
2020-06-10 13:38:04 +02:00
parent c62ed584dc
commit 7dcd5a4a57
15 changed files with 137 additions and 173 deletions

View File

@@ -86,7 +86,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\Behaviors\DirtyStatusBehavior.cs" /> <Compile Include="Common\Behaviors\DirtyStatusBehavior.cs" />
<Compile Include="Common\Interfaces\IBreadcrumbService.cs" />
<Compile Include="Common\Interfaces\ICryptographyClient.cs" /> <Compile Include="Common\Interfaces\ICryptographyClient.cs" />
<Compile Include="Common\Interfaces\IDatabaseSettingsProxy.cs" /> <Compile Include="Common\Interfaces\IDatabaseSettingsProxy.cs" />
<Compile Include="Common\Interfaces\IDatabaseProxy.cs" /> <Compile Include="Common\Interfaces\IDatabaseProxy.cs" />
@@ -101,7 +100,6 @@
<Compile Include="Common\Mappings\IMapFrom.cs" /> <Compile Include="Common\Mappings\IMapFrom.cs" />
<Compile Include="Common\Mappings\MappingProfile.cs" /> <Compile Include="Common\Mappings\MappingProfile.cs" />
<Compile Include="Common\Models\BreadcrumbItem.cs" /> <Compile Include="Common\Models\BreadcrumbItem.cs" />
<Compile Include="Common\Services\BreadcrumbService.cs" />
<Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" /> <Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" />
<Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" /> <Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" />
<Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" /> <Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" />

View File

@@ -1,12 +0,0 @@
using System.Collections.Generic;
using ModernKeePass.Application.Common.Models;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IBreadcrumbService
{
void Push(BreadcrumbItem item);
BreadcrumbItem Pop(int count = 1);
IEnumerable<BreadcrumbItem> GetItems();
}
}

View File

@@ -1,37 +0,0 @@
using System.Collections.Generic;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Models;
namespace ModernKeePass.Application.Common.Services
{
public class BreadcrumbService: IBreadcrumbService
{
private readonly Stack<BreadcrumbItem> _breadcrumb;
public BreadcrumbService()
{
_breadcrumb = new Stack<BreadcrumbItem>();
}
public void Push(BreadcrumbItem item)
{
_breadcrumb.Push(item);
}
public BreadcrumbItem Pop(int count = 1)
{
if (_breadcrumb.Count == 0) return null;
for (var i = 1; i < count; i++)
{
_breadcrumb.Pop();
}
return _breadcrumb.Pop();
}
public IEnumerable<BreadcrumbItem> GetItems()
{
return _breadcrumb;
}
}
}

View File

@@ -2,8 +2,6 @@
using MediatR; using MediatR;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using ModernKeePass.Application.Common.Behaviors; using ModernKeePass.Application.Common.Behaviors;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Services;
namespace ModernKeePass.Application namespace ModernKeePass.Application
{ {
@@ -15,7 +13,6 @@ namespace ModernKeePass.Application
services.AddMediatR(assembly); services.AddMediatR(assembly);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>));
services.AddSingleton(typeof(IBreadcrumbService), typeof(BreadcrumbService));
return services; return services;
} }
} }

View File

@@ -44,12 +44,14 @@ namespace ModernKeePass.ViewModels
public string Id => _current.Id; public string Id => _current.Id;
public GroupVm Parent { get; private set; }
public bool IsRecycleOnDelete public bool IsRecycleOnDelete
{ {
get get
{ {
var database = Database; var database = Database;
return database.IsRecycleBinEnabled && _parent.Id != database.RecycleBinId; return database.IsRecycleBinEnabled && Parent.Id != database.RecycleBinId;
} }
} }
@@ -224,7 +226,6 @@ namespace ModernKeePass.ViewModels
private readonly IFileProxy _file; private readonly IFileProxy _file;
private readonly ICryptographyClient _cryptography; private readonly ICryptographyClient _cryptography;
private readonly IDateTime _dateTime; private readonly IDateTime _dateTime;
private GroupVm _parent;
private EntryVm _current; private EntryVm _current;
private int _selectedIndex; private int _selectedIndex;
private int _additionalFieldSelectedIndex = -1; private int _additionalFieldSelectedIndex = -1;
@@ -242,7 +243,7 @@ namespace ModernKeePass.ViewModels
_dateTime = dateTime; _dateTime = dateTime;
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);
MoveCommand = new RelayCommand<string>(async destination => await Move(destination), destination => _parent != null && !string.IsNullOrEmpty(destination) && destination != _parent.Id); MoveCommand = new RelayCommand<string>(async destination => await Move(destination), destination => Parent != null && !string.IsNullOrEmpty(destination) && destination != Parent.Id);
RestoreCommand = new RelayCommand(async () => await RestoreHistory()); RestoreCommand = new RelayCommand(async () => await RestoreHistory());
DeleteCommand = new RelayCommand(async () => await AskForDelete()); DeleteCommand = new RelayCommand(async () => await AskForDelete());
AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry); AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry);
@@ -263,7 +264,7 @@ namespace ModernKeePass.ViewModels
SelectedIndex = 0; SelectedIndex = 0;
_current = await _mediator.Send(new GetEntryQuery { Id = entryId }); _current = await _mediator.Send(new GetEntryQuery { Id = entryId });
SetCurrentEntry(_current); SetCurrentEntry(_current);
_parent = await _mediator.Send(new GetGroupQuery { Id = _current.ParentGroupId }); Parent = await _mediator.Send(new GetGroupQuery { Id = _current.ParentGroupId });
History = new ObservableCollection<IEntityVm> { _current }; History = new ObservableCollection<IEntityVm> { _current };
foreach (var entry in _current.History.Skip(1)) foreach (var entry in _current.History.Skip(1))
{ {
@@ -289,7 +290,7 @@ namespace ModernKeePass.ViewModels
private async Task Move(string destination) private async Task Move(string destination)
{ {
await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id }); await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id });
await _mediator.Send(new RemoveEntryCommand { ParentGroupId = _parent.Id, EntryId = Id }); await _mediator.Send(new RemoveEntryCommand { ParentGroupId = Parent.Id, EntryId = Id });
GoToGroup(destination); GoToGroup(destination);
} }

View File

@@ -10,7 +10,6 @@ using GalaSoft.MvvmLight.Views;
using MediatR; using MediatR;
using Messages; using Messages;
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Models;
using ModernKeePass.Application.Database.Commands.SaveDatabase; using ModernKeePass.Application.Database.Commands.SaveDatabase;
using ModernKeePass.Application.Database.Models; using ModernKeePass.Application.Database.Models;
using ModernKeePass.Application.Database.Queries.GetDatabase; using ModernKeePass.Application.Database.Queries.GetDatabase;
@@ -44,6 +43,8 @@ namespace ModernKeePass.ViewModels
public bool IsNotRoot => Database.RootGroupId != _group.Id; public bool IsNotRoot => Database.RootGroupId != _group.Id;
public GroupVm Parent { get; private set; }
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
group e by (e.Title.Value ?? string.Empty).ToUpper().FirstOrDefault() into grp group e by (e.Title.Value ?? string.Empty).ToUpper().FirstOrDefault() into grp
orderby grp.Key orderby grp.Key
@@ -82,11 +83,11 @@ namespace ModernKeePass.ViewModels
} }
} }
public string ParentGroupName => _parent == null ? Database.Name : _parent.Title; public string ParentGroupName => Parent == null ? Database.Name : Parent.Title;
public bool IsRecycleOnDelete => Database.IsRecycleBinEnabled && !IsInRecycleBin; public bool IsRecycleOnDelete => Database.IsRecycleBinEnabled && !IsInRecycleBin;
public bool IsInRecycleBin => _parent != null && _parent.Id == Database.RecycleBinId; public bool IsInRecycleBin => Parent != null && Parent.Id == Database.RecycleBinId;
public RelayCommand SaveCommand { get; } public RelayCommand SaveCommand { get; }
public RelayCommand SortEntriesCommand { get; } public RelayCommand SortEntriesCommand { get; }
@@ -108,21 +109,18 @@ namespace ModernKeePass.ViewModels
private readonly INavigationService _navigation; private readonly INavigationService _navigation;
private readonly IDialogService _dialog; private readonly IDialogService _dialog;
private readonly INotificationService _notification; private readonly INotificationService _notification;
private readonly IBreadcrumbService _breadcrumb;
private GroupVm _group; private GroupVm _group;
private GroupVm _parent;
private bool _isEditMode; private bool _isEditMode;
private EntryVm _reorderedEntry; private EntryVm _reorderedEntry;
private GroupVm _reorderedGroup; private GroupVm _reorderedGroup;
public GroupDetailVm(IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification, IBreadcrumbService breadcrumb) public GroupDetailVm(IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification)
{ {
_mediator = mediator; _mediator = mediator;
_resource = resource; _resource = resource;
_navigation = navigation; _navigation = navigation;
_dialog = dialog; _dialog = dialog;
_notification = notification; _notification = notification;
_breadcrumb = breadcrumb;
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);
SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode); SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode);
@@ -132,7 +130,7 @@ namespace ModernKeePass.ViewModels
CreateGroupCommand = new RelayCommand<string>(async newGroupName => await AddNewGroup(newGroupName), _ => !IsInRecycleBin && Database.RecycleBinId != Id); CreateGroupCommand = new RelayCommand<string>(async newGroupName => await AddNewGroup(newGroupName), _ => !IsInRecycleBin && Database.RecycleBinId != Id);
DeleteCommand = new RelayCommand(async () => await AskForDelete(),() => IsNotRoot); DeleteCommand = new RelayCommand(async () => await AskForDelete(),() => IsNotRoot);
GoBackCommand = new RelayCommand(() => _navigation.GoBack()); GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id), () => _parent != null); GoToParentCommand = new RelayCommand(() => GoToGroup(Parent.Id), () => Parent != null);
GoToGroupCommand = new RelayCommand<GroupVm>(group => GoToGroup(group.Id), group => group != null); GoToGroupCommand = new RelayCommand<GroupVm>(group => GoToGroup(group.Id), group => group != null);
GoToEntryCommand = new RelayCommand<EntryVm>(entry => GoToEntry(entry.Id), entry => entry != null); GoToEntryCommand = new RelayCommand<EntryVm>(entry => GoToEntry(entry.Id), entry => entry != null);
DeleteEntryCommand = new RelayCommand<EntryVm>(async entry => await AskForDeleteEntry(entry), entry => entry != null); DeleteEntryCommand = new RelayCommand<EntryVm>(async entry => await AskForDeleteEntry(entry), entry => entry != null);
@@ -140,43 +138,14 @@ namespace ModernKeePass.ViewModels
MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged()); MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged());
} }
private async Task AskForDeleteEntry(EntryVm entry)
{
if (IsRecycleOnDelete)
{
await DeleteEntry(entry.Id);
_notification.Show(_resource.GetResourceValue("EntityDeleting"), string.Format(_resource.GetResourceValue("EntryRecycled"), entry.Title));
}
else
{
await _dialog.ShowMessage(
string.Format(_resource.GetResourceValue("EntryDeletingConfirmation"), entry.Title),
_resource.GetResourceValue("EntityDeleting"),
_resource.GetResourceValue("EntityDeleteActionButton"),
_resource.GetResourceValue("EntityDeleteCancelButton"),
async isOk =>
{
if (isOk) await DeleteEntry(entry.Id);
});
}
}
private async Task DeleteEntry(string entryId)
{
await _mediator.Send(new DeleteEntryCommand
{
EntryId = entryId,
ParentGroupId = Id,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
});
}
public async Task Initialize(string groupId) public async Task Initialize(string groupId)
{ {
_group = await _mediator.Send(new GetGroupQuery { Id = groupId }); _group = await _mediator.Send(new GetGroupQuery { Id = groupId });
if (!string.IsNullOrEmpty(_group.ParentGroupId)) if (!string.IsNullOrEmpty(_group.ParentGroupId))
{ {
_parent = await _mediator.Send(new GetGroupQuery { Id = _group.ParentGroupId }); Parent = await _mediator.Send(new GetGroupQuery {Id = _group.ParentGroupId});
} }
else Parent = null;
Entries = new ObservableCollection<EntryVm>(_group.Entries); Entries = new ObservableCollection<EntryVm>(_group.Entries);
Entries.CollectionChanged += Entries_CollectionChanged; Entries.CollectionChanged += Entries_CollectionChanged;
@@ -186,7 +155,6 @@ namespace ModernKeePass.ViewModels
public void GoToEntry(string entryId, bool isNew = false) public void GoToEntry(string entryId, bool isNew = false)
{ {
_breadcrumb.Push(new BreadcrumbItem { Name = Title, Path = Id, Icon = _group.Icon });
_navigation.NavigateTo(Constants.Navigation.EntryPage, new NavigationItem _navigation.NavigateTo(Constants.Navigation.EntryPage, new NavigationItem
{ {
Id = entryId, Id = entryId,
@@ -195,7 +163,6 @@ namespace ModernKeePass.ViewModels
} }
public void GoToGroup(string groupId, bool isNew = false) public void GoToGroup(string groupId, bool isNew = false)
{ {
_breadcrumb.Push(new BreadcrumbItem { Name = Title, Path = Id, Icon = _group.Icon });
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem
{ {
Id = groupId, Id = groupId,
@@ -218,7 +185,7 @@ namespace ModernKeePass.ViewModels
public async Task Move(string destinationId) public async Task Move(string destinationId)
{ {
await _mediator.Send(new AddGroupCommand {ParentGroupId = destinationId, GroupId = Id }); await _mediator.Send(new AddGroupCommand {ParentGroupId = destinationId, GroupId = Id });
await _mediator.Send(new RemoveGroupCommand {ParentGroupId = _parent.Id, GroupId = Id }); await _mediator.Send(new RemoveGroupCommand {ParentGroupId = Parent.Id, GroupId = Id });
GoToGroup(destinationId); GoToGroup(destinationId);
} }
@@ -328,8 +295,38 @@ namespace ModernKeePass.ViewModels
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
}); });
_breadcrumb.Pop();
_navigation.GoBack(); _navigation.GoBack();
} }
private async Task AskForDeleteEntry(EntryVm entry)
{
if (IsRecycleOnDelete)
{
await DeleteEntry(entry);
_notification.Show(_resource.GetResourceValue("EntityDeleting"), string.Format(_resource.GetResourceValue("EntryRecycled"), entry.Title));
}
else
{
await _dialog.ShowMessage(
string.Format(_resource.GetResourceValue("EntryDeletingConfirmation"), entry.Title),
_resource.GetResourceValue("EntityDeleting"),
_resource.GetResourceValue("EntityDeleteActionButton"),
_resource.GetResourceValue("EntityDeleteCancelButton"),
async isOk =>
{
if (isOk) await DeleteEntry(entry);
});
}
}
private async Task DeleteEntry(EntryVm entry)
{
await _mediator.Send(new DeleteEntryCommand
{
EntryId = entry.Id,
ParentGroupId = Id,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
});
Entries.Remove(entry);
}
} }
} }

View File

@@ -232,7 +232,7 @@
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:BreadcrumbUserControl x:Name="Breadcrumb" /> <controls:BreadcrumbUserControl x:Name="Breadcrumb" Group="{Binding Parent}" />
<controls:TopMenuUserControl <controls:TopMenuUserControl
x:Name="TopMenu" Grid.Column="2" x:Name="TopMenu" Grid.Column="2"
MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}" MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}"

View File

@@ -130,7 +130,7 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemDelete" Command="{Binding DeleteEntryCommand}" CommandParameter="{Binding}" /> <MenuFlyoutItem x:Uid="EntryItemDelete" Command="{Binding DataContext.DeleteEntryCommand, ElementName=Page}" CommandParameter="{Binding}" />
</MenuFlyout> </MenuFlyout>
</Button.Flyout> </Button.Flyout>
</Button> </Button>
@@ -189,41 +189,12 @@
<!-- Back button and page title --> <!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}"> <Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" <userControls:BreadcrumbUserControl x:Name="Breadcrumb" Group="{Binding Parent}" />
Command="{Binding GoBackCommand}" <userControls:TopMenuUserControl x:Name="TopMenu" Grid.Column="2"
Height="{StaticResource MenuHeight}"
Width="{StaticResource MenuWidth}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<Button Grid.Column="1"
Height="{StaticResource MenuHeight}"
Command="{Binding GoToParentCommand}"
Style="{StaticResource NoBorderButtonStyle}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Up" />
<TextBlock x:Name="UpButtonText" Margin="10,2,0,0" Text="{Binding ParentGroupName}" FontStyle="Italic" HorizontalAlignment="Center" />
</StackPanel>
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip>
</Button>
<!--<Viewbox Grid.Column="2" MaxHeight="200" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<userControls:SymbolPickerUserControl Width="100" Height="70" SelectedSymbol="{Binding Icon, Mode=TwoWay}" />
</Viewbox>
<Viewbox Grid.Column="2" MaxHeight="200" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{Binding Icon}" Width="100" Height="70" />
</Viewbox>-->
<userControls:TopMenuUserControl x:Name="TopMenu" Grid.Column="4"
SortButtonVisibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" SortButtonVisibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}"
EditButtonVisibility="Visible" EditButtonVisibility="Visible"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}" IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
@@ -253,9 +224,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="-60,0,0,0"/> <DiscreteObjectKeyFrame KeyTime="0" Value="-60,0,0,0"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Medium"> <VisualState x:Name="Medium">
@@ -266,9 +234,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/> <DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Large"> <VisualState x:Name="Large">
@@ -282,9 +247,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/> <DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
</VisualStateGroup> </VisualStateGroup>

View File

@@ -53,18 +53,21 @@ namespace ModernKeePass.Views
VisualStateManager.GoToState(this, "Small", true); VisualStateManager.GoToState(this, "Small", true);
VisualStateManager.GoToState(TopMenu, "Collapsed", true); VisualStateManager.GoToState(TopMenu, "Collapsed", true);
VisualStateManager.GoToState(HamburgerMenu, "Hidden", true); VisualStateManager.GoToState(HamburgerMenu, "Hidden", true);
VisualStateManager.GoToState(Breadcrumb, "Small", true);
} }
else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008) else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008)
{ {
VisualStateManager.GoToState(this, "Medium", true); VisualStateManager.GoToState(this, "Medium", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Medium", true);
} }
else else
{ {
VisualStateManager.GoToState(this, "Large", true); VisualStateManager.GoToState(this, "Large", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Expanded", true); VisualStateManager.GoToState(HamburgerMenu, "Expanded", true);
VisualStateManager.GoToState(Breadcrumb, "Large", true);
} }
} }

View File

@@ -9,18 +9,19 @@
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core" xmlns:core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d"> mc:Ignorable="d">
<StackPanel Orientation="Horizontal" DataContext="{Binding Source={StaticResource Locator}, Path=Breadcrumb}"> <StackPanel x:Name="StackPanel" Orientation="Horizontal" DataContext="{Binding Source={StaticResource Locator}, Path=Breadcrumb}">
<StackPanel.Resources> <StackPanel.Resources>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/> <converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
<DataTemplate x:Key="FirstItemTemplate"> <DataTemplate x:Key="FirstItemTemplate">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=48}" Margin="0,3,0,0" />
<TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" /> <TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="OtherItemTemplate"> <DataTemplate x:Key="OtherItemTemplate">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Play" Margin="0" /> <TextBlock Margin="10,5,10,0" Text=">" HorizontalAlignment="Center" />
<SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=48}" Margin="0" /> <SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=48}" Margin="0,3,0,0" />
<TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" /> <TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
@@ -37,17 +38,18 @@
</Button> </Button>
<Button <Button
Height="{StaticResource MenuHeight}" Height="{StaticResource MenuHeight}"
Command="{Binding GoToCommand}" Command="{Binding GoUpCommand}"
Style="{StaticResource NoBorderButtonStyle}"> Style="{StaticResource NoBorderButtonStyle}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Up" /> <SymbolIcon Symbol="Up" />
<TextBlock x:Name="UpButtonText" Margin="10,2,0,0" Text="{Binding ParentGroupName}" FontStyle="Italic" HorizontalAlignment="Center" /> <SymbolIcon x:Name="UpButtonIcon" Symbol="{Binding ParentGroupIcon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=48}" Margin="16,0,0,0" />
<TextBlock x:Name="UpButtonText" Margin="11,2,0,0" Text="{Binding ParentGroupName}" FontStyle="Italic" FontWeight="Normal" HorizontalAlignment="Center" />
</StackPanel> </StackPanel>
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" /> <ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
</Button> </Button>
<ListView x:Name="ListView" ItemsSource="{Binding BreadcrumbItems}" Height="{StaticResource MenuHeight}"> <ListView x:Name="ListView" ItemsSource="{Binding BreadcrumbItems}">
<ListView.ItemsPanel> <ListView.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" /> <StackPanel Orientation="Horizontal" />
@@ -59,6 +61,7 @@
<ListView.ItemContainerStyle> <ListView.ItemContainerStyle>
<Style TargetType="ListViewItem"> <Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0"/> <Setter Property="Margin" Value="0"/>
<Setter Property="Height" Value="{StaticResource MenuHeight}" />
</Style> </Style>
</ListView.ItemContainerStyle> </ListView.ItemContainerStyle>
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
@@ -71,6 +74,9 @@
<VisualStateGroup x:Name="PageLayout"> <VisualStateGroup x:Name="PageLayout">
<VisualState x:Name="Small"> <VisualState x:Name="Small">
<Storyboard> <Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonIcon" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
@@ -81,6 +87,9 @@
</VisualState> </VisualState>
<VisualState x:Name="Medium"> <VisualState x:Name="Medium">
<Storyboard> <Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonIcon" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
@@ -91,6 +100,9 @@
</VisualState> </VisualState>
<VisualState x:Name="Large"> <VisualState x:Name="Large">
<Storyboard> <Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonIcon" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>

View File

@@ -1,9 +1,30 @@
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236 using Windows.UI.Xaml;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.ViewModels;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls namespace ModernKeePass.Views.UserControls
{ {
public sealed partial class BreadcrumbUserControl public sealed partial class BreadcrumbUserControl
{ {
public GroupVm Group
{
get { return (GroupVm)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
}
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register(
nameof(Group),
typeof(GroupVm),
typeof(BreadcrumbUserControl),
new PropertyMetadata(null, async (o, args) =>
{
var userControl = o as BreadcrumbUserControl;
var vm = userControl?.StackPanel.DataContext as BreadcrumbControlVm;
if (vm != null) await vm.Initialize(args.NewValue as GroupVm).ConfigureAwait(false);
}));
public BreadcrumbUserControl() public BreadcrumbUserControl()
{ {
InitializeComponent(); InitializeComponent();

View File

@@ -573,4 +573,7 @@
<data name="ToastCopyTitle.Message" xml:space="preserve"> <data name="ToastCopyTitle.Message" xml:space="preserve">
<value>Title copied to clipboard</value> <value>Title copied to clipboard</value>
</data> </data>
<data name="EntryItemDelete.Text" xml:space="preserve">
<value>Delete</value>
</data>
</root> </root>

View File

@@ -573,4 +573,7 @@
<data name="ToastCopyTitle.Message" xml:space="preserve"> <data name="ToastCopyTitle.Message" xml:space="preserve">
<value>Titre copié dans le presse-papiers</value> <value>Titre copié dans le presse-papiers</value>
</data> </data>
<data name="EntryItemDelete.Text" xml:space="preserve">
<value>Supprimer</value>
</data>
</root> </root>

View File

@@ -1,45 +1,62 @@
using System.Collections.Generic; using System.Collections.ObjectModel;
using System.Linq; using System.Threading.Tasks;
using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Views; using GalaSoft.MvvmLight.Views;
using ModernKeePass.Application.Common.Interfaces; using MediatR;
using ModernKeePass.Application.Common.Models; using ModernKeePass.Application.Common.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Application.Group.Queries.GetGroup;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Models; using ModernKeePass.Models;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
public class BreadcrumbControlVm public class BreadcrumbControlVm
{ {
public IEnumerable<BreadcrumbItem> BreadcrumbItems { get; } public ObservableCollection<BreadcrumbItem> BreadcrumbItems { get; }
public string ParentGroupName => BreadcrumbItems.Last()?.Name; public string ParentGroupName { get; private set; }
public Icon ParentGroupIcon { get; private set; }
public RelayCommand GoBackCommand { get; } public RelayCommand GoBackCommand { get; }
public RelayCommand GoUpCommand { get; private set; }
public RelayCommand<int> GoToCommand { get; } public RelayCommand<int> GoToCommand { get; }
private readonly IMediator _mediator;
private readonly INavigationService _navigation; private readonly INavigationService _navigation;
private readonly IBreadcrumbService _breadcrumb;
public BreadcrumbControlVm(INavigationService navigation, IBreadcrumbService breadcrumb) public BreadcrumbControlVm(IMediator mediator, INavigationService navigation)
{ {
_mediator = mediator;
_navigation = navigation; _navigation = navigation;
_breadcrumb = breadcrumb;
BreadcrumbItems = _breadcrumb.GetItems().Reverse(); BreadcrumbItems = new ObservableCollection<BreadcrumbItem>();
GoBackCommand = new RelayCommand(GoBack); GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToCommand = new RelayCommand<int>(GoTo); GoToCommand = new RelayCommand<int>(GoTo);
} }
public async Task Initialize(GroupVm group)
{
GoUpCommand = new RelayCommand(() => GoTo(BreadcrumbItems.Count - 1), () => group != null);
if (group == null) return;
ParentGroupName = group.Title;
ParentGroupIcon = group.Icon;
BreadcrumbItems.Insert(0, new BreadcrumbItem { Path = group.Id, Name = group.Title, Icon = group.Icon });
var parentGroup = group;
while (!string.IsNullOrEmpty(parentGroup.ParentGroupId))
{
parentGroup = await _mediator.Send(new GetGroupQuery {Id = parentGroup.ParentGroupId});
BreadcrumbItems.Insert(0, new BreadcrumbItem {Path = parentGroup.Id, Name = parentGroup.Title, Icon = parentGroup.Icon});
}
}
private void GoTo(int index) private void GoTo(int index)
{ {
var breadcrumb = _breadcrumb.Pop(BreadcrumbItems.Count() - index); if (BreadcrumbItems.Count == 0) return;
var breadcrumb = BreadcrumbItems[index];
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = breadcrumb.Path }); _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = breadcrumb.Path });
} }
private void GoBack()
{
_breadcrumb.Pop();
_navigation.GoBack();
}
} }
} }

View File

@@ -57,7 +57,6 @@ namespace ModernKeePass.ViewModels
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INavigationService>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INavigationService>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INotificationService>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INotificationService>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<ICryptographyClient>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<ICryptographyClient>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IBreadcrumbService>());
} }
SimpleIoc.Default.Register<SecurityVm>(); SimpleIoc.Default.Register<SecurityVm>();