More commands/queries

WIP on XAML EntryVm and GroupVm
This commit is contained in:
Geoffroy BONNEVILLE
2020-03-27 13:27:29 +01:00
parent 22072bb2fe
commit e3638c2f5c
24 changed files with 344 additions and 227 deletions

View File

@@ -54,6 +54,9 @@
<Compile Include="Common\Interfaces\ISettingsProxy.cs" /> <Compile Include="Common\Interfaces\ISettingsProxy.cs" />
<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="Cryptography\Commands\SetCipher\SetCipherCommand.cs" />
<Compile Include="Cryptography\Commands\SetCompression\SetCompressionCommand.cs" />
<Compile Include="Cryptography\Commands\SetKeyDerivation\SetKeyDerivationCommand.cs" />
<Compile Include="Cryptography\Models\CipherVm.cs" /> <Compile Include="Cryptography\Models\CipherVm.cs" />
<Compile Include="Cryptography\Models\KeyDerivationVm.cs" /> <Compile Include="Cryptography\Models\KeyDerivationVm.cs" />
<Compile Include="Cryptography\Queries\GetCiphers\GetCiphersQuery.cs" /> <Compile Include="Cryptography\Queries\GetCiphers\GetCiphersQuery.cs" />
@@ -71,6 +74,7 @@
<Compile Include="Database\Queries\ReOpenDatabase\ReOpenDatabaseQuery.cs" /> <Compile Include="Database\Queries\ReOpenDatabase\ReOpenDatabaseQuery.cs" />
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="Entry\Commands\SetFieldValue\SetFieldValueCommand.cs" /> <Compile Include="Entry\Commands\SetFieldValue\SetFieldValueCommand.cs" />
<Compile Include="Entry\Commands\SetFieldValue\SetFieldValueCommandValidator.cs" />
<Compile Include="Entry\Models\EntryVm.cs" /> <Compile Include="Entry\Models\EntryVm.cs" />
<Compile Include="Group\Commands\AddEntry\AddEntryCommand.cs" /> <Compile Include="Group\Commands\AddEntry\AddEntryCommand.cs" />
<Compile Include="Group\Commands\AddGroup\AddGroupCommand.cs" /> <Compile Include="Group\Commands\AddGroup\AddGroupCommand.cs" />
@@ -78,6 +82,7 @@
<Compile Include="Group\Commands\CreateGroup\CreateGroupCommand.cs" /> <Compile Include="Group\Commands\CreateGroup\CreateGroupCommand.cs" />
<Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" /> <Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" />
<Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" /> <Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" />
<Compile Include="Group\Commands\InsertEntry\InsertEntryCommand.cs" />
<Compile Include="Group\Commands\RemoveEntry\RemoveEntryCommand.cs" /> <Compile Include="Group\Commands\RemoveEntry\RemoveEntryCommand.cs" />
<Compile Include="Group\Commands\RemoveGroup\RemoveGroupCommand.cs" /> <Compile Include="Group\Commands\RemoveGroup\RemoveGroupCommand.cs" />
<Compile Include="Group\Commands\SortEntries\SortEntriesCommand.cs" /> <Compile Include="Group\Commands\SortEntries\SortEntriesCommand.cs" />
@@ -102,9 +107,6 @@
<Folder Include="Entry\Queries\GetEntry\" /> <Folder Include="Entry\Queries\GetEntry\" />
<Folder Include="Group\Commands\UpdateGroup\" /> <Folder Include="Group\Commands\UpdateGroup\" />
<Folder Include="Group\Queries\GetGroup\" /> <Folder Include="Group\Queries\GetGroup\" />
<Folder Include="Parameters\Commands\SetCipher\" />
<Folder Include="Parameters\Commands\SetCompression\" />
<Folder Include="Parameters\Commands\SetKeyDerivation\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ModernKeePass.Domain\Domain.csproj"> <ProjectReference Include="..\ModernKeePass.Domain\Domain.csproj">

View File

