Better global exception handling

Save error now shows a Save as button
Recent list now not changed at every access (only on actual file open)
Some code refactoring
This commit is contained in:
BONNEVILLE Geoffroy
2017-11-24 12:17:41 +01:00
parent 1b2d25e171
commit 7cd05cb1d8
11 changed files with 99 additions and 30 deletions

View File

@@ -9,6 +9,7 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces;
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
@@ -37,14 +38,17 @@ namespace ModernKeePass
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
// TODO: catch only save errors for now, rethrow (do not handle) otherwise
// Save the argument exception because it's cleared on first access
var exception = unhandledExceptionEventArgs.Exception;
MessageDialogHelper.ShowErrorDialog(
var realException =
exception is TargetInvocationException &&
exception.InnerException != null
? exception.InnerException
: exception);
: exception;
if (!(realException is SaveException)) return;
unhandledExceptionEventArgs.Handled = true;
MessageDialogHelper.SaveErrorDialog(realException as SaveException, Database);
}
/// <summary>

View File

@@ -1,6 +1,8 @@
using System;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces;
using ModernKeePass.ViewModels;
using ModernKeePassLib;
@@ -102,7 +104,7 @@ namespace ModernKeePass.Common
{
Status = (int)DatabaseStatus.CompositeKeyError;
}
catch (Exception ex)
catch (Exception)
{
Status = (int)DatabaseStatus.Error;
throw;
@@ -115,9 +117,21 @@ namespace ModernKeePass.Common
/// <param name="file">The new database file</param>
public void Save(StorageFile file)
{
var oldFile = DatabaseFile;
DatabaseFile = file;
_pwDatabase.SaveAs(IOConnectionInfo.FromFile(DatabaseFile), true, new NullStatusLogger());
Status = (int)DatabaseStatus.Opened;
try
{
_pwDatabase.SaveAs(IOConnectionInfo.FromFile(DatabaseFile), true, new NullStatusLogger());
}
catch
{
DatabaseFile = oldFile;
throw;
}
finally
{
Status = (int)DatabaseStatus.Opened;
}
}
/// <summary>
@@ -126,7 +140,14 @@ namespace ModernKeePass.Common
public void Save()
{
if (_pwDatabase == null || !_pwDatabase.IsOpen) return;
_pwDatabase.Save(new NullStatusLogger());
try
{
_pwDatabase.Save(new NullStatusLogger());
}
catch (Exception e)
{
throw new SaveException(e);
}
}
/// <summary>
@@ -145,7 +166,7 @@ namespace ModernKeePass.Common
public void CreateRecycleBin()
{
RecycleBin = ((GroupVm)RootGroup).AddNewGroup("Recycle bin");
RecycleBin = RootGroup.AddNewGroup("Recycle bin");
RecycleBin.IsSelected = true;
RecycleBin.IconSymbol = Symbol.Delete;
}

View File

@@ -1,18 +1,22 @@
using System;
using System.Collections.Generic;
using Windows.Storage.Pickers;
using Windows.UI.Popups;
using ModernKeePass.Exceptions;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Common
{
public static class MessageDialogHelper
{
public static async void ShowDeleteConfirmationDialog(string actionText, string contentText, UICommandInvokedHandler action)
public static async void ShowActionDialog(string title, string contentText, string actionButtonText, string cancelButtonText, UICommandInvokedHandler action)
{
// Create the message dialog and set its content
var messageDialog = new MessageDialog(contentText);
var messageDialog = new MessageDialog(contentText, title);
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand(actionText, action));
messageDialog.Commands.Add(new UICommand("Cancel"));
messageDialog.Commands.Add(new UICommand(actionButtonText, action));
messageDialog.Commands.Add(new UICommand(cancelButtonText));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 1;
@@ -24,6 +28,22 @@ namespace ModernKeePass.Common
await messageDialog.ShowAsync();
}
public static void SaveErrorDialog(SaveException exception, IDatabase database)
{
ShowActionDialog("Save error", exception.InnerException.Message, "Save as", "Discard", async command =>
{
var savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = $"{database.DatabaseFile.DisplayName} - copy"
};
savePicker.FileTypeChoices.Add("KeePass 2.x database", new List<string> { ".kdbx" });
var file = await savePicker.PickSaveFileAsync();
if (file != null) database.Save(file);
});
}
public static async void ShowErrorDialog(Exception exception)
{
if (exception == null) return;

View File

@@ -0,0 +1,14 @@
using System;
namespace ModernKeePass.Exceptions
{
public class SaveException : Exception
{
public new Exception InnerException { get; }
public SaveException(Exception exception)
{
InnerException = exception;
}
}
}

View File

@@ -123,6 +123,7 @@
<Compile Include="Common\ToastNotificationHelper.cs" />
<Compile Include="Converters\DiscreteIntToSolidColorBrushConverter.cs" />
<Compile Include="Converters\NullToBooleanConverter.cs" />
<Compile Include="Exceptions\SaveException.cs" />
<Compile Include="Interfaces\IDatabase.cs" />
<Compile Include="Interfaces\IHasSelectableObject.cs" />
<Compile Include="Interfaces\ISelectableModel.cs" />

View File

@@ -76,7 +76,7 @@ namespace ModernKeePass.Pages
? "Are you sure you want to send this entry to the recycle bin?"
: "Are you sure you want to delete this entry?";
var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed";
MessageDialogHelper.ShowDeleteConfirmationDialog("Delete", message, a =>
MessageDialogHelper.ShowActionDialog("Warning", message, "Delete", "Cancel", a =>
{
ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text);
Model.MarkForDelete();

View File

@@ -116,7 +116,7 @@ namespace ModernKeePass.Pages
? "Are you sure you want to send the whole group and all its entries to the recycle bin?"
: "Are you sure you want to delete the whole group and all its entries?";
var text = isRecycleBinEnabled ? "Item moved to the Recycle bin" : "Item permanently removed";
MessageDialogHelper.ShowDeleteConfirmationDialog("Delete", message, a =>
MessageDialogHelper.ShowActionDialog("Warning", message, "Delete", "Cancel", a =>
{
ToastNotificationHelper.ShowMovedToast(Model, "Deleting", text);
Model.MarkForDelete();

View File

@@ -34,9 +34,13 @@
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" Padding="5,0,0,0" />
<TextBlock Grid.Row="1" Text="{Binding Path}" Padding="5,0,0,0" FontSize="10" />
<local:CompositeKeyUserControl Grid.Row="2" x:Name="DatabaseUserControl" HorizontalAlignment="Stretch" MinWidth="400" Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" ValidationChecking="OpenDatabaseUserControl_OnValidationChecking">
<local:CompositeKeyUserControl Grid.Row="2" x:Name="DatabaseUserControl" HorizontalAlignment="Stretch" MinWidth="400" Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecking">
<core:CallMethodAction TargetObject="{Binding}" MethodName="OpenDatabaseFile" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:CallMethodAction TargetObject="{Binding}" MethodName="UpdateAccessTime" />
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>

View File

@@ -1,9 +1,5 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
using System;
using Windows.UI.Xaml;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Pages
{
/// <summary>
@@ -11,17 +7,9 @@ namespace ModernKeePass.Pages
/// </summary>
public sealed partial class RecentDatabasesPage
{
public RecentVm Model => (RecentVm)DataContext;
public RecentDatabasesPage()
{
InitializeComponent();
}
private void OpenDatabaseUserControl_OnValidationChecking(object sender, EventArgs e)
{
var app = (App)Application.Current;
app.Database.DatabaseFile = ((RecentItemVm)Model.SelectedItem).DatabaseFile;
}
}
}

View File

@@ -1,6 +1,8 @@
using Windows.Storage;
using System;
using Windows.Storage;
using ModernKeePass.Common;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml;
using ModernKeePass.Interfaces;
namespace ModernKeePass.ViewModels
@@ -27,5 +29,21 @@ namespace ModernKeePass.ViewModels
get { return _isSelected; }
set { SetProperty(ref _isSelected, value); }
}
public void OpenDatabaseFile()
{
OpenDatabaseFile((Application.Current as App)?.Database);
}
public void OpenDatabaseFile(IDatabase database)
{
database.DatabaseFile = DatabaseFile;
}
public async void UpdateAccessTime()
{
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
await mru.GetFileAsync(Token);
}
}
}

View File

@@ -37,13 +37,12 @@ namespace ModernKeePass.ViewModels
public RecentVm()
{
// TODO: opening the files actually changes the MRU order
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
foreach (var entry in mru.Entries)
{
try
{
var file = mru.GetFileAsync(entry.Token).GetAwaiter().GetResult();
var file = mru.GetFileAsync(entry.Token, AccessCacheOptions.SuppressAccessTimeUpdate).GetAwaiter().GetResult();
RecentItems.Add(new RecentItemVm(entry, file));
}
catch (Exception)