From c62ed584dc45da0b838c4c8a5a4675ef635399fd Mon Sep 17 00:00:00 2001 From: Geoffroy BONNEVILLE Date: Tue, 9 Jun 2020 20:18:17 +0200 Subject: [PATCH] New Breadcrumb user control New Breadcrumb service WIP icons and back button behavior --- ModernKeePass.Application/Application.csproj | 3 + .../Common/Interfaces/IBreadcrumbService.cs | 12 ++ .../Common/Models/BreadcrumbItem.cs | 12 ++ .../Common/Services/BreadcrumbService.cs | 37 ++++++ .../DependencyInjection.cs | 3 + ModernKeePass/ViewModels/EntryDetailVm.cs | 8 +- ModernKeePass/ViewModels/GroupDetailVm.cs | 42 ++++++- ModernKeePass/Views/EntryDetailPage.xaml | 37 +----- ModernKeePass/Views/EntryDetailPage.xaml.cs | 3 + ModernKeePass/Views/GroupDetailPage.xaml | 4 +- .../UserControls/BreadcrumbUserControl.xaml | 105 ++++++++++++++++++ .../BreadcrumbUserControl.xaml.cs | 12 ++ ModernKeePass/Win81App.csproj | 7 ++ .../FirstItemDataTemplateSelector.cs | 18 +++ .../UserControls/BreadcrumbControlVm.cs | 45 ++++++++ .../ViewModels/ViewModelLocatorCommon.cs | 3 + WinAppCommon/WinAppCommon.projitems | 2 + 17 files changed, 308 insertions(+), 45 deletions(-) create mode 100644 ModernKeePass.Application/Common/Interfaces/IBreadcrumbService.cs create mode 100644 ModernKeePass.Application/Common/Models/BreadcrumbItem.cs create mode 100644 ModernKeePass.Application/Common/Services/BreadcrumbService.cs create mode 100644 ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml create mode 100644 ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml.cs create mode 100644 WinAppCommon/TemplateSelectors/FirstItemDataTemplateSelector.cs create mode 100644 WinAppCommon/ViewModels/UserControls/BreadcrumbControlVm.cs diff --git a/ModernKeePass.Application/Application.csproj b/ModernKeePass.Application/Application.csproj index 269fc4a..4d1fc74 100644 --- a/ModernKeePass.Application/Application.csproj +++ b/ModernKeePass.Application/Application.csproj @@ -86,6 +86,7 @@ + @@ -99,6 +100,8 @@ + + diff --git a/ModernKeePass.Application/Common/Interfaces/IBreadcrumbService.cs b/ModernKeePass.Application/Common/Interfaces/IBreadcrumbService.cs new file mode 100644 index 0000000..cff8b2d --- /dev/null +++ b/ModernKeePass.Application/Common/Interfaces/IBreadcrumbService.cs @@ -0,0 +1,12 @@ +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 GetItems(); + } +} \ No newline at end of file diff --git a/ModernKeePass.Application/Common/Models/BreadcrumbItem.cs b/ModernKeePass.Application/Common/Models/BreadcrumbItem.cs new file mode 100644 index 0000000..4cdff16 --- /dev/null +++ b/ModernKeePass.Application/Common/Models/BreadcrumbItem.cs @@ -0,0 +1,12 @@ +using ModernKeePass.Domain.Enums; + +namespace ModernKeePass.Application.Common.Models +{ + public class BreadcrumbItem + { + public string Path { get; set; } + public string Name { get; set; } + public Icon Icon { get; set; } + + } +} \ No newline at end of file diff --git a/ModernKeePass.Application/Common/Services/BreadcrumbService.cs b/ModernKeePass.Application/Common/Services/BreadcrumbService.cs new file mode 100644 index 0000000..5965cda --- /dev/null +++ b/ModernKeePass.Application/Common/Services/BreadcrumbService.cs @@ -0,0 +1,37 @@ +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 _breadcrumb; + + public BreadcrumbService() + { + _breadcrumb = new Stack(); + } + + 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 GetItems() + { + return _breadcrumb; + } + } +} \ No newline at end of file diff --git a/ModernKeePass.Application/DependencyInjection.cs b/ModernKeePass.Application/DependencyInjection.cs index 19bdf9b..89d4557 100644 --- a/ModernKeePass.Application/DependencyInjection.cs +++ b/ModernKeePass.Application/DependencyInjection.cs @@ -2,6 +2,8 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using ModernKeePass.Application.Common.Behaviors; +using ModernKeePass.Application.Common.Interfaces; +using ModernKeePass.Application.Common.Services; namespace ModernKeePass.Application { @@ -13,6 +15,7 @@ namespace ModernKeePass.Application services.AddMediatR(assembly); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>)); + services.AddSingleton(typeof(IBreadcrumbService), typeof(BreadcrumbService)); return services; } } diff --git a/ModernKeePass/ViewModels/EntryDetailVm.cs b/ModernKeePass/ViewModels/EntryDetailVm.cs index 6c3e00e..60f4326 100644 --- a/ModernKeePass/ViewModels/EntryDetailVm.cs +++ b/ModernKeePass/ViewModels/EntryDetailVm.cs @@ -43,9 +43,7 @@ namespace ModernKeePass.ViewModels public bool HasExpired => HasExpirationDate && ExpiryDate < _dateTime.Now; public string Id => _current.Id; - - public string ParentGroupName => _parent.Title; - + public bool IsRecycleOnDelete { get @@ -209,8 +207,6 @@ namespace ModernKeePass.ViewModels public RelayCommand MoveCommand { get; } public RelayCommand RestoreCommand { get; } public RelayCommand DeleteCommand { get; } - public RelayCommand GoBackCommand { get; } - public RelayCommand GoToParentCommand { get; } public RelayCommand AddAdditionalField { get; } public RelayCommand DeleteAdditionalField { get; } public RelayCommand OpenAttachmentCommand { get; } @@ -249,8 +245,6 @@ namespace ModernKeePass.ViewModels MoveCommand = new RelayCommand(async destination => await Move(destination), destination => _parent != null && !string.IsNullOrEmpty(destination) && destination != _parent.Id); RestoreCommand = new RelayCommand(async () => await RestoreHistory()); DeleteCommand = new RelayCommand(async () => await AskForDelete()); - GoBackCommand = new RelayCommand(() => _navigation.GoBack()); - GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id)); AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry); DeleteAdditionalField = new RelayCommand(async field => await DeleteField(field), field => field != null && IsCurrentEntry); OpenAttachmentCommand = new RelayCommand(async attachment => await OpenAttachment(attachment)); diff --git a/ModernKeePass/ViewModels/GroupDetailVm.cs b/ModernKeePass/ViewModels/GroupDetailVm.cs index 53702af..205cb90 100644 --- a/ModernKeePass/ViewModels/GroupDetailVm.cs +++ b/ModernKeePass/ViewModels/GroupDetailVm.cs @@ -10,6 +10,7 @@ using GalaSoft.MvvmLight.Views; using MediatR; using Messages; using ModernKeePass.Application.Common.Interfaces; +using ModernKeePass.Application.Common.Models; using ModernKeePass.Application.Database.Commands.SaveDatabase; using ModernKeePass.Application.Database.Models; using ModernKeePass.Application.Database.Queries.GetDatabase; @@ -18,6 +19,7 @@ using ModernKeePass.Application.Group.Commands.AddEntry; using ModernKeePass.Application.Group.Commands.AddGroup; using ModernKeePass.Application.Group.Commands.CreateEntry; using ModernKeePass.Application.Group.Commands.CreateGroup; +using ModernKeePass.Application.Group.Commands.DeleteEntry; using ModernKeePass.Application.Group.Commands.DeleteGroup; using ModernKeePass.Application.Group.Commands.MoveEntry; using ModernKeePass.Application.Group.Commands.MoveGroup; @@ -97,6 +99,7 @@ namespace ModernKeePass.ViewModels public RelayCommand GoToParentCommand { get; } public RelayCommand GoToGroupCommand { get; } public RelayCommand GoToEntryCommand { get; } + public RelayCommand DeleteEntryCommand { get; } private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); @@ -105,19 +108,21 @@ namespace ModernKeePass.ViewModels private readonly INavigationService _navigation; private readonly IDialogService _dialog; private readonly INotificationService _notification; + private readonly IBreadcrumbService _breadcrumb; private GroupVm _group; private GroupVm _parent; private bool _isEditMode; private EntryVm _reorderedEntry; private GroupVm _reorderedGroup; - public GroupDetailVm(IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification) + public GroupDetailVm(IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification, IBreadcrumbService breadcrumb) { _mediator = mediator; _resource = resource; _navigation = navigation; _dialog = dialog; _notification = notification; + _breadcrumb = breadcrumb; SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode); @@ -130,10 +135,41 @@ namespace ModernKeePass.ViewModels GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id), () => _parent != null); GoToGroupCommand = new RelayCommand(group => GoToGroup(group.Id), group => group != null); GoToEntryCommand = new RelayCommand(entry => GoToEntry(entry.Id), entry => entry != null); + DeleteEntryCommand = new RelayCommand(async entry => await AskForDeleteEntry(entry), entry => entry != null); MessengerInstance.Register(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) { _group = await _mediator.Send(new GetGroupQuery { Id = groupId }); @@ -150,6 +186,7 @@ namespace ModernKeePass.ViewModels 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 { Id = entryId, @@ -158,6 +195,7 @@ namespace ModernKeePass.ViewModels } 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 { Id = groupId, @@ -289,6 +327,8 @@ namespace ModernKeePass.ViewModels ParentGroupId = _group.ParentGroupId, RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") }); + + _breadcrumb.Pop(); _navigation.GoBack(); } } diff --git a/ModernKeePass/Views/EntryDetailPage.xaml b/ModernKeePass/Views/EntryDetailPage.xaml index 3feaf44..7b5e5d0 100644 --- a/ModernKeePass/Views/EntryDetailPage.xaml +++ b/ModernKeePass/Views/EntryDetailPage.xaml @@ -32,7 +32,6 @@ - @@ -229,36 +228,13 @@ - - - - + - - - @@ -289,9 +262,6 @@ - - - @@ -299,9 +269,6 @@ - - - diff --git a/ModernKeePass/Views/EntryDetailPage.xaml.cs b/ModernKeePass/Views/EntryDetailPage.xaml.cs index 8612cea..5aada28 100644 --- a/ModernKeePass/Views/EntryDetailPage.xaml.cs +++ b/ModernKeePass/Views/EntryDetailPage.xaml.cs @@ -45,18 +45,21 @@ namespace ModernKeePass.Views VisualStateManager.GoToState(this, "Small", true); VisualStateManager.GoToState(TopMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Hidden", true); + VisualStateManager.GoToState(Breadcrumb, "Small", true); } else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008) { VisualStateManager.GoToState(this, "Medium", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); + VisualStateManager.GoToState(Breadcrumb, "Medium", true); } else { VisualStateManager.GoToState(this, "Large", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); + VisualStateManager.GoToState(Breadcrumb, "Large", true); } } } diff --git a/ModernKeePass/Views/GroupDetailPage.xaml b/ModernKeePass/Views/GroupDetailPage.xaml index 7a7b7e1..60d61d1 100644 --- a/ModernKeePass/Views/GroupDetailPage.xaml +++ b/ModernKeePass/Views/GroupDetailPage.xaml @@ -1,4 +1,4 @@ - - @@ -131,6 +130,7 @@ + diff --git a/ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml b/ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml new file mode 100644 index 0000000..daeb43d --- /dev/null +++ b/ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml.cs b/ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml.cs new file mode 100644 index 0000000..f2b829e --- /dev/null +++ b/ModernKeePass/Views/UserControls/BreadcrumbUserControl.xaml.cs @@ -0,0 +1,12 @@ +// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236 + +namespace ModernKeePass.Views.UserControls +{ + public sealed partial class BreadcrumbUserControl + { + public BreadcrumbUserControl() + { + InitializeComponent(); + } + } +} diff --git a/ModernKeePass/Win81App.csproj b/ModernKeePass/Win81App.csproj index 5a1ba23..089ff4a 100644 --- a/ModernKeePass/Win81App.csproj +++ b/ModernKeePass/Win81App.csproj @@ -124,6 +124,9 @@ SettingsWelcomePage.xaml + + BreadcrumbUserControl.xaml + ColorPickerUserControl.xaml @@ -219,6 +222,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/WinAppCommon/TemplateSelectors/FirstItemDataTemplateSelector.cs b/WinAppCommon/TemplateSelectors/FirstItemDataTemplateSelector.cs new file mode 100644 index 0000000..5d32170 --- /dev/null +++ b/WinAppCommon/TemplateSelectors/FirstItemDataTemplateSelector.cs @@ -0,0 +1,18 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace ModernKeePass.TemplateSelectors +{ + public class FirstItemDataTemplateSelector : DataTemplateSelector + { + public DataTemplate FirstItem { get; set; } + public DataTemplate OtherItem { get; set; } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + var itemsControl = ItemsControl.ItemsControlFromItemContainer(container); + var returnTemplate = itemsControl?.IndexFromContainer(container) == 0 ? FirstItem : OtherItem; + return returnTemplate; + } + } +} \ No newline at end of file diff --git a/WinAppCommon/ViewModels/UserControls/BreadcrumbControlVm.cs b/WinAppCommon/ViewModels/UserControls/BreadcrumbControlVm.cs new file mode 100644 index 0000000..02b606d --- /dev/null +++ b/WinAppCommon/ViewModels/UserControls/BreadcrumbControlVm.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using GalaSoft.MvvmLight.Command; +using GalaSoft.MvvmLight.Views; +using ModernKeePass.Application.Common.Interfaces; +using ModernKeePass.Application.Common.Models; +using ModernKeePass.Common; +using ModernKeePass.Models; + +namespace ModernKeePass.ViewModels +{ + public class BreadcrumbControlVm + { + public IEnumerable BreadcrumbItems { get; } + public string ParentGroupName => BreadcrumbItems.Last()?.Name; + + public RelayCommand GoBackCommand { get; } + public RelayCommand GoToCommand { get; } + + private readonly INavigationService _navigation; + private readonly IBreadcrumbService _breadcrumb; + + public BreadcrumbControlVm(INavigationService navigation, IBreadcrumbService breadcrumb) + { + _navigation = navigation; + _breadcrumb = breadcrumb; + + BreadcrumbItems = _breadcrumb.GetItems().Reverse(); + GoBackCommand = new RelayCommand(GoBack); + GoToCommand = new RelayCommand(GoTo); + } + + private void GoTo(int index) + { + var breadcrumb = _breadcrumb.Pop(BreadcrumbItems.Count() - index); + _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = breadcrumb.Path }); + } + + private void GoBack() + { + _breadcrumb.Pop(); + _navigation.GoBack(); + } + } +} \ No newline at end of file diff --git a/WinAppCommon/ViewModels/ViewModelLocatorCommon.cs b/WinAppCommon/ViewModels/ViewModelLocatorCommon.cs index 43b3042..7719b2f 100644 --- a/WinAppCommon/ViewModels/ViewModelLocatorCommon.cs +++ b/WinAppCommon/ViewModels/ViewModelLocatorCommon.cs @@ -57,6 +57,7 @@ namespace ModernKeePass.ViewModels SimpleIoc.Default.Register(() => App.Services.GetRequiredService()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService()); + SimpleIoc.Default.Register(() => App.Services.GetRequiredService()); } SimpleIoc.Default.Register(); @@ -70,6 +71,7 @@ namespace ModernKeePass.ViewModels SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); + SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); @@ -87,6 +89,7 @@ namespace ModernKeePass.ViewModels public TopMenuVm TopMenu => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); public ColorPickerControlVm ColorPicker => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); public PasswordGenerationBoxControlVm PasswordGenerationBox => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); + public BreadcrumbControlVm Breadcrumb => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); public NewVm New => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); public OpenVm Open => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); public RecentVm Recent => ServiceLocator.Current.GetInstance(Guid.NewGuid().ToString()); diff --git a/WinAppCommon/WinAppCommon.projitems b/WinAppCommon/WinAppCommon.projitems index 3eb064d..fbce8cf 100644 --- a/WinAppCommon/WinAppCommon.projitems +++ b/WinAppCommon/WinAppCommon.projitems @@ -42,6 +42,7 @@ + @@ -50,6 +51,7 @@ +