@@ -25,8 +25,9 @@ namespace ModernKeePass.Application.Common.Interfaces
void CloseDatabase(); void CloseDatabase();
Task AddEntry(string parentGroupId, string entryId); Task AddEntry(string parentGroupId, string entryId);
Task InsertEntry(string parentGroupId, string entryId, int messageIndex);
Task AddGroup(string parentGroupId, string groupId); Task AddGroup(string parentGroupId, string groupId);
void UpdateEntry(string entryId, string fieldName, string fieldValue); void UpdateEntry(string entryId, string fieldName, object fieldValue);
void UpdateGroup(string groupId); void UpdateGroup(string groupId);
Task RemoveEntry(string parentGroupId, string entryId); Task RemoveEntry(string parentGroupId, string entryId);
Task RemoveGroup(string parentGroupId, string groupId); Task RemoveGroup(string parentGroupId, string groupId);

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Cryptography.Commands.SetCipher
{
public class SetCipherCommand : IRequest
{
public string CipherId { get; set; }
public class SetCipherCommandHandler : IRequestHandler<SetCipherCommand>
{
private readonly IDatabaseProxy _database;
public SetCipherCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetCipherCommand message)
{
if (_database.IsOpen) _database.CipherId = message.CipherId;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Cryptography.Commands.SetCompression
{
public class SetCompressionCommand : IRequest
{
public string Compression { get; set; }
public class SetCompressionCommandHandler : IRequestHandler<SetCompressionCommand>
{
private readonly IDatabaseProxy _database;
public SetCompressionCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetCompressionCommand message)
{
if (_database.IsOpen) _database.Compression = message.Compression;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Cryptography.Commands.SetKeyDerivation
{
public class SetKeyDerivationCommand : IRequest
{
public string KeyDerivationId { get; set; }
public class SetKeyDerivationCommandHandler : IRequestHandler<SetKeyDerivationCommand>
{
private readonly IDatabaseProxy _database;
public SetKeyDerivationCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetKeyDerivationCommand message)
{
if (_database.IsOpen) _database.KeyDerivationId = message.KeyDerivationId;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -14,7 +14,7 @@ namespace ModernKeePass.Application.Database.Commands.UpdateCredentials
{ {
private readonly IDatabaseProxy _database; private readonly IDatabaseProxy _database;
public UpdateCredentialsCommandHandler(IDatabaseProxy database, IMediator mediator) public UpdateCredentialsCommandHandler(IDatabaseProxy database)
{ {
_database = database; _database = database;
} }

View File

@@ -13,7 +13,7 @@ namespace ModernKeePass.Application
var assembly = typeof(DependencyInjection).GetTypeInfo().Assembly; var assembly = typeof(DependencyInjection).GetTypeInfo().Assembly;
services.AddAutoMapper(assembly); services.AddAutoMapper(assembly);
services.AddMediatR(assembly); services.AddMediatR(assembly);
//services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); //services.AddValidatorsFromAssembly(assembly);
return services; return services;
} }

View File

@@ -8,7 +8,7 @@ namespace ModernKeePass.Application.Entry.Commands.SetFieldValue
{ {
public string EntryId { get; set; } public string EntryId { get; set; }
public string FieldName { get; set; } public string FieldName { get; set; }
public string FieldValue { get; set; } public object FieldValue { get; set; }
public class SetFieldValueCommandHandler : IRequestHandler<SetFieldValueCommand> public class SetFieldValueCommandHandler : IRequestHandler<SetFieldValueCommand>
{ {

View File

@@ -0,0 +1,17 @@
using FluentValidation;
namespace ModernKeePass.Application.Entry.Commands.SetFieldValue
{
public class SetFieldValueCommandValidator: AbstractValidator<SetFieldValueCommand>
{
public SetFieldValueCommandValidator()
{
RuleFor(v => v.EntryId)
.NotNull()
.NotEmpty();
RuleFor(v => v.FieldName)
.NotNull()
.NotEmpty();
}
}
}

View File

@@ -17,7 +17,7 @@ namespace ModernKeePass.Application.Entry.Models
public string Notes { get; set; } public string Notes { get; set; }
public Uri Url { get; set; } public Uri Url { get; set; }
public Dictionary<string, string> AdditionalFields { get; set; } public Dictionary<string, string> AdditionalFields { get; set; }
public IEnumerable<EntryEntity> History { get; set; } public IEnumerable<EntryVm> History { get; set; }
public Icon Icon { get; set; } public Icon Icon { get; set; }
public Color ForegroundColor { get; set; } public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; } public Color BackgroundColor { get; set; }

View File

@@ -0,0 +1,34 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.InsertEntry
{
public class InsertEntryCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public EntryVm Entry { get; set; }
public int Index { get; set; }
public class InsertEntryCommandHandler : IAsyncRequestHandler<InsertEntryCommand>
{
private readonly IDatabaseProxy _database;
public InsertEntryCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(InsertEntryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.InsertEntry(message.ParentGroup.Id, message.Entry.Id, message.Index);
message.ParentGroup.Entries.Insert(message.Index, message.Entry);
}
}
}
}

View File

@@ -5,6 +5,7 @@ using AutoMapper;
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Dtos; using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Entities; using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Domain.Exceptions; using ModernKeePass.Domain.Exceptions;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Cryptography.KeyDerivation;
@@ -45,6 +46,7 @@ namespace ModernKeePass.Infrastructure.KeePass
} }
set { _pwDatabase.RecycleBinUuid = BuildIdFromString(value); } set { _pwDatabase.RecycleBinUuid = BuildIdFromString(value); }
} }
public string CipherId public string CipherId
{ {
get { return _pwDatabase.DataCipherUuid.ToHexString(); } get { return _pwDatabase.DataCipherUuid.ToHexString(); }
@@ -168,6 +170,17 @@ namespace ModernKeePass.Infrastructure.KeePass
parentPwGroup.AddEntry(pwEntry, true); parentPwGroup.AddEntry(pwEntry, true);
}); });
} }
public async Task InsertEntry(string parentGroupId, string entryId, int index)
{
await Task.Run(() =>
{
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
parentPwGroup.Entries.Insert((uint)index, pwEntry);
});
}
public async Task AddGroup(string parentGroupId, string groupId) public async Task AddGroup(string parentGroupId, string groupId)
{ {
await Task.Run(() => await Task.Run(() =>
@@ -197,15 +210,34 @@ namespace ModernKeePass.Infrastructure.KeePass
}); });
} }
public void UpdateEntry(string entryId, string fieldName, string fieldValue) public void UpdateEntry(string entryId, string fieldName, object fieldValue)
{ {
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
pwEntry.Touch(true); pwEntry.Touch(true);
pwEntry.CreateBackup(null); pwEntry.CreateBackup(null);
pwEntry.Strings.Set(EntryFieldMapper.MapFieldToPwDef(fieldName), new ProtectedString(true, fieldValue));
switch (fieldName)
{
case EntryFieldName.Title:
case EntryFieldName.UserName:
case EntryFieldName.Password:
case EntryFieldName.Notes:
case EntryFieldName.Url:
pwEntry.Strings.Set(EntryFieldMapper.MapFieldToPwDef(fieldName), new ProtectedString(true, fieldValue.ToString()));
break;
case EntryFieldName.HasExpirationDate:
pwEntry.Expires = (bool)fieldValue;
break;
case EntryFieldName.ExpirationDate:
pwEntry.ExpiryTime = (DateTime)fieldValue;
break;
case EntryFieldName.Icon:
pwEntry.IconId = IconMapper.MapIconToPwIcon((Icon)fieldValue);
break;
}
} }
public void UpdateGroup(string group) public void UpdateGroup(string groupId)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@@ -237,7 +269,7 @@ namespace ModernKeePass.Infrastructure.KeePass
var id = pwEntry.Uuid; var id = pwEntry.Uuid;
pwEntry.ParentGroup.Entries.Remove(pwEntry); pwEntry.ParentGroup.Entries.Remove(pwEntry);
if (_pwDatabase.RecycleBinEnabled) if (!_pwDatabase.RecycleBinEnabled || pwEntry.ParentGroup.Uuid.Equals(_pwDatabase.RecycleBinUuid))
{ {
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow)); _pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow));
} }
@@ -252,7 +284,7 @@ namespace ModernKeePass.Infrastructure.KeePass
var id = pwGroup.Uuid; var id = pwGroup.Uuid;
pwGroup.ParentGroup.Groups.Remove(pwGroup); pwGroup.ParentGroup.Groups.Remove(pwGroup);
if (_pwDatabase.RecycleBinEnabled) if (!_pwDatabase.RecycleBinEnabled || pwGroup.ParentGroup.Uuid.Equals(_pwDatabase.RecycleBinUuid))
{ {
_pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow)); _pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow));
} }

