mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-03 15:40:18 -04:00

No need to click twice on history menu Skipped first history entry as it is the same as the current entry Stored SaveException innerexception as it is read more than once
323 lines
12 KiB
C#
323 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using Windows.UI.Xaml.Controls;
|
|
using Windows.UI.Xaml.Media;
|
|
using MediatR;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using ModernKeePass.Application.Database.Commands.SaveDatabase;
|
|
using ModernKeePass.Application.Database.Models;
|
|
using ModernKeePass.Application.Database.Queries.GetDatabase;
|
|
using ModernKeePass.Application.Entry.Commands.AddHistory;
|
|
using ModernKeePass.Application.Entry.Commands.DeleteHistory;
|
|
using ModernKeePass.Application.Entry.Commands.RestoreHistory;
|
|
using ModernKeePass.Application.Entry.Commands.SetFieldValue;
|
|
using ModernKeePass.Application.Entry.Models;
|
|
using ModernKeePass.Application.Entry.Queries.GetEntry;
|
|
using ModernKeePass.Application.Group.Commands.AddEntry;
|
|
using ModernKeePass.Application.Group.Commands.DeleteEntry;
|
|
using ModernKeePass.Application.Group.Commands.RemoveEntry;
|
|
using ModernKeePass.Application.Group.Queries.GetGroup;
|
|
using ModernKeePass.Application.Security.Commands.GeneratePassword;
|
|
using ModernKeePass.Application.Security.Queries.EstimatePasswordComplexity;
|
|
using ModernKeePass.Common;
|
|
using ModernKeePass.Domain.Enums;
|
|
using ModernKeePass.Interfaces;
|
|
using ModernKeePass.Application.Group.Models;
|
|
using ModernKeePass.Domain.AOP;
|
|
using ModernKeePass.Extensions;
|
|
|
|
namespace ModernKeePass.ViewModels
|
|
{
|
|
public class EntryDetailVm : NotifyPropertyChangedBase, IVmEntity
|
|
{
|
|
public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password);
|
|
public bool HasExpired => HasExpirationDate && ExpiryDate < DateTime.Now;
|
|
public double PasswordComplexityIndicator => _mediator.Send(new EstimatePasswordComplexityQuery {Password = Password}).GetAwaiter().GetResult();
|
|
public bool UpperCasePatternSelected { get; set; } = true;
|
|
public bool LowerCasePatternSelected { get; set; } = true;
|
|
public bool DigitsPatternSelected { get; set; } = true;
|
|
public bool MinusPatternSelected { get; set; }
|
|
public bool UnderscorePatternSelected { get; set; }
|
|
public bool SpacePatternSelected { get; set; }
|
|
public bool SpecialPatternSelected { get; set; }
|
|
public bool BracketsPatternSelected { get; set; }
|
|
public string CustomChars { get; set; } = string.Empty;
|
|
public string Id => SelectedItem.Id;
|
|
|
|
public bool IsRecycleOnDelete
|
|
{
|
|
get
|
|
{
|
|
var database = Database;
|
|
return database.IsRecycleBinEnabled && _parent.Id != database.RecycleBinId;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<GroupVm> BreadCrumb => new List<GroupVm> { _parent };
|
|
public ObservableCollection<EntryVm> History { get; }
|
|
|
|
/// <summary>
|
|
/// Determines if the Entry is current or from history
|
|
/// </summary>
|
|
public bool IsCurrentEntry => SelectedIndex == 0;
|
|
|
|
public EntryVm SelectedItem
|
|
{
|
|
get { return _selectedItem; }
|
|
set
|
|
{
|
|
SetProperty(ref _selectedItem, value);
|
|
if (value != null) OnPropertyChanged();
|
|
}
|
|
}
|
|
public int SelectedIndex
|
|
{
|
|
get { return _selectedIndex; }
|
|
set
|
|
{
|
|
SetProperty(ref _selectedIndex, value);
|
|
OnPropertyChanged(nameof(IsCurrentEntry));
|
|
}
|
|
}
|
|
|
|
public double PasswordLength
|
|
{
|
|
get { return _passwordLength; }
|
|
set { SetProperty(ref _passwordLength, value); }
|
|
}
|
|
|
|
public string Title
|
|
{
|
|
get { return SelectedItem.Title; }
|
|
set
|
|
{
|
|
SelectedItem.Title = value;
|
|
SetFieldValue(nameof(Title), value).Wait();
|
|
}
|
|
}
|
|
|
|
public string UserName
|
|
{
|
|
get { return SelectedItem.Username; }
|
|
set { SelectedItem.Username = value; }
|
|
}
|
|
|
|
public string Password
|
|
{
|
|
get { return SelectedItem.Password; }
|
|
set
|
|
{
|
|
SelectedItem.Password = value;
|
|
SetFieldValue(nameof(Password), value).Wait();
|
|
OnPropertyChanged(nameof(Password));
|
|
OnPropertyChanged(nameof(PasswordComplexityIndicator));
|
|
}
|
|
}
|
|
|
|
public string Url
|
|
{
|
|
get { return SelectedItem.Url?.ToString(); }
|
|
set
|
|
{
|
|
SelectedItem.Url = new Uri(value);
|
|
SetFieldValue(nameof(Url), value).Wait();
|
|
}
|
|
}
|
|
|
|
public string Notes
|
|
{
|
|
get { return SelectedItem.Notes; }
|
|
set
|
|
{
|
|
SelectedItem.Notes = value;
|
|
SetFieldValue(nameof(Notes), value).Wait();
|
|
}
|
|
}
|
|
|
|
public Symbol Icon
|
|
{
|
|
get { return (Symbol)Enum.Parse(typeof(Symbol), SelectedItem.Icon.ToString()); }
|
|
set
|
|
{
|
|
SelectedItem.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString());
|
|
SetFieldValue(nameof(Icon), SelectedItem.Icon).Wait();
|
|
}
|
|
}
|
|
|
|
public DateTimeOffset ExpiryDate
|
|
{
|
|
get { return SelectedItem.ExpirationDate; }
|
|
set
|
|
{
|
|
if (!HasExpirationDate) return;
|
|
|
|
SelectedItem.ExpirationDate = value.Date;
|
|
SetFieldValue("ExpirationDate", SelectedItem.ExpirationDate).Wait();
|
|
}
|
|
}
|
|
|
|
public TimeSpan ExpiryTime
|
|
{
|
|
get { return SelectedItem.ExpirationDate.TimeOfDay; }
|
|
set
|
|
{
|
|
if (!HasExpirationDate) return;
|
|
|
|
SelectedItem.ExpirationDate = SelectedItem.ExpirationDate.Date.Add(value);
|
|
SetFieldValue("ExpirationDate", SelectedItem.ExpirationDate).Wait();
|
|
}
|
|
}
|
|
|
|
public bool HasExpirationDate
|
|
{
|
|
get { return SelectedItem.HasExpirationDate; }
|
|
set
|
|
{
|
|
SelectedItem.HasExpirationDate = value;
|
|
SetFieldValue(nameof(HasExpirationDate), value).Wait();
|
|
OnPropertyChanged(nameof(HasExpirationDate));
|
|
}
|
|
}
|
|
|
|
public SolidColorBrush BackgroundColor
|
|
{
|
|
get { return SelectedItem?.BackgroundColor.ToSolidColorBrush(); }
|
|
set
|
|
{
|
|
SelectedItem.BackgroundColor = value.ToColor();
|
|
SetFieldValue(nameof(BackgroundColor), SelectedItem.BackgroundColor).Wait();
|
|
}
|
|
}
|
|
|
|
public SolidColorBrush ForegroundColor
|
|
{
|
|
get { return SelectedItem?.ForegroundColor.ToSolidColorBrush(); }
|
|
set
|
|
{
|
|
SelectedItem.ForegroundColor = value.ToColor();
|
|
SetFieldValue(nameof(ForegroundColor), SelectedItem.ForegroundColor).Wait();
|
|
}
|
|
}
|
|
|
|
|
|
public bool IsEditMode
|
|
{
|
|
get { return IsCurrentEntry && _isEditMode; }
|
|
set { SetProperty(ref _isEditMode, value); }
|
|
}
|
|
|
|
public bool IsRevealPassword
|
|
{
|
|
get { return _isRevealPassword; }
|
|
set { SetProperty(ref _isRevealPassword, value); }
|
|
}
|
|
|
|
public ICommand SaveCommand { get; }
|
|
public ICommand GeneratePasswordCommand { get; }
|
|
public ICommand MoveCommand { get; }
|
|
public ICommand RestoreCommand { get; }
|
|
|
|
private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult();
|
|
|
|
private readonly IMediator _mediator;
|
|
private readonly GroupVm _parent;
|
|
private EntryVm _selectedItem;
|
|
private int _selectedIndex;
|
|
private bool _isEditMode;
|
|
private bool _isRevealPassword;
|
|
private double _passwordLength = 25;
|
|
private bool _isDirty;
|
|
|
|
public EntryDetailVm() { }
|
|
|
|
internal EntryDetailVm(string entryId) : this(entryId, App.Services.GetRequiredService<IMediator>()) { }
|
|
|
|
public EntryDetailVm(string entryId, IMediator mediator)
|
|
{
|
|
_mediator = mediator;
|
|
SelectedItem = _mediator.Send(new GetEntryQuery { Id = entryId }).GetAwaiter().GetResult();
|
|
_parent = _mediator.Send(new GetGroupQuery { Id = SelectedItem.ParentGroupId }).GetAwaiter().GetResult();
|
|
History = new ObservableCollection<EntryVm> { SelectedItem };
|
|
foreach (var entry in SelectedItem.History.Skip(1))
|
|
{
|
|
History.Add(entry);
|
|
}
|
|
SelectedIndex = 0;
|
|
|
|
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);
|
|
GeneratePasswordCommand = new RelayCommand(async () => await GeneratePassword());
|
|
MoveCommand = new RelayCommand(async () => await Move(_parent), () => _parent != null);
|
|
RestoreCommand = new RelayCommand(async () => await RestoreHistory());
|
|
}
|
|
|
|
public async Task GeneratePassword()
|
|
{
|
|
Password = await _mediator.Send(new GeneratePasswordCommand
|
|
{
|
|
BracketsPatternSelected = BracketsPatternSelected,
|
|
CustomChars = CustomChars,
|
|
DigitsPatternSelected = DigitsPatternSelected,
|
|
LowerCasePatternSelected = LowerCasePatternSelected,
|
|
MinusPatternSelected = MinusPatternSelected,
|
|
PasswordLength = (int)PasswordLength,
|
|
SpacePatternSelected = SpacePatternSelected,
|
|
SpecialPatternSelected = SpecialPatternSelected,
|
|
UnderscorePatternSelected = UnderscorePatternSelected,
|
|
UpperCasePatternSelected = UpperCasePatternSelected
|
|
});
|
|
OnPropertyChanged(nameof(IsRevealPasswordEnabled));
|
|
}
|
|
|
|
public async Task MarkForDelete(string recycleBinTitle)
|
|
{
|
|
await _mediator.Send(new DeleteEntryCommand {EntryId = Id, ParentGroupId = SelectedItem.ParentGroupId, RecycleBinName = recycleBinTitle});
|
|
}
|
|
|
|
public async Task Move(GroupVm destination)
|
|
{
|
|
await _mediator.Send(new AddEntryCommand { ParentGroup = destination, Entry = SelectedItem });
|
|
await _mediator.Send(new RemoveEntryCommand { ParentGroup = _parent, Entry = SelectedItem });
|
|
}
|
|
|
|
public async Task SetFieldValue(string fieldName, object value)
|
|
{
|
|
await _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = fieldName, FieldValue = value });
|
|
((RelayCommand)SaveCommand).RaiseCanExecuteChanged();
|
|
_isDirty = true;
|
|
}
|
|
|
|
public async Task AddHistory()
|
|
{
|
|
if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] });
|
|
}
|
|
|
|
private async Task RestoreHistory()
|
|
{
|
|
await _mediator.Send(new RestoreHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex });
|
|
History.Insert(0, SelectedItem);
|
|
SelectedIndex = 0;
|
|
((RelayCommand)SaveCommand).RaiseCanExecuteChanged();
|
|
}
|
|
|
|
public async Task DeleteHistory()
|
|
{
|
|
await _mediator.Send(new DeleteHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex });
|
|
History.RemoveAt(SelectedIndex);
|
|
SelectedIndex = 0;
|
|
((RelayCommand)SaveCommand).RaiseCanExecuteChanged();
|
|
}
|
|
|
|
private async Task SaveChanges()
|
|
{
|
|
await AddHistory();
|
|
await _mediator.Send(new SaveDatabaseCommand());
|
|
((RelayCommand)SaveCommand).RaiseCanExecuteChanged();
|
|
_isDirty = false;
|
|
}
|
|
}
|
|
}
|