Toast notifications and undo mechanism now works! (for group and entries)

This commit is contained in:
2017-10-18 10:32:51 +02:00
committed by BONNEVILLE Geoffroy
parent bc6e7d0097
commit dc62cedb74
8 changed files with 148 additions and 126 deletions

View File

@@ -9,7 +9,6 @@ using Windows.UI.Xaml.Navigation;
using Microsoft.QueryStringDotNET;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.ViewModels;
// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227
@@ -21,7 +20,7 @@ namespace ModernKeePass
sealed partial class App : Application
{
public DatabaseHelper Database { get; set; } = new DatabaseHelper();
public Queue<IPwEntity> PendingDeleteQueue = new Queue<IPwEntity>();
public Dictionary<string, IPwEntity> PendingDeleteEntities = new Dictionary<string, IPwEntity>();
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
@@ -33,6 +32,7 @@ namespace ModernKeePass
Suspending += OnSuspending;
}
#region Event Handlers
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
@@ -90,25 +90,20 @@ namespace ModernKeePass
// parameter
rootFrame.Navigate(typeof(MainPage), lauchActivatedEventArgs.Arguments);
}
}
//if (e is ToastNotificationActivatedEventArgs)
else
{
/*var toastActivationArgs = e as ToastNotificationActivatedEventArgs;
// App is "launched" via the Toast Activation event
UndoEntityDelete(lauchActivatedEventArgs.Arguments);
}
}
// This is only available on Windows 10...
/*else if (e is ToastNotificationActivatedEventArgs)
{
var toastActivationArgs = e as ToastNotificationActivatedEventArgs;
// Parse the query string (using QueryString.NET)
QueryString args = QueryString.Parse(toastActivationArgs.Argument);
// See what action is being requested
switch (args["action"])
{
UndoEntityDelete(QueryString.Parse(toastActivationArgs.Argument));
}*/
var entity = PendingDeleteQueue.Dequeue();
if (entity is GroupVm)
{
entity.ParentGroup.Groups.Add(entity as GroupVm);
}
}
// Ensure the current window is active
Window.Current.Activate();
}
@@ -138,6 +133,10 @@ namespace ModernKeePass
deferral.Complete();
}
/// <summary>
/// Invoked when application is launched from opening a file in Windows Explorer
/// </summary>
/// <param name="args">Details about the file being opened</param>
protected override void OnFileActivated(FileActivatedEventArgs args)
{
base.OnFileActivated(args);
@@ -147,5 +146,14 @@ namespace ModernKeePass
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
#endregion
private void UndoEntityDelete(string arguments)
{
var args = QueryString.Parse(arguments);
var entity = PendingDeleteEntities[args["entityId"]];
PendingDeleteEntities.Remove(args["entityId"]);
entity.UndoDelete();
}
}
}

View File

@@ -0,0 +1,97 @@
using System;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Microsoft.QueryStringDotNET;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Common
{
public static class ToastNotificationHelper
{
public static /*async*/ void ShowUndoToast(string entityType, IPwEntity entity)
{
// This is for Windows 10
// Construct the visuals of the toast
/*var visual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText
{
Text = $"{entityType} {entity.Name} deleted."
}
}
}
};
// Construct the actions for the toast (inputs and buttons)
var actions = new ToastActionsCustom
{
Buttons =
{
new ToastButton("Undo", new QueryString
{
{ "action", "undo" },
{ "entityType", entityType },
{ "entityId", entity.Id }
}.ToString())
}
};
// Now we can construct the final toast content
var toastContent = new ToastContent
{
Visual = visual,
Actions = actions,
// Arguments when the user taps body of toast
Launch = new QueryString()
{
{ "action", "undo" },
{ "entityType", "group" },
{ "entityId", entity.Id }
}.ToString()
};
// And create the toast notification
var toastXml = new XmlDocument();
toastXml.LoadXml(toastContent.GetContent());
var toast = new ToastNotification(toastXml) {ExpirationTime = DateTime.Now.AddSeconds(5)};
toast.Dismissed += Toast_Dismissed;
*/
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text");
toastElements[0].AppendChild(notificationXml.CreateTextNode($"{entityType} deleted"));
toastElements[1].AppendChild(notificationXml.CreateTextNode("Click me to undo"));
var toastNode = notificationXml.SelectSingleNode("/toast");
((XmlElement)toastNode)?.SetAttribute("launch", new QueryString
{
{ "entityType", entityType },
{ "entityId", entity.Id }
}.ToString());
var toast = new ToastNotification(notificationXml)
{
ExpirationTime = DateTime.Now.AddSeconds(5)
};
toast.Dismissed += Toast_Dismissed;
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
private static void Toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
{
var toastNode = sender.Content.SelectSingleNode("/toast");
var launchArguments = QueryString.Parse(((XmlElement)toastNode)?.GetAttribute("launch"));
var app = (App)Application.Current;
var entity = app.PendingDeleteEntities[launchArguments["entityId"]];
app.PendingDeleteEntities.Remove(launchArguments["entityId"]);
entity.CommitDelete();
}
}
}

View File

@@ -12,5 +12,6 @@ namespace ModernKeePass.Interfaces
bool IsEditMode { get; }
void CommitDelete();
void UndoDelete();
}
}