View File

@@ -1,9 +1,9 @@
using System.Windows.Input; using System.Windows.Input;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Microsoft.Xaml.Interactivity; using Microsoft.Xaml.Interactivity;
using ModernKeePass.Application.Resources.Queries;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
namespace ModernKeePass.Actions namespace ModernKeePass.Actions
@@ -32,19 +32,23 @@ namespace ModernKeePass.Actions
public object Execute(object sender, object parameter) public object Execute(object sender, object parameter)
{ {
var resource = new ResourcesService(); var mediator = App.Mediator;
var type = Entity is GroupVm ? "Group" : "Entry"; var type = Entity is GroupVm ? "Group" : "Entry";
var message = Entity.IsRecycleOnDelete var message = Entity.IsRecycleOnDelete
? resource.GetResourceValue($"{type}RecyclingConfirmation") ? mediator.Send(new GetResourceQuery { Key = $"{type}RecyclingConfirmation" })
: resource.GetResourceValue($"{type}DeletingConfirmation"); : mediator.Send(new GetResourceQuery { Key = $"{type}DeletingConfirmation" });
var text = Entity.IsRecycleOnDelete ? resource.GetResourceValue($"{type}Recycled") : resource.GetResourceValue($"{type}Deleted"); var text = Entity.IsRecycleOnDelete ?
MessageDialogHelper.ShowActionDialog(resource.GetResourceValue("EntityDeleteTitle"), message, mediator.Send(new GetResourceQuery { Key = $"{type}Recycled" }) :
resource.GetResourceValue("EntityDeleteActionButton"), mediator.Send(new GetResourceQuery { Key = $"{type}Deleted" });
resource.GetResourceValue("EntityDeleteCancelButton"), a => MessageDialogHelper.ShowActionDialog(
mediator.Send(new GetResourceQuery { Key = "EntityDeleteTitle" }).GetAwaiter().GetResult(),
message.GetAwaiter().GetResult(),
mediator.Send(new GetResourceQuery { Key = "EntityDeleteActionButton" }).GetAwaiter().GetResult(),
mediator.Send(new GetResourceQuery { Key = "EntityDeleteCancelButton" }).GetAwaiter().GetResult(), async a =>
{ {
ToastNotificationHelper.ShowMovedToast(Entity, resource.GetResourceValue("EntityDeleting"), text); ToastNotificationHelper.ShowMovedToast(Entity, await mediator.Send(new GetResourceQuery { Key = "EntityDeleting" }), await text);
Entity.MarkForDelete(resource.GetResourceValue("RecycleBinTitle")); await Entity.MarkForDelete(await mediator.Send(new GetResourceQuery { Key = "RecycleBinTitle"}));
Command.Execute(null); Command.Execute(null);
}, null).GetAwaiter(); }, null).GetAwaiter();

View File

@@ -13,7 +13,7 @@ namespace ModernKeePass.Common
{ {
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text"); var toastElements = notificationXml.GetElementsByTagName("text");
toastElements[0].AppendChild(notificationXml.CreateTextNode($"{action} {entity.Name}")); toastElements[0].AppendChild(notificationXml.CreateTextNode($"{action} {entity.Title}"));
toastElements[1].AppendChild(notificationXml.CreateTextNode(text)); toastElements[1].AppendChild(notificationXml.CreateTextNode(text));
var toastNode = notificationXml.SelectSingleNode("/toast"); var toastNode = notificationXml.SelectSingleNode("/toast");

View File

@@ -9,9 +9,9 @@ namespace ModernKeePass.Interfaces
{ {
GroupVm ParentGroup { get; } GroupVm ParentGroup { get; }
GroupVm PreviousGroup { get; } GroupVm PreviousGroup { get; }
int IconId { get; } int Icon { get; }
string Id { get; } string Id { get; }
string Name { get; set; } string Title { get; set; }
IEnumerable<IVmEntity> BreadCrumb { get; } IEnumerable<IVmEntity> BreadCrumb { get; }
bool IsEditMode { get; } bool IsEditMode { get; }
bool IsRecycleOnDelete { get; } bool IsRecycleOnDelete { get; }

View File

@@ -14,7 +14,7 @@ namespace ModernKeePass.Services
foreach (var entity in data) foreach (var entity in data)
{ {
var entry = group.AddNewEntry(); var entry = group.AddNewEntry();
entry.Name = entity["0"]; entry.Title = entity["0"];
entry.UserName = entity["1"]; entry.UserName = entity["1"];
entry.Password = entity["2"]; entry.Password = entity["2"];
if (entity.Count > 3) entry.Url = entity["3"]; if (entity.Count > 3) entry.Url = entity["3"];

View File

@@ -1,25 +1,27 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using MediatR;
using ModernKeePass.Application.Database.Commands.SaveDatabase;
using ModernKeePass.Application.Entry.Commands.SetFieldValue;
using ModernKeePass.Application.Group.Commands.DeleteEntry;
using ModernKeePass.Application.Resources.Queries;
using ModernKeePass.Application.Security.Commands.GeneratePassword;
using ModernKeePass.Application.Security.Queries.EstimatePasswordComplexity;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePass.Services;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Security;
using ModernKeePassLib.Cryptography;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
public class EntryVm : INotifyPropertyChanged, IVmEntity, ISelectableModel public class EntryVm : NotifyPropertyChangedBase, IVmEntity, ISelectableModel
{ {
public GroupVm ParentGroup { get; private set; } public GroupVm ParentGroup { get; private set; }
public GroupVm PreviousGroup { get; private set; } public GroupVm PreviousGroup { get; private set; }
public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password); public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password);
public bool HasExpired => HasExpirationDate && _pwEntry.ExpiryTime < DateTime.Now; public bool HasExpired => HasExpirationDate && ExpiryDate < DateTime.Now;
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray()); public double PasswordComplexityIndicator => _mediator.Send(new EstimatePasswordComplexityQuery {Password = Password}).GetAwaiter().GetResult();
public bool UpperCasePatternSelected { get; set; } = true; public bool UpperCasePatternSelected { get; set; } = true;
public bool LowerCasePatternSelected { get; set; } = true; public bool LowerCasePatternSelected { get; set; } = true;
public bool DigitsPatternSelected { get; set; } = true; public bool DigitsPatternSelected { get; set; } = true;
@@ -29,8 +31,7 @@ namespace ModernKeePass.ViewModels
public bool SpecialPatternSelected { get; set; } public bool SpecialPatternSelected { get; set; }
public bool BracketsPatternSelected { get; set; } public bool BracketsPatternSelected { get; set; }
public string CustomChars { get; set; } = string.Empty; public string CustomChars { get; set; } = string.Empty;
public PwUuid IdUuid => _pwEntry?.Uuid; public string Id => _entry.Id;
public string Id => _pwEntry?.Uuid.ToHexString();
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected; public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected;
public IEnumerable<IVmEntity> BreadCrumb => new List<IVmEntity>(ParentGroup.BreadCrumb) {ParentGroup}; public IEnumerable<IVmEntity> BreadCrumb => new List<IVmEntity>(ParentGroup.BreadCrumb) {ParentGroup};
/// <summary> /// <summary>
@@ -44,80 +45,73 @@ namespace ModernKeePass.ViewModels
set set
{ {
_passwordLength = value; _passwordLength = value;
NotifyPropertyChanged("PasswordLength"); OnPropertyChanged();
} }
} }
public string Name public string Title
{ {
get { return GetEntryValue(PwDefs.TitleField); } get { return _entry.Title; }
set { SetEntryValue(PwDefs.TitleField, new ProtectedString(true, value)); } set { _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = nameof(Title), FieldValue = value}); }
} }
public string UserName public string UserName
{ {
get { return GetEntryValue(PwDefs.UserNameField); } get { return _entry.Username; }
set { SetEntryValue(PwDefs.UserNameField, new ProtectedString(true, value)); } set { _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = nameof(UserName), FieldValue = value }); }
} }
public string Password public string Password
{ {
get { return GetEntryValue(PwDefs.PasswordField); } get { return _entry.Password; }
set set
{ {
SetEntryValue(PwDefs.PasswordField, new ProtectedString(true, value)); _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = nameof(Password), FieldValue = value });
NotifyPropertyChanged("Password"); OnPropertyChanged();
NotifyPropertyChanged("PasswordComplexityIndicator"); OnPropertyChanged(nameof(PasswordComplexityIndicator));
} }
} }
public string Url public string Url
{ {
get { return GetEntryValue(PwDefs.UrlField); } get { return _entry.Url.ToString();}
set { SetEntryValue(PwDefs.UrlField, new ProtectedString(true, value)); } set { _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = nameof(Url), FieldValue = value }); }
} }
public string Notes public string Notes
{ {
get { return GetEntryValue(PwDefs.NotesField); } get { return _entry.Notes; }
set { SetEntryValue(PwDefs.NotesField, new ProtectedString(true, value)); } set { _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = nameof(Notes), FieldValue = value }); }
} }
public int IconId public int Icon
{ {
get get
{ {
if (HasExpired) return (int) PwIcon.Expired; if (HasExpired) return (int)Domain.Enums.Icon.ReportHacked;
if (_pwEntry?.IconId != null) return (int) _pwEntry?.IconId; return (int) _entry.Icon;
return -1;
}
set
{
HandleBackup();
_pwEntry.IconId = (PwIcon)value;
} }
set { _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = nameof(Icon), FieldValue = value }); }
} }
public DateTimeOffset ExpiryDate public DateTimeOffset ExpiryDate
{ {
get { return new DateTimeOffset(_pwEntry.ExpiryTime.Date); } get { return _entry.ExpirationDate; }
set set
{ {
if (!HasExpirationDate) return; if (!HasExpirationDate) return;
HandleBackup(); _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = "ExpirationDate", FieldValue = value.Date });
_pwEntry.ExpiryTime = value.DateTime;
} }
} }
public TimeSpan ExpiryTime public TimeSpan ExpiryTime
{ {
get { return _pwEntry.ExpiryTime.TimeOfDay; } get { return _entry.ExpirationDate.TimeOfDay; }
set set
{ {
if (!HasExpirationDate) return; if (!HasExpirationDate) return;
HandleBackup(); _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = "ExpirationDate", FieldValue = ExpiryDate.Date.Add(value) });
_pwEntry.ExpiryTime = _pwEntry.ExpiryTime.Date.Add(value);
} }
} }
@@ -127,7 +121,7 @@ namespace ModernKeePass.ViewModels
set set
{ {
_isEditMode = value; _isEditMode = value;
NotifyPropertyChanged("IsEditMode"); OnPropertyChanged();
} }
} }
@@ -137,7 +131,7 @@ namespace ModernKeePass.ViewModels
set set
{ {
_isVisible = value; _isVisible = value;
NotifyPropertyChanged("IsVisible"); OnPropertyChanged();
} }
} }
@@ -147,16 +141,16 @@ namespace ModernKeePass.ViewModels
set set
{ {
_isRevealPassword = value; _isRevealPassword = value;
NotifyPropertyChanged("IsRevealPassword"); OnPropertyChanged();
} }
} }
public bool HasExpirationDate public bool HasExpirationDate
{ {
get { return _pwEntry.Expires; } get { return _entry.HasExpirationDate; }
set set
{ {
_pwEntry.Expires = value; _mediator.Send(new SetFieldValueCommand {EntryId = Id, FieldName = nameof(HasExpirationDate), FieldValue = value});
NotifyPropertyChanged("HasExpirationDate"); OnPropertyChanged();
} }
} }
@@ -165,7 +159,7 @@ namespace ModernKeePass.ViewModels
get get
{ {
var history = new Stack<EntryVm>(); var history = new Stack<EntryVm>();
foreach (var historyEntry in _pwEntry.History) foreach (var historyEntry in _entry.History)
{ {
history.Push(new EntryVm(historyEntry, ParentGroup) {IsSelected = false}); history.Push(new EntryVm(historyEntry, ParentGroup) {IsSelected = false});
} }
@@ -178,89 +172,68 @@ namespace ModernKeePass.ViewModels
public Color? BackgroundColor public Color? BackgroundColor
{ {
get { return _pwEntry?.BackgroundColor; } get { return _entry?.BackgroundColor; }
set set
{ {
if (value != null) _pwEntry.BackgroundColor = (Color) value; if (value != null) _entry.BackgroundColor = (Color) value;
} }
} }
public Color? ForegroundColor public Color? ForegroundColor
{ {
get { return _pwEntry?.ForegroundColor; } get { return _entry?.ForegroundColor; }
set set
{ {
if (value != null) _pwEntry.ForegroundColor = (Color)value; if (value != null) _entry.ForegroundColor = (Color)value;
} }
} }
public ICommand SaveCommand { get; } public ICommand SaveCommand { get; }
public ICommand GeneratePasswordCommand { get; } public ICommand GeneratePasswordCommand { get; }
public ICommand UndoDeleteCommand { get; } public ICommand UndoDeleteCommand { get; }
public event PropertyChangedEventHandler PropertyChanged; private readonly Application.Entry.Models.EntryVm _entry;
private readonly IMediator _mediator;
private readonly PwEntry _pwEntry;
private readonly IDatabaseService _database;
private readonly IResourceService _resource;
private bool _isEditMode; private bool _isEditMode;
private bool _isDirty = true;
private bool _isRevealPassword; private bool _isRevealPassword;
private double _passwordLength = 25; private double _passwordLength = 25;
private bool _isVisible = true; private bool _isVisible = true;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public EntryVm() { } public EntryVm() { }
internal EntryVm(PwEntry entry, GroupVm parent) : this(entry, parent, DatabaseService.Instance, new ResourcesService()) { } internal EntryVm(Application.Entry.Models.EntryVm entry, GroupVm parent) : this(entry, parent, App.Mediator) { }
public EntryVm(PwEntry entry, GroupVm parent, IDatabaseService database, IResourceService resource) public EntryVm(Application.Entry.Models.EntryVm entry, GroupVm parent, IMediator mediator)
{ {
_database = database; _entry = entry;
_resource = resource; _mediator = mediator;
_pwEntry = entry;
ParentGroup = parent; ParentGroup = parent;
SaveCommand = new RelayCommand(() => _database.Save()); SaveCommand = new RelayCommand(() => _mediator.Send(new SaveDatabaseCommand()));
GeneratePasswordCommand = new RelayCommand(GeneratePassword); GeneratePasswordCommand = new RelayCommand(async () => await GeneratePassword());
UndoDeleteCommand = new RelayCommand(() => Move(PreviousGroup), () => PreviousGroup != null); UndoDeleteCommand = new RelayCommand(() => Move(PreviousGroup), () => PreviousGroup != null);
} }
public void GeneratePassword() public async Task GeneratePassword()
{ {
var pwProfile = new PwProfile Password = await _mediator.Send(new GeneratePasswordCommand
{ {
GeneratorType = PasswordGeneratorType.CharSet, BracketsPatternSelected = BracketsPatternSelected,
Length = (uint)PasswordLength, CustomChars = CustomChars,
CharSet = new PwCharSet() DigitsPatternSelected = DigitsPatternSelected,
}; LowerCasePatternSelected = LowerCasePatternSelected,
MinusPatternSelected = MinusPatternSelected,
if (UpperCasePatternSelected) pwProfile.CharSet.Add(PwCharSet.UpperCase); PasswordLength = (int)PasswordLength,
if (LowerCasePatternSelected) pwProfile.CharSet.Add(PwCharSet.LowerCase); SpacePatternSelected = SpacePatternSelected,
if (DigitsPatternSelected) pwProfile.CharSet.Add(PwCharSet.Digits); SpecialPatternSelected = SpecialPatternSelected,
if (SpecialPatternSelected) pwProfile.CharSet.Add(PwCharSet.Special); UnderscorePatternSelected = UnderscorePatternSelected,
if (MinusPatternSelected) pwProfile.CharSet.Add('-'); UpperCasePatternSelected = UpperCasePatternSelected
if (UnderscorePatternSelected) pwProfile.CharSet.Add('_'); });
if (SpacePatternSelected) pwProfile.CharSet.Add(' '); OnPropertyChanged(nameof(IsRevealPasswordEnabled));
if (BracketsPatternSelected) pwProfile.CharSet.Add(PwCharSet.Brackets);
pwProfile.CharSet.Add(CustomChars);
ProtectedString password;
PwGenerator.Generate(out password, pwProfile, null, new CustomPwGeneratorPool());
SetEntryValue(PwDefs.PasswordField, password);
NotifyPropertyChanged("Password");
NotifyPropertyChanged("IsRevealPasswordEnabled");
NotifyPropertyChanged("PasswordComplexityIndicator");
} }
public void MarkForDelete(string recycleBinTitle) public Task MarkForDelete(string recycleBinTitle)
{ {
if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null) if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
_database.CreateRecycleBin(recycleBinTitle); _database.CreateRecycleBin(recycleBinTitle);
@@ -280,43 +253,21 @@ namespace ModernKeePass.ViewModels
ParentGroup.Entries.Add(this); ParentGroup.Entries.Add(this);
} }
public void CommitDelete() public async Task CommitDelete()
{ {
_pwEntry.ParentGroup.Entries.Remove(_pwEntry); await _mediator.Send(new DeleteEntryCommand {Entry = _entry});
if (!_database.RecycleBinEnabled || PreviousGroup.IsSelected) _database.AddDeletedItem(IdUuid);
} }
public PwEntry GetPwEntry() public Application.Entry.Models.EntryVm GetEntry()
{ {
return _pwEntry; return _entry;
}
public void Reset()
{
_isDirty = false;
} }
public override string ToString() public override string ToString()
{ {
return IsSelected ? _resource.GetResourceValue("EntryCurrent") : _pwEntry.LastModificationTime.ToString("g"); return IsSelected ?
} _mediator.Send(new GetResourceQuery{Key = "EntryCurrent"}).GetAwaiter().GetResult() :
_entry.ModificationDate.ToString("g");
private void HandleBackup()
{
if (_isDirty) return;
_pwEntry?.Touch(true);
_pwEntry?.CreateBackup(null);
_isDirty = true;
}
private string GetEntryValue(string key)
{
return _pwEntry?.Strings.GetSafe(key).ReadString();
}
private void SetEntryValue(string key, ProtectedString newValue)
{
HandleBackup();
_pwEntry?.Strings.Set(key, newValue);
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
@@ -8,10 +7,17 @@ using System.Windows.Input;
using MediatR; using MediatR;
using ModernKeePass.Application.Database.Commands.SaveDatabase; using ModernKeePass.Application.Database.Commands.SaveDatabase;
using ModernKeePass.Application.Database.Queries.GetDatabase; using ModernKeePass.Application.Database.Queries.GetDatabase;
using ModernKeePass.Application.Group.Commands.AddEntry;
using ModernKeePass.Application.Group.Commands.CreateEntry;
using ModernKeePass.Application.Group.Commands.CreateGroup;
using ModernKeePass.Application.Group.Commands.DeleteGroup;
using ModernKeePass.Application.Group.Commands.InsertEntry;
using ModernKeePass.Application.Group.Commands.RemoveEntry;
using ModernKeePass.Application.Group.Commands.SortEntries;
using ModernKeePass.Application.Group.Commands.SortGroups;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Domain.Enums; using ModernKeePass.Domain.Enums;
using ModernKeePass.Interfaces; using ModernKeePass.Interfaces;
using ModernKeePassLib;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
@@ -63,17 +69,17 @@ namespace ModernKeePass.ViewModels
} }
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
group e by e.Name.ToUpper().FirstOrDefault() into grp group e by e.Title.ToUpper().FirstOrDefault() into grp
orderby grp.Key orderby grp.Key
select grp; select grp;
public string Name public string Title
{ {
get { return _group == null ? string.Empty : _group.Title; } get { return _group == null ? string.Empty : _group.Title; }
set { _group.Title = value; } set { _group.Title = value; }
} }
public int IconId public int Icon
{ {
get get
{ {
@@ -152,38 +158,44 @@ namespace ModernKeePass.ViewModels
Groups = new ObservableCollection<GroupVm>(group.SubGroups.Select(g => new GroupVm(g, this, recycleBinId))); Groups = new ObservableCollection<GroupVm>(group.SubGroups.Select(g => new GroupVm(g, this, recycleBinId)));
} }
private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) private async void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
var oldIndex = (uint) e.OldStartingIndex; var oldIndex = e.OldStartingIndex;
_reorderedEntry = _group.Entries.GetAt(oldIndex); _reorderedEntry = _group.Entries[oldIndex];
_group.Entries.RemoveAt(oldIndex); await _mediator.Send(new RemoveEntryCommand {Entry = _reorderedEntry, ParentGroup = _group});
break; break;
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
if (_reorderedEntry == null) _group.AddEntry(((EntryVm) e.NewItems[0]).GetPwEntry(), true); if (_reorderedEntry == null)
else _group.Entries.Insert((uint)e.NewStartingIndex, _reorderedEntry); {
var entry = ((EntryVm) e.NewItems[0]).GetEntry();
await _mediator.Send(new AddEntryCommand {Entry = entry, ParentGroup = _group});
}
else
{
await _mediator.Send(new InsertEntryCommand {Entry = _reorderedEntry, ParentGroup = _group, Index = e.NewStartingIndex});
}
break; break;
} }
} }
public GroupVm AddNewGroup(string name = "") public async Task<GroupVm> AddNewGroup(string name = "")
{ {
var pwGroup = new PwGroup(true, true, name, PwIcon.Folder); var newGroup = await _mediator.Send(new CreateGroupCommand {Name = name, ParentGroup = _group});
_group.AddGroup(pwGroup, true); var newGroupVm = new GroupVm(newGroup, this) {Title = name, IsEditMode = string.IsNullOrEmpty(name)};
var newGroup = new GroupVm(pwGroup, this) {Name = name, IsEditMode = string.IsNullOrEmpty(name)}; Groups.Add(newGroupVm);
Groups.Add(newGroup); return newGroupVm;
return newGroup;
} }
public EntryVm AddNewEntry() public async Task<EntryVm> AddNewEntry()
{ {
var pwEntry = new PwEntry(true, true); var newEntry = await _mediator.Send(new CreateEntryCommand { ParentGroup = _group });
var newEntry = new EntryVm(pwEntry, this) {IsEditMode = true}; var newEntryVm = new EntryVm(newEntry, this) {IsEditMode = true};
newEntry.GeneratePassword(); await newEntryVm.GeneratePassword();
Entries.Add(newEntry); Entries.Add(newEntryVm);
return newEntry; return newEntryVm;
} }
public async Task MarkForDelete(string recycleBinTitle) public async Task MarkForDelete(string recycleBinTitle)
@@ -217,42 +229,26 @@ namespace ModernKeePass.ViewModels
public async Task CommitDelete() public async Task CommitDelete()
{ {
_group.ParentGroup.Groups.Remove(_group); await _mediator.Send(new DeleteGroupCommand { Group = _group });
if (await IsRecycleBinEnabled() && !PreviousGroup.IsSelected) _database.RecycleBin._group.AddGroup(_group, true);
else _database.AddDeletedItem(IdUuid);
} }
public override string ToString() public override string ToString()
{ {
return Name; return Title;
} }
private async Task SortEntriesAsync() private async Task SortEntriesAsync()
{ {
var comparer = new PwEntryComparer(PwDefs.TitleField, true, false); await _mediator.Send(new SortEntriesCommand {Group = _group});
try Entries = new ObservableCollection<EntryVm>(Entries.OrderBy(e => e.Title));
{
_group.Entries.Sort(comparer);
Entries = new ObservableCollection<EntryVm>(Entries.OrderBy(e => e.Name));
}
catch (Exception e)
{
await MessageDialogHelper.ShowErrorDialog(e);
}
} }
private async Task SortGroupsAsync() private async Task SortGroupsAsync()
{ {
try await _mediator.Send(new SortGroupsCommand {Group = _group});
{ Groups = new ObservableCollection<GroupVm>(Groups.OrderBy(g => g.Title).ThenBy(g => g._group == null));
_group.SortSubGroups(false); // TODO: should not be needed
Groups = new ObservableCollection<GroupVm>(Groups.OrderBy(g => g.Name).ThenBy(g => g._group == null)); OnPropertyChanged(nameof(Groups));
OnPropertyChanged("Groups");
}
catch (Exception e)
{
await MessageDialogHelper.ShowErrorDialog(e);
}
} }
private async Task<bool> IsRecycleBinEnabled() private async Task<bool> IsRecycleBinEnabled()

