diff --git a/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs b/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs index 057104c..bca6126 100644 --- a/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs +++ b/ModernKeePass.Application/Common/Interfaces/IDatabaseProxy.cs @@ -8,6 +8,7 @@ namespace ModernKeePass.Application.Common.Interfaces { public interface IDatabaseProxy { + // PW Database properties bool IsOpen { get; } string Name { get; } string RootGroupId { get; } @@ -16,6 +17,8 @@ namespace ModernKeePass.Application.Common.Interfaces string KeyDerivationId { get; set; } string Compression { get; set; } bool IsRecycleBinEnabled { get; set; } + + // Custom properties string FileAccessToken { get; set; } int Size { get; set; } bool IsDirty { get; set; } diff --git a/ModernKeePass.Application/Database/Commands/CloseDatabase/CloseDatabaseCommand.cs b/ModernKeePass.Application/Database/Commands/CloseDatabase/CloseDatabaseCommand.cs index f7f99d2..ae88bec 100644 --- a/ModernKeePass.Application/Database/Commands/CloseDatabase/CloseDatabaseCommand.cs +++ b/ModernKeePass.Application/Database/Commands/CloseDatabase/CloseDatabaseCommand.cs @@ -21,7 +21,11 @@ namespace ModernKeePass.Application.Database.Commands.CloseDatabase if (!_database.IsOpen) throw new DatabaseClosedException(); _database.CloseDatabase(); _file.ReleaseFile(_database.FileAccessToken); + + // Cleanup _database.FileAccessToken = null; + _database.IsDirty = false; + _database.Size = 0; } } } diff --git a/ModernKeePass.Application/Database/Commands/CreateDatabase/CreateDatabaseCommand.cs b/ModernKeePass.Application/Database/Commands/CreateDatabase/CreateDatabaseCommand.cs index 1a96008..d239405 100644 --- a/ModernKeePass.Application/Database/Commands/CreateDatabase/CreateDatabaseCommand.cs +++ b/ModernKeePass.Application/Database/Commands/CreateDatabase/CreateDatabaseCommand.cs @@ -29,7 +29,7 @@ namespace ModernKeePass.Application.Database.Commands.CreateDatabase public async Task Handle(CreateDatabaseCommand message) { - if (_database.IsOpen) throw new DatabaseOpenException(); + if (_database.IsDirty) throw new DatabaseOpenException(); var version = DatabaseVersion.V2; switch (message.Version) @@ -48,8 +48,6 @@ namespace ModernKeePass.Application.Database.Commands.CreateDatabase Password = message.Password }, message.Name, version); _database.FileAccessToken = message.FilePath; - var contents = await _database.SaveDatabase(); - await _file.WriteBinaryContentsToFile(_database.FileAccessToken, contents); if (message.CreateSampleData) { diff --git a/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs b/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs index 9453d7c..94cc86f 100644 --- a/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs +++ b/ModernKeePass.Application/Database/Commands/SaveDatabase/SaveDatabaseCommand.cs @@ -34,8 +34,7 @@ namespace ModernKeePass.Application.Database.Commands.SaveDatabase // Test DB integrity before writing changes to file _database.CloseDatabase(); - var file = await _file.OpenBinaryFile(_database.FileAccessToken); - await _database.ReOpen(file); + await _database.ReOpen(contents); await _file.WriteBinaryContentsToFile(_database.FileAccessToken, contents); } diff --git a/ModernKeePass.Application/Database/Queries/OpenDatabase/OpenDatabaseQuery.cs b/ModernKeePass.Application/Database/Queries/OpenDatabase/OpenDatabaseQuery.cs index baafb10..8e7ec2f 100644 --- a/ModernKeePass.Application/Database/Queries/OpenDatabase/OpenDatabaseQuery.cs +++ b/ModernKeePass.Application/Database/Queries/OpenDatabase/OpenDatabaseQuery.cs @@ -25,18 +25,19 @@ namespace ModernKeePass.Application.Database.Queries.OpenDatabase public async Task Handle(OpenDatabaseQuery message) { - if (_database.IsOpen) throw new DatabaseOpenException(); + if (_database.IsDirty) throw new DatabaseOpenException(); var file = await _file.OpenBinaryFile(message.FilePath); + var hasKeyFile = !string.IsNullOrEmpty(message.KeyFilePath); await _database.Open(file, new Credentials { - KeyFileContents = !string.IsNullOrEmpty(message.KeyFilePath) ? await _file.OpenBinaryFile(message.KeyFilePath): null, + KeyFileContents = hasKeyFile ? await _file.OpenBinaryFile(message.KeyFilePath): null, Password = message.Password }); + if (hasKeyFile) _file.ReleaseFile(message.KeyFilePath); _database.Size = file.Length; _database.FileAccessToken = message.FilePath; } - } } } \ No newline at end of file diff --git a/ModernKeePass.Domain/Exceptions/SaveException.cs b/ModernKeePass.Domain/Exceptions/SaveException.cs index 4ad0818..d2c0474 100644 --- a/ModernKeePass.Domain/Exceptions/SaveException.cs +++ b/ModernKeePass.Domain/Exceptions/SaveException.cs @@ -4,11 +4,13 @@ namespace ModernKeePass.Domain.Exceptions { public class SaveException : Exception { - public new Exception InnerException { get; } + public new string Message { get; } + public new string Source { get; } public SaveException(Exception exception) { - InnerException = exception; + Message = exception.Message; + Source = exception.Source; } } } diff --git a/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs b/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs index 20bd9e2..4730988 100644 --- a/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs +++ b/ModernKeePass.Infrastructure/KeePass/KeePassDatabaseClient.cs @@ -351,7 +351,7 @@ namespace ModernKeePass.Infrastructure.KeePass private CompositeKey CreateCompositeKey(Credentials credentials) { var compositeKey = new CompositeKey(); - if (!string.IsNullOrEmpty(credentials.Password)) compositeKey.AddUserKey(new KcpPassword(credentials.Password)); + if (credentials.Password != null) compositeKey.AddUserKey(new KcpPassword(credentials.Password)); if (credentials.KeyFileContents != null) { compositeKey.AddUserKey(new KcpKeyFile(IOConnectionInfo.FromByteArray(credentials.KeyFileContents))); diff --git a/ModernKeePass.Infrastructure/UWP/UwpRecentFilesClient.cs b/ModernKeePass.Infrastructure/UWP/UwpRecentFilesClient.cs index 9a1d952..2b8f5ea 100644 --- a/ModernKeePass.Infrastructure/UWP/UwpRecentFilesClient.cs +++ b/ModernKeePass.Infrastructure/UWP/UwpRecentFilesClient.cs @@ -21,7 +21,7 @@ namespace ModernKeePass.Infrastructure.UWP { var file = await _mru.GetFileAsync(token, updateAccessTime ? AccessCacheOptions.None : AccessCacheOptions.SuppressAccessTimeUpdate).AsTask().ConfigureAwait(false); - _fal.AddOrReplace(token, file); + _fal.AddOrReplace(token, file, file.Name); return new FileInfo { Id = token, diff --git a/ModernKeePass/App.xaml b/ModernKeePass/App.xaml index 175a021..23eb518 100644 --- a/ModernKeePass/App.xaml +++ b/ModernKeePass/App.xaml @@ -1,21 +1,26 @@ - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/ModernKeePass/App.xaml.cs b/ModernKeePass/App.xaml.cs index e855750..ee17779 100644 --- a/ModernKeePass/App.xaml.cs +++ b/ModernKeePass/App.xaml.cs @@ -88,10 +88,9 @@ namespace ModernKeePass if (realException is SaveException) { - var innerException = realException.InnerException; unhandledExceptionEventArgs.Handled = true; - _hockey.TrackException(innerException); - await _dialog.ShowMessage(innerException?.Message, + _hockey.TrackException(realException); + await _dialog.ShowMessage(realException.Message, _resource.GetResourceValue("MessageDialogSaveErrorTitle"), _resource.GetResourceValue("MessageDialogSaveErrorButtonSaveAs"), _resource.GetResourceValue("MessageDialogSaveErrorButtonDiscard"), @@ -112,7 +111,7 @@ namespace ModernKeePass var file = await savePicker.PickSaveFileAsync().AsTask(); if (file != null) { - var token = StorageApplicationPermissions.FutureAccessList.Add(file); + var token = StorageApplicationPermissions.FutureAccessList.Add(file, file.Name); await _mediator.Send(new SaveDatabaseCommand {FilePath = token}); } } @@ -252,7 +251,7 @@ namespace ModernKeePass if (file != null) { - var token = StorageApplicationPermissions.FutureAccessList.Add(file); + var token = StorageApplicationPermissions.FutureAccessList.Add(file, file.Name); var fileInfo = new FileInfo { Id = token, diff --git a/ModernKeePass/DependencyInjection.cs b/ModernKeePass/DependencyInjection.cs index 7420f02..646338c 100644 --- a/ModernKeePass/DependencyInjection.cs +++ b/ModernKeePass/DependencyInjection.cs @@ -25,7 +25,6 @@ namespace ModernKeePass nav.Configure(Constants.Navigation.GroupPage, typeof(GroupDetailPage)); return nav; }); - services.AddSingleton(provider => Messenger.Default); services.AddTransient(typeof(IDialogService), typeof(DialogService)); services.AddSingleton(provider => diff --git a/ModernKeePass/Interfaces/IImportService.cs b/ModernKeePass/Interfaces/IImportService.cs deleted file mode 100644 index 57880c3..0000000 --- a/ModernKeePass/Interfaces/IImportService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; -using Windows.Storage; -using ModernKeePass.ViewModels; - -namespace ModernKeePass.Interfaces -{ - public interface IImportService where T: IFormat - { - Task Import(T format, IStorageFile source, GroupVm group); - } -} \ No newline at end of file diff --git a/ModernKeePass/Interfaces/IVmEntity.cs b/ModernKeePass/Interfaces/IVmEntity.cs deleted file mode 100644 index 2593463..0000000 --- a/ModernKeePass/Interfaces/IVmEntity.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Windows.UI.Xaml.Controls; -using ModernKeePass.Application.Group.Models; - -namespace ModernKeePass.Interfaces -{ - public interface IVmEntity - { - Symbol Icon { get; } - string Id { get; } - string Title { get; set; } - IEnumerable BreadCrumb { get; } - bool IsEditMode { get; } - - /// - /// Move a entity to the destination group - /// - /// The destination to move the entity to - Task Move(GroupVm destination); - } -} \ No newline at end of file diff --git a/ModernKeePass/ViewModel/MainViewModel.cs b/ModernKeePass/ViewModel/MainViewModel.cs deleted file mode 100644 index 5d6d57d..0000000 --- a/ModernKeePass/ViewModel/MainViewModel.cs +++ /dev/null @@ -1,34 +0,0 @@ -using GalaSoft.MvvmLight; - -namespace ModernKeePass.ViewModel -{ - /// - /// This class contains properties that the main View can data bind to. - /// - /// Use the mvvminpc snippet to add bindable properties to this ViewModel. - /// - /// - /// You can also use Blend to data bind with the tool's support. - /// - /// - /// See http://www.galasoft.ch/mvvm - /// - /// - public class MainViewModel : ViewModelBase - { - /// - /// Initializes a new instance of the MainViewModel class. - /// - public MainViewModel() - { - ////if (IsInDesignMode) - ////{ - //// // Code runs in Blend --> create design time data. - ////} - ////else - ////{ - //// // Code runs "for real" - ////} - } - } -} \ No newline at end of file diff --git a/ModernKeePass/ViewModel/ViewModelLocator.cs b/ModernKeePass/ViewModel/ViewModelLocator.cs deleted file mode 100644 index dbbd121..0000000 --- a/ModernKeePass/ViewModel/ViewModelLocator.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - In App.xaml: - - - - - In the View: - DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}" - - You can also use Blend to do all this with the tool's support. - See http://www.galasoft.ch/mvvm -*/ - -using CommonServiceLocator; -using GalaSoft.MvvmLight; -using GalaSoft.MvvmLight.Ioc; - -namespace ModernKeePass.ViewModel -{ - /// - /// This class contains static references to all the view models in the - /// application and provides an entry point for the bindings. - /// - public class ViewModelLocator - { - /// - /// Initializes a new instance of the ViewModelLocator class. - /// - public ViewModelLocator() - { - ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); - - ////if (ViewModelBase.IsInDesignModeStatic) - ////{ - //// // Create design time view services and models - //// SimpleIoc.Default.Register(); - ////} - ////else - ////{ - //// // Create run time view services and models - //// SimpleIoc.Default.Register(); - ////} - - SimpleIoc.Default.Register(); - } - - public MainViewModel Main => ServiceLocator.Current.GetInstance(); - - public static void Cleanup() - { - // TODO Clear the ViewModels - } - } -} \ No newline at end of file diff --git a/ModernKeePass/ViewModels/CompositeKeyVm.cs b/ModernKeePass/ViewModels/CompositeKeyVm.cs deleted file mode 100644 index 94f165a..0000000 --- a/ModernKeePass/ViewModels/CompositeKeyVm.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using GalaSoft.MvvmLight; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using ModernKeePass.Application.Common.Interfaces; -using ModernKeePass.Application.Database.Commands.CreateDatabase; -using ModernKeePass.Application.Database.Commands.UpdateCredentials; -using ModernKeePass.Application.Database.Queries.GetDatabase; -using ModernKeePass.Application.Database.Queries.OpenDatabase; -using ModernKeePass.Application.Security.Commands.GenerateKeyFile; -using ModernKeePass.Application.Security.Queries.EstimatePasswordComplexity; -using ModernKeePass.Domain.Dtos; - -namespace ModernKeePass.ViewModels -{ - public class CompositeKeyVm: ObservableObject - { - public enum StatusTypes - { - Normal = 0, - Error = 1, - Warning = 3, - Success = 5 - } - - public bool HasPassword - { - get { return _hasPassword; } - set - { - Set(() => HasPassword, ref _hasPassword, value); - RaisePropertyChanged(nameof(IsValid)); - } - } - - public bool HasKeyFile - { - get { return _hasKeyFile; } - set - { - Set(() => HasKeyFile, ref _hasKeyFile, value); - RaisePropertyChanged(nameof(IsValid)); - } - } - - public bool IsValid => !_isOpening && (HasPassword || HasKeyFile && KeyFilePath != null); - - public string Status - { - get { return _status; } - set { Set(() => Status, ref _status, value); } - } - - public int StatusType - { - get { return _statusType; } - set { Set(() => StatusType, ref _statusType, value); } - } - - public string Password - { - get { return _password; } - set - { - _password = value; - RaisePropertyChanged(nameof(PasswordComplexityIndicator)); - StatusType = (int)StatusTypes.Normal; - Status = string.Empty; - } - } - - public string KeyFilePath - { - get { return _keyFilePath; } - set - { - _keyFilePath = value; - RaisePropertyChanged(nameof(IsValid)); - } - } - - public string KeyFileText - { - get { return _keyFileText; } - set { Set(() => KeyFileText, ref _keyFileText, value); } - } - - public string RootGroupId { get; set; } - - public double PasswordComplexityIndicator => _mediator.Send(new EstimatePasswordComplexityQuery { Password = Password }).GetAwaiter().GetResult(); - - private bool _hasPassword; - private bool _hasKeyFile; - private bool _isOpening; - private string _password = string.Empty; - private string _status; - private int _statusType; - private string _keyFilePath; - private string _keyFileText; - private readonly IMediator _mediator; - private readonly ISettingsProxy _settings; - private readonly IResourceProxy _resource; - - public CompositeKeyVm() : this( - App.Services.GetRequiredService(), - App.Services.GetRequiredService(), - App.Services.GetRequiredService()) { } - - public CompositeKeyVm(IMediator mediator, ISettingsProxy settings, IResourceProxy resource) - { - _mediator = mediator; - _settings = settings; - _resource = resource; - _keyFileText = _resource.GetResourceValue("CompositeKeyDefaultKeyFile"); - } - - public async Task OpenDatabase(string databaseFilePath, bool createNew) - { - try - { - _isOpening = true; - RaisePropertyChanged(nameof(IsValid)); - if (createNew) - { - await _mediator.Send(new CreateDatabaseCommand - { - FilePath = databaseFilePath, - KeyFilePath = HasKeyFile ? KeyFilePath : null, - Password = HasPassword ? Password : null, - Name = "New Database", - CreateSampleData = _settings.GetSetting("Sample") - }); - } - else - { - await _mediator.Send(new OpenDatabaseQuery { - FilePath = databaseFilePath, - KeyFilePath = HasKeyFile ? KeyFilePath : null, - Password = HasPassword ? Password : null, - }); - } - RootGroupId = (await _mediator.Send(new GetDatabaseQuery())).RootGroupId; - return true; - } - catch (ArgumentException) - { - var errorMessage = new StringBuilder($"{_resource.GetResourceValue("CompositeKeyErrorOpen")}\n"); - if (HasPassword) errorMessage.AppendLine(_resource.GetResourceValue("CompositeKeyErrorUserPassword")); - if (HasKeyFile) errorMessage.AppendLine(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile")); - UpdateStatus(errorMessage.ToString(), StatusTypes.Error); - } - catch (Exception e) - { - var error = $"{_resource.GetResourceValue("CompositeKeyErrorOpen")}{e.Message}"; - UpdateStatus(error, StatusTypes.Error); - } - finally - { - _isOpening = false; - RaisePropertyChanged(nameof(IsValid)); - } - return false; - } - - public async Task UpdateKey() - { - await _mediator.Send(new UpdateCredentialsCommand - { - KeyFilePath = HasKeyFile ? KeyFilePath : null, - Password = HasPassword ? Password : null, - }); - UpdateStatus(_resource.GetResourceValue("CompositeKeyUpdated"), StatusTypes.Success); - } - - public async Task CreateKeyFile(FileInfo file) - { - // TODO: implement entropy generator - await _mediator.Send(new GenerateKeyFileCommand {KeyFilePath = file.Id}); - KeyFilePath = file.Path; - KeyFileText = file.Name; - } - - private void UpdateStatus(string text, StatusTypes type) - { - Status = text; - StatusType = (int)type; - } - } -} diff --git a/ModernKeePass/ViewModels/EntryDetailVm.cs b/ModernKeePass/ViewModels/EntryDetailVm.cs index c95e935..347910e 100644 --- a/ModernKeePass/ViewModels/EntryDetailVm.cs +++ b/ModernKeePass/ViewModels/EntryDetailVm.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Views; using MediatR; using Microsoft.Extensions.DependencyInjection; @@ -26,14 +27,12 @@ using ModernKeePass.Application.Group.Queries.GetGroup; using ModernKeePass.Application.Security.Commands.GeneratePassword; using ModernKeePass.Application.Security.Queries.EstimatePasswordComplexity; using ModernKeePass.Domain.Enums; -using ModernKeePass.Interfaces; using ModernKeePass.Application.Group.Models; using ModernKeePass.Extensions; -using RelayCommand = GalaSoft.MvvmLight.Command.RelayCommand; namespace ModernKeePass.ViewModels { - public class EntryDetailVm : ObservableObject, IVmEntity + public class EntryDetailVm : ObservableObject { public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password); public bool HasExpired => HasExpirationDate && ExpiryDate < DateTime.Now; @@ -230,6 +229,7 @@ namespace ModernKeePass.ViewModels private readonly INavigationService _navigation; private readonly IResourceProxy _resource; private readonly IDialogService _dialog; + private readonly INotificationService _notification; private readonly GroupVm _parent; private EntryVm _selectedItem; private int _selectedIndex; @@ -244,14 +244,16 @@ namespace ModernKeePass.ViewModels App.Services.GetRequiredService(), App.Services.GetRequiredService(), App.Services.GetRequiredService(), - App.Services.GetRequiredService()) { } + App.Services.GetRequiredService(), + App.Services.GetRequiredService()) { } - public EntryDetailVm(string entryId, IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog) + public EntryDetailVm(string entryId, IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog, INotificationService notification) { _mediator = mediator; _navigation = navigation; _resource = resource; _dialog = dialog; + _notification = notification; SelectedItem = _mediator.Send(new GetEntryQuery { Id = entryId }).GetAwaiter().GetResult(); _parent = _mediator.Send(new GetGroupQuery { Id = SelectedItem.ParentGroupId }).GetAwaiter().GetResult(); History = new ObservableCollection { SelectedItem }; @@ -273,31 +275,22 @@ namespace ModernKeePass.ViewModels { if (IsCurrentEntry) { - var isRecycleOnDelete = IsRecycleOnDelete; - - var message = isRecycleOnDelete - ? _resource.GetResourceValue("EntryRecyclingConfirmation") - : _resource.GetResourceValue("EntryDeletingConfirmation"); - await _dialog.ShowMessage(message, - _resource.GetResourceValue("EntityDeleteTitle"), - _resource.GetResourceValue("EntityDeleteActionButton"), - _resource.GetResourceValue("EntityDeleteCancelButton"), - async isOk => - { - if (isOk) + if (IsRecycleOnDelete) + { + await Delete(); + _notification.Show(_resource.GetResourceValue("EntryRecyclingConfirmation"), _resource.GetResourceValue("EntryRecycled")); + } + else + { + await _dialog.ShowMessage(_resource.GetResourceValue("EntryDeletingConfirmation"), + _resource.GetResourceValue("EntityDeleteTitle"), + _resource.GetResourceValue("EntityDeleteActionButton"), + _resource.GetResourceValue("EntityDeleteCancelButton"), + async isOk => { - var text = isRecycleOnDelete - ? _resource.GetResourceValue("EntryRecycled") - : _resource.GetResourceValue("EntryDeleted"); - //ToastNotificationHelper.ShowMovedToast(Entity, _resource.GetResourceValue("EntityDeleting"), text); - await _mediator.Send(new DeleteEntryCommand - { - EntryId = Id, ParentGroupId = SelectedItem.ParentGroupId, - RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") - }); - _navigation.GoBack(); - } - }); + if (isOk) await Delete(); + }); + } } else { @@ -305,14 +298,11 @@ namespace ModernKeePass.ViewModels _resource.GetResourceValue("EntityDeleteActionButton"), _resource.GetResourceValue("EntityDeleteCancelButton"), async isOk => { - if (isOk) - { - //ToastNotificationHelper.ShowMovedToast(Entity, _resource.GetResourceValue("EntityDeleting"), text); - await _mediator.Send(new DeleteHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex - 1 }); - History.RemoveAt(SelectedIndex); - SelectedIndex = 0; - SaveCommand.RaiseCanExecuteChanged(); - } + if (!isOk) return; + await _mediator.Send(new DeleteHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex - 1 }); + History.RemoveAt(SelectedIndex); + SelectedIndex = 0; + SaveCommand.RaiseCanExecuteChanged(); }); } } @@ -368,5 +358,16 @@ namespace ModernKeePass.ViewModels SaveCommand.RaiseCanExecuteChanged(); _isDirty = false; } + + private async Task Delete() + { + await _mediator.Send(new DeleteEntryCommand + { + EntryId = Id, + ParentGroupId = SelectedItem.ParentGroupId, + RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") + }); + _navigation.GoBack(); + } } } diff --git a/ModernKeePass/ViewModels/GroupDetailVm.cs b/ModernKeePass/ViewModels/GroupDetailVm.cs index a706387..c0ab849 100644 --- a/ModernKeePass/ViewModels/GroupDetailVm.cs +++ b/ModernKeePass/ViewModels/GroupDetailVm.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using Windows.UI.Xaml.Controls; using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Views; using MediatR; using Microsoft.Extensions.DependencyInjection; @@ -29,13 +30,11 @@ using ModernKeePass.Application.Group.Queries.GetGroup; using ModernKeePass.Application.Group.Queries.SearchEntries; using ModernKeePass.Common; using ModernKeePass.Domain.Enums; -using ModernKeePass.Interfaces; using ModernKeePass.Models; -using RelayCommand = GalaSoft.MvvmLight.Command.RelayCommand; namespace ModernKeePass.ViewModels { - public class GroupDetailVm : ObservableObject, IVmEntity + public class GroupDetailVm : ObservableObject { public ObservableCollection Entries { get; } @@ -102,6 +101,7 @@ namespace ModernKeePass.ViewModels private readonly IResourceProxy _resource; private readonly INavigationService _navigation; private readonly IDialogService _dialog; + private readonly INotificationService _notification; private readonly GroupVm _group; private readonly GroupVm _parent; private bool _isEditMode; @@ -113,15 +113,17 @@ namespace ModernKeePass.ViewModels App.Services.GetRequiredService(), App.Services.GetRequiredService(), App.Services.GetRequiredService(), - App.Services.GetRequiredService()) + App.Services.GetRequiredService(), + App.Services.GetRequiredService()) { } - public GroupDetailVm(string groupId, IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog) + public GroupDetailVm(string groupId, IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification) { _mediator = mediator; _resource = resource; _navigation = navigation; _dialog = dialog; + _notification = notification; _group = _mediator.Send(new GetGroupQuery { Id = groupId }).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(_group.ParentGroupId)) { @@ -145,29 +147,21 @@ namespace ModernKeePass.ViewModels private async Task AskForDelete() { - var message = IsRecycleOnDelete - ? _resource.GetResourceValue("GroupRecyclingConfirmation") - : _resource.GetResourceValue("GroupDeletingConfirmation"); - - await _dialog.ShowMessage(message, _resource.GetResourceValue("EntityDeleteTitle"), + if (IsRecycleOnDelete) + { + await Delete(); + _notification.Show(_resource.GetResourceValue("GroupRecyclingConfirmation"), _resource.GetResourceValue("GroupRecycled")); + } + else + { + await _dialog.ShowMessage(_resource.GetResourceValue("GroupDeletingConfirmation"), _resource.GetResourceValue("EntityDeleteTitle"), _resource.GetResourceValue("EntityDeleteActionButton"), _resource.GetResourceValue("EntityDeleteCancelButton"), async isOk => { - if (isOk) - { - var text = IsRecycleOnDelete - ? _resource.GetResourceValue("GroupRecycled") - : _resource.GetResourceValue("GroupDeleted"); - //ToastNotificationHelper.ShowMovedToast(Entity, resource.GetResourceValue("EntityDeleting"), text); - await _mediator.Send(new DeleteGroupCommand - { - GroupId = _group.Id, ParentGroupId = _group.ParentGroupId, - RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") - }); - _navigation.GoBack(); - } + if (isOk) await Delete(); }); + } } @@ -244,5 +238,16 @@ namespace ModernKeePass.ViewModels RaisePropertyChanged(nameof(Groups)); SaveCommand.RaiseCanExecuteChanged(); } + + private async Task Delete() + { + await _mediator.Send(new DeleteGroupCommand + { + GroupId = _group.Id, + ParentGroupId = _group.ParentGroupId, + RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") + }); + _navigation.GoBack(); + } } } diff --git a/ModernKeePass/ViewModels/MainVm.cs b/ModernKeePass/ViewModels/MainVm.cs index 4716967..1b9317f 100644 --- a/ModernKeePass/ViewModels/MainVm.cs +++ b/ModernKeePass/ViewModels/MainVm.cs @@ -4,6 +4,7 @@ using Windows.ApplicationModel; using Windows.UI.Xaml.Controls; using GalaSoft.MvvmLight; using MediatR; +using Messages; using Microsoft.Extensions.DependencyInjection; using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Database.Queries.GetDatabase; @@ -12,11 +13,23 @@ using ModernKeePass.Domain.Interfaces; using ModernKeePass.Models; using ModernKeePass.ViewModels.ListItems; using ModernKeePass.Views; +using System.Threading.Tasks; +using GalaSoft.MvvmLight.Views; +using ModernKeePass.Application.Database.Commands.CloseDatabase; +using ModernKeePass.Application.Database.Commands.SaveDatabase; +using ModernKeePass.Common; +using ModernKeePass.Domain.Exceptions; namespace ModernKeePass.ViewModels { - public class MainVm : ObservableObject, IHasSelectableObject + public class MainVm : ViewModelBase, IHasSelectableObject { + private readonly IMediator _mediator; + private readonly IRecentProxy _recent; + private readonly IResourceProxy _resource; + private readonly IDialogService _dialog; + private readonly INotificationService _notification; + private readonly INavigationService _navigation; private IOrderedEnumerable> _mainMenuItems; private ISelectableModel _selectedItem; @@ -55,19 +68,45 @@ namespace ModernKeePass.ViewModels destinationFrame, App.Services.GetRequiredService(), App.Services.GetRequiredService(), - App.Services.GetRequiredService(), + App.Services.GetRequiredService(), + App.Services.GetRequiredService(), + App.Services.GetRequiredService(), + App.Services.GetRequiredService(), databaseFile) { } - public MainVm(Frame referenceFrame, Frame destinationFrame, IMediator mediator, IRecentProxy recent, IResourceProxy resource, FileInfo databaseFile = null) + public MainVm( + Frame referenceFrame, + Frame destinationFrame, + IMediator mediator, + IRecentProxy recent, + IResourceProxy resource, + IDialogService dialog, + INotificationService notification, + INavigationService navigation, + FileInfo databaseFile = null) { - var database = mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); + _mediator = mediator; + _recent = recent; + _resource = resource; + _dialog = dialog; + _notification = notification; + _navigation = navigation; + BuildMainMenuItems(referenceFrame, destinationFrame, databaseFile); + + MessengerInstance.Register(this, NavigateToPage); + MessengerInstance.Register(this, async message => await DisplaySaveConfirmation(message)); + } + + private void BuildMainMenuItems(Frame referenceFrame, Frame destinationFrame, FileInfo databaseFile) + { + var database = _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); var mainMenuItems = new ObservableCollection { new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemOpen"), + Title = _resource.GetResourceValue("MainMenuItemOpen"), PageType = typeof(OpenDatabasePage), Destination = destinationFrame, Parameter = databaseFile, @@ -76,14 +115,14 @@ namespace ModernKeePass.ViewModels }, new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemNew"), + Title = _resource.GetResourceValue("MainMenuItemNew"), PageType = typeof(NewDatabasePage), Destination = destinationFrame, SymbolIcon = Symbol.Add }, new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemSave"), + Title = _resource.GetResourceValue("MainMenuItemSave"), PageType = typeof(SaveDatabasePage), Destination = destinationFrame, Parameter = referenceFrame, @@ -93,31 +132,31 @@ namespace ModernKeePass.ViewModels }, new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemRecent"), + Title = _resource.GetResourceValue("MainMenuItemRecent"), PageType = typeof(RecentDatabasesPage), Destination = destinationFrame, Parameter = referenceFrame, SymbolIcon = Symbol.Copy, - IsSelected = !database.IsOpen && recent.EntryCount > 0, - IsEnabled = recent.EntryCount > 0 + IsSelected = !database.IsOpen && _recent.EntryCount > 0, + IsEnabled = _recent.EntryCount > 0 }, new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemSettings"), + Title = _resource.GetResourceValue("MainMenuItemSettings"), PageType = typeof(SettingsPage), Destination = referenceFrame, SymbolIcon = Symbol.Setting }, new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemAbout"), + Title = _resource.GetResourceValue("MainMenuItemAbout"), PageType = typeof(AboutPage), Destination = destinationFrame, SymbolIcon = Symbol.Help }, new MainMenuItemVm { - Title = resource.GetResourceValue("MainMenuItemDonate"), + Title = _resource.GetResourceValue("MainMenuItemDonate"), PageType = typeof(DonatePage), Destination = destinationFrame, SymbolIcon = Symbol.Shop @@ -137,8 +176,41 @@ namespace ModernKeePass.ViewModels Group = "Databases", SymbolIcon = Symbol.ProtectedDocument }); - + MainMenuItems = from item in mainMenuItems group item by item.Group into grp orderby grp.Key select grp; } + + private void NavigateToPage(DatabaseOpenedMessage message) + { + _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = message.RootGroupId }); + } + + private async Task DisplaySaveConfirmation(DatabaseAlreadyOpenedMessage message) + { + var database = await _mediator.Send(new GetDatabaseQuery()); + await _dialog.ShowMessage(string.Format(_resource.GetResourceValue("MessageDialogDBOpenDesc"), database.Name), + _resource.GetResourceValue("MessageDialogDBOpenTitle"), + _resource.GetResourceValue("MessageDialogDBOpenButtonSave"), + _resource.GetResourceValue("MessageDialogDBOpenButtonDiscard"), + async isOk => + { + if (isOk) + { + try + { + await _mediator.Send(new SaveDatabaseCommand()); + _notification.Show(database.Name, _resource.GetResourceValue("ToastSavedMessage")); + } + catch (SaveException exception) + { + _notification.Show(exception.Source, exception.Message); + return; + } + } + + await _mediator.Send(new CloseDatabaseCommand()); + MessengerInstance.Send(new DatabaseClosedMessage { Parameter = message.Parameter }); + }); + } } } diff --git a/ModernKeePass/ViewModels/SettingsVm.cs b/ModernKeePass/ViewModels/SettingsVm.cs index e9167d7..283bb22 100644 --- a/ModernKeePass/ViewModels/SettingsVm.cs +++ b/ModernKeePass/ViewModels/SettingsVm.cs @@ -43,9 +43,7 @@ namespace ModernKeePass.ViewModels } } } - - public SettingsVm() : this(App.Services.GetRequiredService(), App.Services.GetRequiredService()) { } - + public SettingsVm(IMediator mediator, IResourceProxy resource) { var database = mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); diff --git a/ModernKeePass/Views/BasePages/LayoutAwarePageBase.cs b/ModernKeePass/Views/BasePages/LayoutAwarePageBase.cs index 3ba3be8..19db292 100644 --- a/ModernKeePass/Views/BasePages/LayoutAwarePageBase.cs +++ b/ModernKeePass/Views/BasePages/LayoutAwarePageBase.cs @@ -3,6 +3,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Navigation; +using GalaSoft.MvvmLight.Command; using ModernKeePass.Common; using ModernKeePass.Domain.Interfaces; diff --git a/ModernKeePass/Views/EntryDetailPage.xaml.cs b/ModernKeePass/Views/EntryDetailPage.xaml.cs index 0235185..09b6e7a 100644 --- a/ModernKeePass/Views/EntryDetailPage.xaml.cs +++ b/ModernKeePass/Views/EntryDetailPage.xaml.cs @@ -22,15 +22,6 @@ namespace ModernKeePass.Views #region Inscription de NavigationHelper - /// Les méthodes fournies dans cette section sont utilisées simplement pour permettre - /// NavigationHelper pour répondre aux méthodes de navigation de la page. - /// - /// La logique spécifique à la page doit être placée dans les gestionnaires d'événements pour - /// - /// et . - /// Le paramètre de navigation est disponible dans la méthode LoadState - /// en plus de l'état de page conservé durant une session antérieure. - protected override async void OnNavigatedTo(NavigationEventArgs e) { var args = e.Parameter as NavigationItem; diff --git a/ModernKeePass/Views/GroupDetailPage.xaml.cs b/ModernKeePass/Views/GroupDetailPage.xaml.cs index 15fc128..c9111f5 100644 --- a/ModernKeePass/Views/GroupDetailPage.xaml.cs +++ b/ModernKeePass/Views/GroupDetailPage.xaml.cs @@ -37,16 +37,7 @@ namespace ModernKeePass.Views } #region NavigationHelper registration - - /// The methods provided in this section are simply used to allow - /// NavigationHelper to respond to the page's navigation methods. - /// - /// Page specific logic should be placed in event handlers for the - /// - /// and . - /// The navigation parameter is available in the LoadState method - /// in addition to page state preserved during an earlier session. - /// + protected override void OnNavigatedTo(NavigationEventArgs e) { var navigationItem = e.Parameter as NavigationItem; diff --git a/ModernKeePass/Views/MainPage.xaml.cs b/ModernKeePass/Views/MainPage.xaml.cs index df94202..a582287 100644 --- a/ModernKeePass/Views/MainPage.xaml.cs +++ b/ModernKeePass/Views/MainPage.xaml.cs @@ -18,10 +18,16 @@ namespace ModernKeePass.Views public MainPage() { InitializeComponent(); + Unloaded += MainPage_Unloaded; ListView = MenuListView; ListViewSource = MenuItemsSource; } + private void MainPage_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + Model.Cleanup(); + } + private new void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { base.ListView_SelectionChanged(sender, e); diff --git a/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml b/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml index 7dfa220..0e04cfd 100644 --- a/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml +++ b/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml @@ -5,15 +5,14 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:converters="using:ModernKeePass.Converters" - xmlns:viewModels="using:ModernKeePass.ViewModels" xmlns:userControls="using:ModernKeePass.Views.UserControls" - mc:Ignorable="d"> + mc:Ignorable="d" + DataContext="{Binding Source={StaticResource Locator}, Path=New}"> - - + diff --git a/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml.cs b/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml.cs index 89f9399..8b1276a 100644 --- a/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml.cs +++ b/ModernKeePass/Views/MainPageFrames/NewDatabasePage.xaml.cs @@ -20,7 +20,7 @@ namespace ModernKeePass.Views public sealed partial class NewDatabasePage { private readonly IResourceProxy _resource; - private NewVm Model => (NewVm)Resources["ViewModel"]; + private NewVm Model => (NewVm)DataContext; public NewDatabasePage(): this(App.Services.GetRequiredService()) { } public NewDatabasePage(IResourceProxy resource) @@ -41,7 +41,7 @@ namespace ModernKeePass.Views var file = await savePicker.PickSaveFileAsync().AsTask(); if (file == null) return; - var token = StorageApplicationPermissions.FutureAccessList.Add(file); + var token = StorageApplicationPermissions.FutureAccessList.Add(file, file.Name); var fileInfo = new FileInfo { Id = token, diff --git a/ModernKeePass/Views/MainPageFrames/OpenDatabasePage.xaml b/ModernKeePass/Views/MainPageFrames/OpenDatabasePage.xaml index 04299ee..f75ecf2 100644 --- a/ModernKeePass/Views/MainPageFrames/OpenDatabasePage.xaml +++ b/ModernKeePass/Views/MainPageFrames/OpenDatabasePage.xaml @@ -3,17 +3,16 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:viewModels="using:ModernKeePass.ViewModels" xmlns:converters="using:ModernKeePass.Converters" xmlns:userControls="using:ModernKeePass.Views.UserControls" x:Class="ModernKeePass.Views.OpenDatabasePage" - mc:Ignorable="d"> + mc:Ignorable="d" + DataContext="{Binding Source={StaticResource Locator}, Path=Open}"> - - +