View File

@@ -117,6 +117,7 @@
<Compile Include="Common\ObservableDictionary.cs" />
<Compile Include="Common\RelayCommand.cs" />
<Compile Include="Common\SuspensionManager.cs" />
<Compile Include="Common\ToastNotificationHelper.cs" />
<Compile Include="Controls\FirstItemDataTemplateSelector.cs" />
<Compile Include="Controls\ListViewWithDisable.cs" />
<Compile Include="Controls\OpenDatabaseUserControl.xaml.cs">

View File

@@ -86,8 +86,8 @@ namespace ModernKeePass.Pages
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("Delete", delete =>
{
var entry = DataContext as EntryVm;
entry?.RemoveEntry();
ToastNotificationHelper.ShowUndoToast("Entry", Model);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
}));
messageDialog.Commands.Add(new UICommand("Cancel"));

View File

@@ -128,7 +128,7 @@ namespace ModernKeePass.Pages
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("Delete", delete =>
{
ShowToast("Group", Model);
ToastNotificationHelper.ShowUndoToast("Group", Model);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
}));
@@ -171,91 +171,5 @@ namespace ModernKeePass.Pages
#endregion
private async void ShowToast(string entityType, IPwEntity entity)
{
// Construct the visuals of the toast
/*var visual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText
{
Text = $"{entityType} {entity.Name} deleted."
}
}
}
};
// Construct the actions for the toast (inputs and buttons)
var actions = new ToastActionsCustom
{
Buttons =
{
new ToastButton("Undo", new QueryString
{
{ "action", "undo" },
{ "entityType", entityType },
{ "entityId", entity.Id }
}.ToString())
}
};
// Now we can construct the final toast content
var toastContent = new ToastContent
{
Visual = visual,
Actions = actions,
// Arguments when the user taps body of toast
Launch = new QueryString()
{
{ "action", "undo" },
{ "entityType", "group" },
{ "entityId", entity.Id }
}.ToString()
};
// And create the toast notification
var toastXml = new XmlDocument();
toastXml.LoadXml(toastContent.GetContent());
var visualXml = toastXml.GetElementsByTagName("visual")[0];
((XmlElement)visualXml.ChildNodes[0]).SetAttribute("template", "ToastText02");
var toast = new ToastNotification(toastXml) {ExpirationTime = DateTime.Now.AddSeconds(5)};
toast.Dismissed += Toast_Dismissed;
*/
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text");
toastElements[0].AppendChild(notificationXml.CreateTextNode($"{entityType} deleted"));
toastElements[1].AppendChild(notificationXml.CreateTextNode("Click me to undo"));
var toastNode = notificationXml.SelectSingleNode("/toast");
((XmlElement)toastNode).SetAttribute("launch", new QueryString
{
{ "entityType", entityType },
{ "entityId", entity.Id }
}.ToString());
var toast = new ToastNotification(notificationXml)
{
ExpirationTime = DateTime.Now.AddSeconds(5)
};
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
private void Toast_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
{
var app = (App)Application.Current;
if (app.PendingDeleteQueue.Count == 0) return;
var entity = app.PendingDeleteQueue.Dequeue();
if (entity is GroupVm)
{
entity.CommitDelete();
}
}
}
}

View File

@@ -6,6 +6,7 @@ using ModernKeePassLib;
using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Security;
using System;
using Windows.UI.Xaml;
namespace ModernKeePass.ViewModels
{
@@ -112,10 +113,6 @@ namespace ModernKeePass.ViewModels
ParentGroup = parent;
}
public void RemoveEntry()
{
ParentGroup.RemoveEntry(this);
}
public void GeneratePassword()
{
@@ -155,9 +152,21 @@ namespace ModernKeePass.ViewModels
Entry?.Strings.Set(key, new ProtectedString(true, newValue));
}
public void MarkForDelete()
{
var app = (App)Application.Current;
app.PendingDeleteEntities.Add(Id, this);
ParentGroup.Entries.Remove(this);
}
public void CommitDelete()
{
throw new NotImplementedException();
}
Entry.ParentGroup.Entries.Remove(Entry);
}
public void UndoDelete()
{
ParentGroup.Entries.Add(this);
}
}
}

View File

@@ -98,22 +98,10 @@ namespace ModernKeePass.ViewModels
return newEntry;
}
public void RemoveGroup()
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
ParentGroup.Groups.Remove(this);
}
public void RemoveEntry(EntryVm entry)
{
_pwGroup.Entries.Remove(entry.Entry);
Entries.Remove(entry);
}
public void MarkForDelete()
{
var app = (App)Application.Current;
app.PendingDeleteQueue.Enqueue(this);
app.PendingDeleteEntities.Add(Id, this);
ParentGroup.Groups.Remove(this);
}
@@ -121,5 +109,9 @@ namespace ModernKeePass.ViewModels
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
}
public void UndoDelete()
{
ParentGroup.Groups.Add(this);
}
}
}