View File

@@ -41,23 +41,23 @@ namespace ModernKeePass.ViewModels
var converter = new IntToSymbolConverter(); var converter = new IntToSymbolConverter();
var bankingGroup = group.AddNewGroup("Banking"); var bankingGroup = group.AddNewGroup("Banking");
bankingGroup.IconId = (int)converter.ConvertBack(Symbol.Calculator, null, null, string.Empty); bankingGroup.Icon = (int)converter.ConvertBack(Symbol.Calculator, null, null, string.Empty);
var emailGroup = group.AddNewGroup("Email"); var emailGroup = group.AddNewGroup("Email");
emailGroup.IconId = (int)converter.ConvertBack(Symbol.Mail, null, null, string.Empty); emailGroup.Icon = (int)converter.ConvertBack(Symbol.Mail, null, null, string.Empty);
var internetGroup = group.AddNewGroup("Internet"); var internetGroup = group.AddNewGroup("Internet");
internetGroup.IconId = (int)converter.ConvertBack(Symbol.World, null, null, string.Empty); internetGroup.Icon = (int)converter.ConvertBack(Symbol.World, null, null, string.Empty);
var sample1 = group.AddNewEntry(); var sample1 = group.AddNewEntry();
sample1.Name = "Sample Entry"; sample1.Title = "Sample Entry";
sample1.UserName = "Username"; sample1.UserName = "Username";
sample1.Url = PwDefs.HomepageUrl; sample1.Url = PwDefs.HomepageUrl;
sample1.Password = "Password"; sample1.Password = "Password";
sample1.Notes = "You may safely delete this sample"; sample1.Notes = "You may safely delete this sample";
var sample2 = group.AddNewEntry(); var sample2 = group.AddNewEntry();
sample2.Name = "Sample Entry #2"; sample2.Title = "Sample Entry #2";
sample2.UserName = "Michael321"; sample2.UserName = "Michael321";
sample2.Url = PwDefs.HelpUrl + "kb/testform.html"; sample2.Url = PwDefs.HelpUrl + "kb/testform.html";
sample2.Password = "12345"; sample2.Password = "12345";

View File

@@ -415,7 +415,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick"> <core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding UserName}" /> <actions:ClipboardAction Text="{Binding UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Name}" /> <actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</local:TextBoxWithButton> </local:TextBoxWithButton>
@@ -425,7 +425,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick"> <core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding Password}" /> <actions:ClipboardAction Text="{Binding Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Name}" /> <actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</local:TextBoxWithButton> </local:TextBoxWithButton>
@@ -498,13 +498,13 @@
<RowDefinition Height="20" /> <RowDefinition Height="20" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}"> <Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<userControls:SymbolPickerUserControl Width="80" Height="40" SelectedSymbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0, Mode=TwoWay}" /> <userControls:SymbolPickerUserControl Width="80" Height="40" SelectedSymbol="{Binding Icon, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0, Mode=TwoWay}" />
</Viewbox> </Viewbox>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}"> <Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}}" Width="80" Height="40" /> <SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IntToSymbolConverter}}" Width="80" Height="40" />
</Viewbox> </Viewbox>
<TextBox Grid.Column="1" Grid.Row="0" <TextBox Grid.Column="1" Grid.Row="0"
Text="{Binding Name, Mode=TwoWay}" Text="{Binding Title, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}" Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
Background="Transparent" Background="Transparent"
SelectionHighlightColor="{StaticResource MainColor}" SelectionHighlightColor="{StaticResource MainColor}"
@@ -539,7 +539,7 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="RestoreButtonClick"> <core:EventTriggerBehavior EventName="RestoreButtonClick">
<core:InvokeCommandAction Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" /> <core:InvokeCommandAction Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" />
<actions:ToastAction x:Uid="RestoreEntryCommand" Title="{Binding Name}" /> <actions:ToastAction x:Uid="RestoreEntryCommand" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</userControls:TopMenuUserControl> </userControls:TopMenuUserControl>

View File

@@ -49,7 +49,6 @@ namespace ModernKeePass.Views
protected override void OnNavigatedFrom(NavigationEventArgs e) protected override void OnNavigatedFrom(NavigationEventArgs e)
{ {
NavigationHelper.OnNavigatedFrom(e); NavigationHelper.OnNavigatedFrom(e);
Model.Reset();
} }
#endregion #endregion

View File

@@ -105,11 +105,11 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="{Binding BackgroundColor, ConverterParameter={StaticResource MainColor}, Converter={StaticResource ColorToBrushConverter}}"> <Border Grid.Column="0" Background="{Binding BackgroundColor, ConverterParameter={StaticResource MainColor}, Converter={StaticResource ColorToBrushConverter}}">
<Viewbox MaxHeight="50" Width="100"> <Viewbox MaxHeight="50" Width="100">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0}" Foreground="{StaticResource TextColor}" /> <SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0}" Foreground="{StaticResource TextColor}" />
</Viewbox> </Viewbox>
</Border> </Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" > <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/> <TextBlock x:Name="NameTextBlock" Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" /> <TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" />
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" /> <TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" /> <TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" />
@@ -122,7 +122,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding UserName}" /> <actions:ClipboardAction Text="{Binding UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Name}" /> <actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
@@ -130,7 +130,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding Password}" /> <actions:ClipboardAction Text="{Binding Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Name}" /> <actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
@@ -138,7 +138,7 @@
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<actions:NavigateToUrlAction Url="{Binding Url}" /> <actions:NavigateToUrlAction Url="{Binding Url}" />
<actions:ToastAction x:Uid="ToastCopyUrl" Title="{Binding Name}" /> <actions:ToastAction x:Uid="ToastCopyUrl" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
@@ -159,7 +159,7 @@
<GridView.ItemTemplate> <GridView.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock Width="100" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/> <TextBlock Width="100" Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</GridView.ItemTemplate> </GridView.ItemTemplate>
@@ -207,14 +207,14 @@
<RowDefinition Height="20" /> <RowDefinition Height="20" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}"> <Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<userControls:SymbolPickerUserControl Width="80" Height="40" SelectedSymbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=48, Mode=TwoWay}" /> <userControls:SymbolPickerUserControl Width="80" Height="40" SelectedSymbol="{Binding Icon, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=48, Mode=TwoWay}" />
</Viewbox> </Viewbox>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}"> <Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}}" Width="80" Height="40" /> <SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IntToSymbolConverter}}" Width="80" Height="40" />
</Viewbox> </Viewbox>
<TextBox Grid.Column="1" Grid.Row="0" <TextBox Grid.Column="1" Grid.Row="0"
x:Name="TitleTextBox" x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}" Text="{Binding Title, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}" Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
SelectionHighlightColor="{StaticResource MainColor}" SelectionHighlightColor="{StaticResource MainColor}"
Background="Transparent" Background="Transparent"
@@ -252,7 +252,7 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="RestoreButtonClick"> <core:EventTriggerBehavior EventName="RestoreButtonClick">
<core:InvokeCommandAction Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" /> <core:InvokeCommandAction Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" />
<actions:ToastAction x:Uid="RestoreGroupCommand" Title="{Binding Name}" /> <actions:ToastAction x:Uid="RestoreGroupCommand" Title="{Binding Title}" />
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</userControls:TopMenuUserControl> </userControls:TopMenuUserControl>

View File

@@ -122,10 +122,10 @@ namespace ModernKeePass.Views
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args) private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{ {
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appdata://Assets/ModernKeePass-SmallLogo.scale-80.png")); var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appdata://Assets/ModernKeePass-SmallLogo.scale-80.png"));
var results = Model.SubEntries.Where(e => e.Name.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5); var results = Model.SubEntries.Where(e => e.Title.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
foreach (var result in results) foreach (var result in results)
{ {
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Name, result.ParentGroup.Name, result.Id, imageUri, string.Empty); args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Title, result.ParentGroup.Title, result.Id, imageUri, string.Empty);
} }
} }

View File

@@ -118,7 +118,7 @@ namespace ModernKeePassApp.Test
var database = new DatabaseServiceMock(); var database = new DatabaseServiceMock();
var entryVm = new EntryVm(new PwEntry(true, true), new GroupVm(), database, _resource) var entryVm = new EntryVm(new PwEntry(true, true), new GroupVm(), database, _resource)
{ {
Name = "Test", Title = "Test",
UserName = "login", UserName = "login",
Password = "password" Password = "password"
}; };
@@ -130,7 +130,7 @@ namespace ModernKeePassApp.Test
var database = new DatabaseServiceMock(); var database = new DatabaseServiceMock();
var entryVm = new GroupVm(new PwGroup(true, true), new GroupVm(), database) var entryVm = new GroupVm(new PwGroup(true, true), new GroupVm(), database)
{ {
Name = "Test" Title = "Test"
}; };
} }
} }