Added login dialogs
This commit is contained in:
parent
503e1e143e
commit
9ecb32c3d2
@ -1,5 +1,7 @@
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationWinForms.AvaloniaUI
|
namespace LibationWinForms.AvaloniaUI
|
||||||
{
|
{
|
||||||
@ -13,5 +15,14 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
return brush;
|
return brush;
|
||||||
return defaultBrush;
|
return defaultBrush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static T ShowDialogSynchronously<T>(this Avalonia.Controls.Window window, Avalonia.Controls.Window owner)
|
||||||
|
{
|
||||||
|
using var source = new CancellationTokenSource();
|
||||||
|
var dialogTask = window.ShowDialog<T>(owner);
|
||||||
|
dialogTask.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.MainLoop(source.Token);
|
||||||
|
return dialogTask.Result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,10 +116,12 @@ namespace LibationWinForms.AvaloniaUI.Controls
|
|||||||
|
|
||||||
private void setDirectory()
|
private void setDirectory()
|
||||||
{
|
{
|
||||||
Directory
|
var path1
|
||||||
= customStates.CustomChecked ? customStates.CustomDir
|
= customStates.CustomChecked ? customStates.CustomDir
|
||||||
: directorySelectControl.SelectedDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
|
: directorySelectControl.SelectedDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
|
||||||
: Configuration.GetKnownDirectoryPath(directorySelectControl.SelectedDirectory);
|
: Configuration.GetKnownDirectoryPath(directorySelectControl.SelectedDirectory);
|
||||||
|
Directory
|
||||||
|
= System.IO.Path.Combine(path1 ?? string.Empty, SubDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,6 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
Continue = 11
|
Continue = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum MessageBoxIcon
|
public enum MessageBoxIcon
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
@ -39,6 +38,7 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
Asterisk = 64,
|
Asterisk = 64,
|
||||||
Information = 64
|
Information = 64
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MessageBoxButtons
|
public enum MessageBoxButtons
|
||||||
{
|
{
|
||||||
OK,
|
OK,
|
||||||
@ -74,10 +74,8 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
/// -or-
|
/// -or-
|
||||||
/// <paramref name="defaultButton" /> is not a member of <see cref="T:System.Windows.Forms.MessageBoxDefaultButton" />.</exception>
|
/// <paramref name="defaultButton" /> is not a member of <see cref="T:System.Windows.Forms.MessageBoxDefaultButton" />.</exception>
|
||||||
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
||||||
public static async Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||||
{
|
=> ShowCoreAsync(null, text, caption, buttons, icon, defaultButton);
|
||||||
return await ShowCore(null, text, caption, buttons, icon, defaultButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Displays a message box with specified text, caption, buttons, and icon.</summary>
|
/// <summary>Displays a message box with specified text, caption, buttons, and icon.</summary>
|
||||||
@ -90,10 +88,8 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
/// -or-
|
/// -or-
|
||||||
/// The <paramref name="icon" /> parameter specified is not a member of <see cref="T:System.Windows.Forms.MessageBoxIcon" />.</exception>
|
/// The <paramref name="icon" /> parameter specified is not a member of <see cref="T:System.Windows.Forms.MessageBoxIcon" />.</exception>
|
||||||
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
||||||
public static async Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||||
{
|
=> ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Displays a message box with specified text, caption, and buttons.</summary>
|
/// <summary>Displays a message box with specified text, caption, and buttons.</summary>
|
||||||
@ -103,28 +99,22 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
||||||
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">The <paramref name="buttons" /> parameter specified is not a member of <see cref="T:System.Windows.Forms.MessageBoxButtons" />.</exception>
|
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">The <paramref name="buttons" /> parameter specified is not a member of <see cref="T:System.Windows.Forms.MessageBoxButtons" />.</exception>
|
||||||
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
||||||
public static async Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons)
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons)
|
||||||
{
|
=> ShowCoreAsync(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Displays a message box with specified text and caption.</summary>
|
/// <summary>Displays a message box with specified text and caption.</summary>
|
||||||
/// <param name="text">The text to display in the message box.</param>
|
/// <param name="text">The text to display in the message box.</param>
|
||||||
/// <param name="caption">The text to display in the title bar of the message box.</param>
|
/// <param name="caption">The text to display in the title bar of the message box.</param>
|
||||||
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
||||||
public static async Task<DialogResult> Show(string text, string caption)
|
public static DialogResult Show(string text, string caption)
|
||||||
{
|
=> ShowCoreAsync(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Displays a message box with specified text.</summary>
|
/// <summary>Displays a message box with specified text.</summary>
|
||||||
/// <param name="text">The text to display in the message box.</param>
|
/// <param name="text">The text to display in the message box.</param>
|
||||||
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
||||||
public static async Task<DialogResult> Show(string text)
|
public static DialogResult Show(string text)
|
||||||
{
|
=> ShowCoreAsync(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Displays a message box in front of the specified object and with the specified text, caption, buttons, icon, default button, and options.</summary>
|
/// <summary>Displays a message box in front of the specified object and with the specified text, caption, buttons, icon, default button, and options.</summary>
|
||||||
@ -146,10 +136,9 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
/// <exception cref="T:System.ArgumentException">
|
/// <exception cref="T:System.ArgumentException">
|
||||||
/// -or-
|
/// -or-
|
||||||
/// <paramref name="buttons" /> specified an invalid combination of <see cref="T:System.Windows.Forms.MessageBoxButtons" />.</exception>
|
/// <paramref name="buttons" /> specified an invalid combination of <see cref="T:System.Windows.Forms.MessageBoxButtons" />.</exception>
|
||||||
public static async Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
public static DialogResult Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||||
{
|
=> ShowCoreAsync(owner, text, caption, buttons, icon, defaultButton);
|
||||||
return await ShowCore(owner, text, caption, buttons, icon, defaultButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Displays a message box in front of the specified object and with the specified text, caption, buttons, and icon.</summary>
|
/// <summary>Displays a message box in front of the specified object and with the specified text, caption, buttons, and icon.</summary>
|
||||||
@ -164,10 +153,8 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
/// -or-
|
/// -or-
|
||||||
/// <paramref name="icon" /> is not a member of <see cref="T:System.Windows.Forms.MessageBoxIcon" />.</exception>
|
/// <paramref name="icon" /> is not a member of <see cref="T:System.Windows.Forms.MessageBoxIcon" />.</exception>
|
||||||
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
||||||
public static async Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
public static DialogResult Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||||
{
|
=> ShowCoreAsync(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Displays a message box in front of the specified object and with the specified text, caption, and buttons.</summary>
|
/// <summary>Displays a message box in front of the specified object and with the specified text, caption, and buttons.</summary>
|
||||||
/// <param name="owner">An implementation of <see cref="T:System.Windows.Forms.IWin32Window" /> that will own the modal dialog box.</param>
|
/// <param name="owner">An implementation of <see cref="T:System.Windows.Forms.IWin32Window" /> that will own the modal dialog box.</param>
|
||||||
@ -178,35 +165,29 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
|
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
|
||||||
/// <paramref name="buttons" /> is not a member of <see cref="T:System.Windows.Forms.MessageBoxButtons" />.</exception>
|
/// <paramref name="buttons" /> is not a member of <see cref="T:System.Windows.Forms.MessageBoxButtons" />.</exception>
|
||||||
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
/// <exception cref="T:System.InvalidOperationException">An attempt was made to display the <see cref="T:System.Windows.Forms.MessageBox" /> in a process that is not running in User Interactive mode. This is specified by the <see cref="P:System.Windows.Forms.SystemInformation.UserInteractive" /> property.</exception>
|
||||||
public static async Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons)
|
public static DialogResult Show(Window owner, string text, string caption, MessageBoxButtons buttons)
|
||||||
{
|
=> ShowCoreAsync(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Displays a message box in front of the specified object and with the specified text and caption.</summary>
|
/// <summary>Displays a message box in front of the specified object and with the specified text and caption.</summary>
|
||||||
/// <param name="owner">An implementation of <see cref="T:System.Windows.Forms.IWin32Window" /> that will own the modal dialog box.</param>
|
/// <param name="owner">An implementation of <see cref="T:System.Windows.Forms.IWin32Window" /> that will own the modal dialog box.</param>
|
||||||
/// <param name="text">The text to display in the message box.</param>
|
/// <param name="text">The text to display in the message box.</param>
|
||||||
/// <param name="caption">The text to display in the title bar of the message box.</param>
|
/// <param name="caption">The text to display in the title bar of the message box.</param>
|
||||||
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
||||||
public static async Task<DialogResult> Show(Window owner, string text, string caption)
|
public static DialogResult Show(Window owner, string text, string caption)
|
||||||
{
|
=> ShowCoreAsync(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Displays a message box in front of the specified object and with the specified text.</summary>
|
/// <summary>Displays a message box in front of the specified object and with the specified text.</summary>
|
||||||
/// <param name="owner">An implementation of <see cref="T:System.Windows.Forms.IWin32Window" /> that will own the modal dialog box.</param>
|
/// <param name="owner">An implementation of <see cref="T:System.Windows.Forms.IWin32Window" /> that will own the modal dialog box.</param>
|
||||||
/// <param name="text">The text to display in the message box.</param>
|
/// <param name="text">The text to display in the message box.</param>
|
||||||
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
/// <returns>One of the <see cref="T:System.Windows.Forms.DialogResult" /> values.</returns>
|
||||||
public static async Task<DialogResult> Show(Window owner, string text)
|
public static DialogResult Show(Window owner, string text)
|
||||||
{
|
=> ShowCoreAsync(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
return await ShowCore(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task VerboseLoggingWarning_ShowIfTrue()
|
public static async Task VerboseLoggingWarning_ShowIfTrue()
|
||||||
{
|
{
|
||||||
// when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured
|
// when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured
|
||||||
if (Serilog.Log.Logger.IsVerboseEnabled())
|
if (Serilog.Log.Logger.IsVerboseEnabled())
|
||||||
await Show(@"
|
Show(@"
|
||||||
Warning: verbose logging is enabled.
|
Warning: verbose logging is enabled.
|
||||||
|
|
||||||
This should be used for debugging only. It creates many
|
This should be used for debugging only. It creates many
|
||||||
@ -219,7 +200,7 @@ Libation.
|
|||||||
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<DialogResult> ShowConfirmationDialog(Window owner, IEnumerable<LibraryBook> libraryBooks, string format, string title, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
|
public static DialogResult ShowConfirmationDialog(Window owner, IEnumerable<LibraryBook> libraryBooks, string format, string title, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
|
||||||
{
|
{
|
||||||
if (libraryBooks is null || !libraryBooks.Any())
|
if (libraryBooks is null || !libraryBooks.Any())
|
||||||
return DialogResult.Cancel;
|
return DialogResult.Cancel;
|
||||||
@ -234,7 +215,7 @@ Libation.
|
|||||||
= string.Format(format, $"{thisThese} {count} {bookBooks}")
|
= string.Format(format, $"{thisThese} {count} {bookBooks}")
|
||||||
+ $"\r\n\r\n{titlesAgg}";
|
+ $"\r\n\r\n{titlesAgg}";
|
||||||
|
|
||||||
return await ShowCore(owner,
|
return ShowCoreAsync(owner,
|
||||||
message,
|
message,
|
||||||
title,
|
title,
|
||||||
MessageBoxButtons.YesNo,
|
MessageBoxButtons.YesNo,
|
||||||
@ -263,18 +244,11 @@ Libation.
|
|||||||
|
|
||||||
var form = new MessageBoxAlertAdminDialog(text, caption, exception);
|
var form = new MessageBoxAlertAdminDialog(text, caption, exception);
|
||||||
|
|
||||||
await DisplayWindow(form, owner);
|
DisplayWindow(form, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static async Task<DialogResult> ShowCore(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
private static DialogResult ShowCoreAsync(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||||
{
|
|
||||||
if (Avalonia.Threading.Dispatcher.UIThread.CheckAccess())
|
|
||||||
return await ShowCore2(owner, message, caption, buttons, icon, defaultButton);
|
|
||||||
else
|
|
||||||
return await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => ShowCore2(owner, message, caption, buttons, icon, defaultButton));
|
|
||||||
}
|
|
||||||
private static async Task<DialogResult> ShowCore2(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
|
||||||
{
|
{
|
||||||
var dialog = new MessageBoxWindow();
|
var dialog = new MessageBoxWindow();
|
||||||
|
|
||||||
@ -307,15 +281,15 @@ Libation.
|
|||||||
dialog.Height = dialog.MinHeight;
|
dialog.Height = dialog.MinHeight;
|
||||||
dialog.Width = dialog.MinWidth;
|
dialog.Width = dialog.MinWidth;
|
||||||
|
|
||||||
return await DisplayWindow(dialog, owner);
|
return DisplayWindow(dialog, owner);
|
||||||
}
|
}
|
||||||
private static async Task<DialogResult> DisplayWindow(Window toDisplay, Window owner)
|
private static DialogResult DisplayWindow(Window toDisplay, Window owner)
|
||||||
{
|
{
|
||||||
if (owner is null)
|
if (owner is null)
|
||||||
{
|
{
|
||||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
return await toDisplay.ShowDialog<DialogResult>(desktop.MainWindow);
|
return toDisplay.ShowDialogSynchronously<DialogResult>(desktop.MainWindow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -329,7 +303,7 @@ Libation.
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.Show();
|
window.Show();
|
||||||
var result = await toDisplay.ShowDialog<DialogResult>(window);
|
var result = toDisplay.ShowDialogSynchronously<DialogResult>(window);
|
||||||
window.Close();
|
window.Close();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -337,7 +311,7 @@ Libation.
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return await toDisplay.ShowDialog<DialogResult>(owner);
|
return toDisplay.ShowDialogSynchronously<DialogResult>(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -346,7 +346,7 @@ $@" Title: {libraryBook.Book.Title}
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if null then ask user
|
// if null then ask user
|
||||||
dialogResult ??= await MessageBox.Show(string.Format(SkipDialogText + "\r\n\r\nSee Settings to avoid this box in the future.", details), "Skip importing this book?", SkipDialogButtons, MessageBoxIcon.Question, SkipDialogDefaultButton);
|
dialogResult ??= MessageBox.Show(string.Format(SkipDialogText + "\r\n\r\nSee Settings to avoid this box in the future.", details), "Skip importing this book?", SkipDialogButtons, MessageBoxIcon.Question, SkipDialogDefaultButton);
|
||||||
|
|
||||||
if (dialogResult == DialogResult.Abort)
|
if (dialogResult == DialogResult.Abort)
|
||||||
return ProcessBookResult.FailedAbort;
|
return ProcessBookResult.FailedAbort;
|
||||||
|
|||||||
@ -243,7 +243,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList();
|
var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList();
|
||||||
var result = await MessageBox.ShowConfirmationDialog(
|
var result = MessageBox.ShowConfirmationDialog(
|
||||||
null,
|
null,
|
||||||
libraryBooks,
|
libraryBooks,
|
||||||
$"Are you sure you want to remove {selectedBooks.Count} books from Libation's library?",
|
$"Are you sure you want to remove {selectedBooks.Count} books from Libation's library?",
|
||||||
@ -307,7 +307,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
|
|||||||
.Select(lbe => lbe.LibraryBook)
|
.Select(lbe => lbe.LibraryBook)
|
||||||
.Where(lb => !lb.Book.HasLiberated());
|
.Where(lb => !lb.Book.HasLiberated());
|
||||||
|
|
||||||
var removedBooks = await LibraryCommands.FindInactiveBooks(Login.WinformLoginChoiceEager.ApiExtendedFunc, lib, accounts);
|
var removedBooks = await LibraryCommands.FindInactiveBooks(Views.Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, lib, accounts);
|
||||||
|
|
||||||
var removable = allBooks.Where(lbe => removedBooks.Any(rb => rb.Book.AudibleProductId == lbe.AudibleProductId)).ToList();
|
var removable = allBooks.Where(lbe => removedBooks.Any(rb => rb.Book.AudibleProductId == lbe.AudibleProductId)).ToList();
|
||||||
|
|
||||||
|
|||||||
@ -56,13 +56,17 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
// only persist in 'save' step
|
// only persist in 'save' step
|
||||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
var accounts = persister.AccountsSettings.Accounts;
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
if (!accounts.Any())
|
if (accounts.Any())
|
||||||
return;
|
{
|
||||||
|
foreach (var account in accounts)
|
||||||
|
AddAccountToGrid(account);
|
||||||
|
}
|
||||||
|
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
|
addBlankAccount();
|
||||||
foreach (var account in accounts)
|
}
|
||||||
AddAccountToGrid(account);
|
private void addBlankAccount()
|
||||||
|
{
|
||||||
|
|
||||||
var newBlank = new AccountDto();
|
var newBlank = new AccountDto();
|
||||||
newBlank.PropertyChanged += AccountDto_PropertyChanged;
|
newBlank.PropertyChanged += AccountDto_PropertyChanged;
|
||||||
@ -89,9 +93,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
if (Accounts.Any(a => a.IsDefault))
|
if (Accounts.Any(a => a.IsDefault))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var newBlank = new AccountDto();
|
addBlankAccount();
|
||||||
newBlank.PropertyChanged += AccountDto_PropertyChanged;
|
|
||||||
Accounts.Insert(Accounts.Count, newBlank);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@ -134,7 +136,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
if (persister.AccountsSettings.Accounts.Any(a => a.AccountId == account.AccountId && a.IdentityTokens.Locale.Name == account.Locale.Name))
|
if (persister.AccountsSettings.Accounts.Any(a => a.AccountId == account.AccountId && a.IdentityTokens.Locale.Name == account.Locale.Name))
|
||||||
{
|
{
|
||||||
await MessageBox.Show(this, $"An account with that account id and country already exists.\r\n\r\nAccount ID: {account.AccountId}\r\nCountry: {account.Locale.Name}", "Cannot Add Duplicate Account");
|
MessageBox.Show(this, $"An account with that account id and country already exists.\r\n\r\nAccount ID: {account.AccountId}\r\nCountry: {account.Locale.Name}", "Cannot Add Duplicate Account");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +146,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await MessageBox.ShowAdminAlert(
|
MessageBox.ShowAdminAlert(
|
||||||
this,
|
this,
|
||||||
$"An error occurred while importing an account from:\r\n{filePath[0]}\r\n\r\nIs the file encrypted?",
|
$"An error occurred while importing an account from:\r\n{filePath[0]}\r\n\r\nIs the file encrypted?",
|
||||||
"Error Importing Account",
|
"Error Importing Account",
|
||||||
@ -163,7 +165,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await inputIsValid())
|
if (!inputIsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// without transaction, accounts persister will write ANY EDIT immediately to file
|
// without transaction, accounts persister will write ANY EDIT immediately to file
|
||||||
@ -190,8 +192,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void persist(AccountsSettings accountsSettings)
|
private void persist(AccountsSettings accountsSettings)
|
||||||
{
|
{
|
||||||
var existingAccounts = accountsSettings.Accounts;
|
var existingAccounts = accountsSettings.Accounts;
|
||||||
@ -222,7 +222,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
: dto.AccountName.Trim();
|
: dto.AccountName.Trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private async Task<bool> inputIsValid()
|
private bool inputIsValid()
|
||||||
{
|
{
|
||||||
foreach (var dto in Accounts.ToList())
|
foreach (var dto in Accounts.ToList())
|
||||||
{
|
{
|
||||||
@ -234,13 +234,13 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(dto.AccountId))
|
if (string.IsNullOrWhiteSpace(dto.AccountId))
|
||||||
{
|
{
|
||||||
await MessageBox.Show(this, "Account id cannot be blank. Please enter an account id for all accounts.", "Blank account", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(this, "Account id cannot be blank. Please enter an account id for all accounts.", "Blank account", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(dto.SelectedLocale?.Name))
|
if (string.IsNullOrWhiteSpace(dto.SelectedLocale?.Name))
|
||||||
{
|
{
|
||||||
await MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
if (account.IdentityTokens?.IsValid != true)
|
if (account.IdentityTokens?.IsValid != true)
|
||||||
{
|
{
|
||||||
await MessageBox.Show(this, "This account hasn't been authenticated yet. First scan your library to log into your account, then try exporting again.", "Account Not Authenticated");
|
MessageBox.Show(this, "This account hasn't been authenticated yet. First scan your library to log into your account, then try exporting again.", "Account Not Authenticated");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,11 +283,11 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
File.WriteAllText(fileName, jsonText);
|
File.WriteAllText(fileName, jsonText);
|
||||||
|
|
||||||
await MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{fileName}", "Success!");
|
MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{fileName}", "Success!");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await MessageBox.ShowAdminAlert(
|
MessageBox.ShowAdminAlert(
|
||||||
this,
|
this,
|
||||||
$"An error occurred while exporting account:\r\n{account.AccountName}",
|
$"An error occurred while exporting account:\r\n{account.AccountName}",
|
||||||
"Error Exporting Account",
|
"Error Exporting Account",
|
||||||
|
|||||||
@ -17,13 +17,30 @@
|
|||||||
<Setter Property="BorderThickness" Value="2" />
|
<Setter Property="BorderThickness" Value="2" />
|
||||||
</Style>
|
</Style>
|
||||||
</Grid.Styles>
|
</Grid.Styles>
|
||||||
<Grid ColumnDefinitions="Auto,*" Margin="10,10,10,0">
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="*,Auto" Margin="10,10,10,0">
|
||||||
<Panel VerticalAlignment="Top" Margin="5" Background="LightGray" Width="80" Height="80" >
|
<Panel VerticalAlignment="Top" Margin="5" Background="LightGray" Width="80" Height="80" >
|
||||||
<Image Grid.Column="0" Width="80" Height="80" Source="{Binding Cover}" />
|
<Image Grid.Column="0" Width="80" Height="80" Source="{Binding Cover}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
|
<Panel Grid.Column="0" Grid.Row="1">
|
||||||
|
<Panel.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="Blue"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="TextDecorations" Value="Underline"/>
|
||||||
|
</Style>
|
||||||
|
</Panel.Styles>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Margin="10" TextWrapping="Wrap" TextAlignment="Center"
|
||||||
|
Tapped="GoToAudible_Tapped"
|
||||||
|
Text="Open in
Audible
(Browser)" />
|
||||||
|
</Panel>
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.RowSpan="2"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
@ -43,7 +60,7 @@
|
|||||||
|
|
||||||
<TextBox Margin="0,5,0,5"
|
<TextBox Margin="0,5,0,5"
|
||||||
MinHeight="25"
|
MinHeight="25"
|
||||||
FontSize="12"
|
FontSize="12" Name="tagsTbox"
|
||||||
Text="{Binding Tags, Mode=TwoWay}"/>
|
Text="{Binding Tags, Mode=TwoWay}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</controls:GroupBox>
|
</controls:GroupBox>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationWinForms.AvaloniaUI.ViewModels;
|
using LibationWinForms.AvaloniaUI.ViewModels;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -34,6 +35,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
public BookDetailsDialog()
|
public BookDetailsDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
ControlToFocusOnShow = this.Find<TextBox>(nameof(tagsTbox));
|
||||||
|
|
||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
{
|
{
|
||||||
@ -46,13 +48,18 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
LibraryBook = libraryBook;
|
LibraryBook = libraryBook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void SaveAndClose()
|
protected override void SaveAndClose()
|
||||||
{
|
{
|
||||||
SaveButton_Clicked(null, null);
|
SaveButton_Clicked(null, null);
|
||||||
base.SaveAndClose();
|
base.SaveAndClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GoToAudible_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var locale = AudibleApi.Localization.Get(_libraryBook.Book.Locale);
|
||||||
|
var link = $"https://www.audible.{locale.TopDomain}/pd/{_libraryBook.Book.AudibleProductId}";
|
||||||
|
Go.To.Url(link);
|
||||||
|
}
|
||||||
|
|
||||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
Content="Reset to Default"
|
Content="Reset to Default"
|
||||||
Click="ResetButton_Click" />
|
Click="ResetButton_Click" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="*,*">
|
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
|
||||||
|
|
||||||
<Border
|
<Border
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
@ -62,7 +62,7 @@
|
|||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="Description">
|
<DataGridTemplateColumn Width="Auto" Header="Description">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextPresenter
|
<TextPresenter
|
||||||
@ -80,10 +80,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
RowDefinitions="Auto,*,80">
|
RowDefinitions="Auto,*,80" HorizontalAlignment="Stretch">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="5,5,5,10"
|
Margin="5,5,5,10"
|
||||||
|
|||||||
@ -19,7 +19,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
{
|
{
|
||||||
class BracketEscapeConverter : IValueConverter
|
class BracketEscapeConverter : IValueConverter
|
||||||
{
|
{
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is string str && str[0] != '<' && str[^1] != '>')
|
if (value is string str && str[0] != '<' && str[^1] != '>')
|
||||||
@ -68,7 +67,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
}
|
}
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
if (!await _viewModel.Validate())
|
if (!_viewModel.Validate())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TemplateText = _viewModel.workingTemplateText;
|
TemplateText = _viewModel.workingTemplateText;
|
||||||
@ -119,7 +118,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
public void resetTextBox(string value) => workingTemplateText = value;
|
public void resetTextBox(string value) => workingTemplateText = value;
|
||||||
|
|
||||||
public async Task<bool> Validate()
|
public bool Validate()
|
||||||
{
|
{
|
||||||
if (template.IsValid(workingTemplateText))
|
if (template.IsValid(workingTemplateText))
|
||||||
return true;
|
return true;
|
||||||
@ -127,7 +126,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
.GetErrors(workingTemplateText)
|
.GetErrors(workingTemplateText)
|
||||||
.Select(err => $"- {err}")
|
.Select(err => $"- {err}")
|
||||||
.Aggregate((a, b) => $"{a}\r\n{b}");
|
.Aggregate((a, b) => $"{a}\r\n{b}");
|
||||||
await MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,11 +231,13 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
for(int i = 0; i < wordsSplit.Length; i++)
|
for(int i = 0; i < wordsSplit.Length; i++)
|
||||||
{
|
{
|
||||||
var tb = new TextBlock();
|
var tb = new TextBlock
|
||||||
tb.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom;
|
{
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom,
|
||||||
tb.Text = wordsSplit[i] + (i == wordsSplit.Length - 1 ? "" : " ");
|
TextWrapping = TextWrapping.Wrap,
|
||||||
tb.FontWeight = item.Item2;
|
Text = wordsSplit[i] + (i == wordsSplit.Length - 1 ? "" : " "),
|
||||||
|
FontWeight = item.Item2
|
||||||
|
};
|
||||||
|
|
||||||
WrapPanel.Children.Add(tb);
|
WrapPanel.Children.Add(tb);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, $"Failed to save picture to {fileName}");
|
Serilog.Log.Logger.Error(ex, $"Failed to save picture to {fileName}");
|
||||||
await MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
|
MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
if (!System.IO.Directory.Exists(libationDir))
|
if (!System.IO.Directory.Exists(libationDir))
|
||||||
{
|
{
|
||||||
await MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="120"
|
||||||
|
MinWidth="240" MinHeight="120"
|
||||||
|
MaxWidth="240" MaxHeight="120"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login.ApprovalNeededDialog"
|
||||||
|
Title="Approval Alert Detected"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="10"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="Amazon is sending you an email."/>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1" Margin="10,0,10,0"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="Please press this button after you've approved the notification."/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="10"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Content="Approve"
|
||||||
|
Click="Approve_Click" />
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class ApprovalNeededDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public ApprovalNeededDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("Approve button clicked");
|
||||||
|
|
||||||
|
return base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Approve_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public abstract class AvaloniaLoginBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <returns>True if ShowDialog's DialogResult == OK</returns>
|
||||||
|
protected static bool ShowDialog(DialogWindow dialog)
|
||||||
|
{
|
||||||
|
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var result = dialog.ShowDialogSynchronously<DialogResult>(desktop.MainWindow);
|
||||||
|
Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result });
|
||||||
|
return result == DialogResult.OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using AudibleApi;
|
||||||
|
using AudibleUtilities;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public class AvaloniaLoginCallback : AvaloniaLoginBase, ILoginCallback
|
||||||
|
{
|
||||||
|
private Account _account { get; }
|
||||||
|
|
||||||
|
public AvaloniaLoginCallback(Account account)
|
||||||
|
{
|
||||||
|
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Get2faCode()
|
||||||
|
{
|
||||||
|
var dialog = new _2faCodeDialog();
|
||||||
|
if (ShowDialog(dialog))
|
||||||
|
return dialog.Code;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCaptchaAnswer(byte[] captchaImage)
|
||||||
|
{
|
||||||
|
var dialog = new CaptchaDialog(captchaImage);
|
||||||
|
if (ShowDialog(dialog))
|
||||||
|
return dialog.Answer;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public (string name, string value) GetMfaChoice(MfaConfig mfaConfig)
|
||||||
|
{
|
||||||
|
var dialog = new MfaDialog(mfaConfig);
|
||||||
|
if (ShowDialog(dialog))
|
||||||
|
return (dialog.SelectedName, dialog.SelectedValue);
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public (string email, string password) GetLogin()
|
||||||
|
{
|
||||||
|
var dialog = new LoginCallbackDialog(_account);
|
||||||
|
if (ShowDialog(dialog))
|
||||||
|
return (_account.AccountId, dialog.Password);
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowApprovalNeeded()
|
||||||
|
{
|
||||||
|
var dialog = new ApprovalNeededDialog();
|
||||||
|
ShowDialog(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AudibleApi;
|
||||||
|
using AudibleUtilities;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public class AvaloniaLoginChoiceEager : AvaloniaLoginBase, ILoginChoiceEager
|
||||||
|
{
|
||||||
|
/// <summary>Convenience method. Recommended when wiring up Winforms to <see cref="ApplicationServices.LibraryCommands.ImportAccountAsync"/></summary>
|
||||||
|
public static async Task<ApiExtended> ApiExtendedFunc(Account account) => await ApiExtended.CreateAsync(account, new AvaloniaLoginChoiceEager(account));
|
||||||
|
|
||||||
|
public ILoginCallback LoginCallback { get; private set; }
|
||||||
|
|
||||||
|
private Account _account { get; }
|
||||||
|
|
||||||
|
public AvaloniaLoginChoiceEager(Account account)
|
||||||
|
{
|
||||||
|
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
LoginCallback = new AvaloniaLoginCallback(_account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChoiceOut Start(ChoiceIn choiceIn)
|
||||||
|
{
|
||||||
|
var dialog = new LoginChoiceEagerDialog(_account);
|
||||||
|
|
||||||
|
if (!ShowDialog(dialog))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
|
||||||
|
switch (dialog.LoginMethod)
|
||||||
|
{
|
||||||
|
case LoginMethod.Api:
|
||||||
|
return ChoiceOut.WithApi(dialog.Account.AccountId, dialog.Password);
|
||||||
|
case LoginMethod.External:
|
||||||
|
{
|
||||||
|
var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl);
|
||||||
|
return ShowDialog(externalDialog)
|
||||||
|
? ChoiceOut.External(externalDialog.ResponseUrl)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Exception($"Unknown {nameof(LoginMethod)} value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="220" d:DesignHeight="180"
|
||||||
|
MinWidth="220" MinHeight="180"
|
||||||
|
MaxWidth="220" MaxHeight="180"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login.CaptchaDialog"
|
||||||
|
Title="CAPTCHA"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
RowDefinitions="Auto,Auto,*"
|
||||||
|
ColumnDefinitions="Auto,*">
|
||||||
|
|
||||||
|
<Panel
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="10"
|
||||||
|
MinWidth="200"
|
||||||
|
MinHeight="70"
|
||||||
|
Background="LightGray">
|
||||||
|
|
||||||
|
<Image
|
||||||
|
Stretch="None"
|
||||||
|
Source="{Binding CaptchaImage}" />
|
||||||
|
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="10,0,10,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="CAPTCHA
answer:" />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="10,0,10,0" Text="{Binding Answer}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="10"
|
||||||
|
Padding="0,5,0,5"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Content="Submit"
|
||||||
|
Click="Submit_Click" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class CaptchaDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public string Answer { get; set; }
|
||||||
|
public Bitmap CaptchaImage { get; }
|
||||||
|
public CaptchaDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CaptchaDialog(byte[] captchaImage) :this()
|
||||||
|
{
|
||||||
|
using var ms = new MemoryStream(captchaImage);
|
||||||
|
CaptchaImage = new Bitmap(ms);
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { Answer });
|
||||||
|
|
||||||
|
return base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="120"
|
||||||
|
MinWidth="300" MinHeight="120"
|
||||||
|
Width="300" Height="120"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login.LoginCallbackDialog"
|
||||||
|
Title="Audible Login"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,Auto,*" ColumnDefinitions="*" Margin="5">
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||||
|
<TextBlock Text="Locale: " />
|
||||||
|
<TextBlock Text="{Binding Account.Locale.Name}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||||
|
<TextBlock Text="Username: " />
|
||||||
|
<TextBlock Text="{Binding Account.AccountId}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid Margin="0,5,0,5" Grid.Row="2" Grid.Column="0" ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Grid.Column="0" VerticalAlignment="Center" Text="Password: " />
|
||||||
|
<TextBox Grid.Column="1" PasswordChar="*" Text="{Binding Password, Mode=TwoWay}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="3"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Padding="30,5,30,5"
|
||||||
|
Content="Submit"
|
||||||
|
Click="Submit_Click"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
using AudibleUtilities;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Dinah.Core;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class LoginCallbackDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public Account Account { get; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public LoginCallbackDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
|
Account = accounts.FirstOrDefault();
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public LoginCallbackDialog(Account account) : this()
|
||||||
|
{
|
||||||
|
Account = account;
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { email = Account?.AccountId?.ToMask(), passwordLength = Password?.Length });
|
||||||
|
|
||||||
|
return base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="350" d:DesignHeight="200"
|
||||||
|
MinWidth="350" MinHeight="200"
|
||||||
|
Width="350" Height="200"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login.LoginChoiceEagerDialog"
|
||||||
|
Title="Audible Login"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico" >
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,Auto,*" ColumnDefinitions="*" Margin="5">
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="0"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
|
||||||
|
<TextBlock Text="Locale: " />
|
||||||
|
<TextBlock Text="{Binding Account.Locale.Name}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="1"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
|
||||||
|
<TextBlock Text="Username: " />
|
||||||
|
<TextBlock Text="{Binding Account.AccountId}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
ColumnDefinitions="Auto,*">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Password: " />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Column="1"
|
||||||
|
PasswordChar="*"
|
||||||
|
Text="{Binding Password, Mode=TwoWay}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="3"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Foreground="Blue"
|
||||||
|
TextDecorations="Underline"
|
||||||
|
Tapped="ExternalLoginLink_Tapped"
|
||||||
|
Text="Or click here to log in with your browser." />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="This more advanced login is recommended if you're experiencing errors logging in the conventional way above or if you're not comfortable typing your password here." />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
using AudibleApi;
|
||||||
|
using AudibleUtilities;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class LoginChoiceEagerDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public Account Account { get; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public LoginMethod LoginMethod { get; private set; }
|
||||||
|
|
||||||
|
public LoginChoiceEagerDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
|
Account = accounts.FirstOrDefault();
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public LoginChoiceEagerDialog(Account account):this()
|
||||||
|
{
|
||||||
|
Account = account;
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void ExternalLoginLink_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
LoginMethod = LoginMethod.External;
|
||||||
|
await SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="650" d:DesignHeight="500"
|
||||||
|
Width="650" Height="500"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login.LoginExternalDialog"
|
||||||
|
Title="Audible Login External"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*,Auto,*" ColumnDefinitions="*" Margin="5">
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="0"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
|
||||||
|
<TextBlock Text="Locale: " />
|
||||||
|
<TextBlock Text="{Binding Account.Locale.Name}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="1"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
|
||||||
|
<TextBlock Text="Username: " />
|
||||||
|
<TextBlock Text="{Binding Account.AccountId}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
RowDefinitions="Auto,*,Auto"
|
||||||
|
ColumnDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Paste this URL into your browser:" />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
IsReadOnly="True"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding ExternalLoginUrl}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,5,0,0"
|
||||||
|
Content="Copy URL to Clipboard"
|
||||||
|
Click="CopyUrlToClipboard_Click" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,5,0,0"
|
||||||
|
Content="Launch in Browser"
|
||||||
|
Click="LaunchInBrowser_Click" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="3"
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Text="tl;dr : an ERROR on Amazon is GOOD. Sorry, I can't control their weird login" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="Login with your Amazon/Audible credentials.
|
||||||
|

After login is complete, your browser will show you an error page similar to:
|
||||||
|

 Looking for Something?
|
||||||
|

 We're sorry. The Web address you entered is not a functioning page on our site
|
||||||
|

Don't worry -- this is ACTUALLY A SUCCESSFUL LOGIN.
|
||||||
|

Copy the current url from your browser's address bar and paste it here:
|
||||||
|
" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,5,0,5"
|
||||||
|
RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding ResponseUrl, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,5,0,0"
|
||||||
|
Padding="30,3,30,3" HorizontalAlignment="Right"
|
||||||
|
Content="Submit"
|
||||||
|
Click="Submit_Click" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
using AudibleApi;
|
||||||
|
using AudibleUtilities;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Dinah.Core;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class LoginExternalDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public Account Account { get; }
|
||||||
|
public string ExternalLoginUrl { get; }
|
||||||
|
public string ResponseUrl { get; set; }
|
||||||
|
|
||||||
|
public LoginExternalDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
|
Account = accounts.FirstOrDefault();
|
||||||
|
ExternalLoginUrl = "ht" + "tps://us.audible.com/Test_url";
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public LoginExternalDialog(Account account, string loginUrl):this()
|
||||||
|
{
|
||||||
|
Account = account;
|
||||||
|
ExternalLoginUrl = loginUrl;
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginExternalDialog(Account account)
|
||||||
|
{
|
||||||
|
Account = account;
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { ResponseUrl });
|
||||||
|
if (!Uri.TryCreate(ResponseUrl, UriKind.Absolute, out var result))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Invalid response URL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async void CopyUrlToClipboard_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await Application.Current.Clipboard.SetTextAsync(ExternalLoginUrl);
|
||||||
|
|
||||||
|
public void LaunchInBrowser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> Go.To.Url(ExternalLoginUrl);
|
||||||
|
|
||||||
|
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="160"
|
||||||
|
MinWidth="400" MinHeight="160"
|
||||||
|
MaxWidth="400" MaxHeight="160"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login.MfaDialog"
|
||||||
|
Title="Two-Step Verification"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="10,0,10,10" Name="rbStackPanel" Orientation="Vertical"/>
|
||||||
|
<Button Grid.Row="1" Content="Submit" Margin="10" Padding="30,5,30,5" Click="Submit_Click" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ReactiveUI;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Data;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class MfaDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public string SelectedName { get; private set; }
|
||||||
|
public string SelectedValue { get; private set; }
|
||||||
|
private RbValues Values { get; } = new();
|
||||||
|
|
||||||
|
public MfaDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
var mfaConfig = new AudibleApi.MfaConfig { Title = "My title" };
|
||||||
|
mfaConfig.Buttons.Add(new() { Text = "Enter the OTP from the authenticator app", Name = "otpDeviceContext", Value = "aAbBcC=, TOTP" });
|
||||||
|
mfaConfig.Buttons.Add(new() { Text = "Send an SMS to my number ending with 123", Name = "otpDeviceContext", Value = "dDeEfE=, SMS" });
|
||||||
|
mfaConfig.Buttons.Add(new() { Text = "Call me on my number ending with 123", Name = "otpDeviceContext", Value = "dDeEfE=, VOICE" });
|
||||||
|
|
||||||
|
loadRadioButtons(mfaConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MfaDialog(AudibleApi.MfaConfig mfaConfig) : this()
|
||||||
|
{
|
||||||
|
loadRadioButtons(mfaConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRadioButtons(AudibleApi.MfaConfig mfaConfig)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(mfaConfig.Title))
|
||||||
|
Title = mfaConfig.Title;
|
||||||
|
|
||||||
|
rbStackPanel = this.Find<StackPanel>(nameof(rbStackPanel));
|
||||||
|
|
||||||
|
foreach (var conf in mfaConfig.Buttons)
|
||||||
|
{
|
||||||
|
var rb = new RbValue(conf);
|
||||||
|
Values.AddButton(rb);
|
||||||
|
|
||||||
|
RadioButton radioButton = new()
|
||||||
|
{
|
||||||
|
Content = new TextBlock { Text = conf.Text },
|
||||||
|
Margin = new Thickness(0, 10, 0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
radioButton.Bind(
|
||||||
|
RadioButton.IsCheckedProperty,
|
||||||
|
new Binding
|
||||||
|
{
|
||||||
|
Source = rb,
|
||||||
|
Path = nameof(rb.IsChecked)
|
||||||
|
});
|
||||||
|
|
||||||
|
rbStackPanel.Children.Add(radioButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
var selected = Values.CheckedButton;
|
||||||
|
|
||||||
|
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new
|
||||||
|
{
|
||||||
|
text = selected?.Text,
|
||||||
|
name = selected?.Name,
|
||||||
|
value = selected?.Value
|
||||||
|
});
|
||||||
|
if (selected is null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("No MFA option selected", "None selected", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedName = selected.Name;
|
||||||
|
SelectedValue = selected.Value;
|
||||||
|
|
||||||
|
await base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
|
||||||
|
|
||||||
|
private class RbValue : ViewModels.ViewModelBase
|
||||||
|
{
|
||||||
|
private bool _isChecked;
|
||||||
|
public bool IsChecked
|
||||||
|
{
|
||||||
|
get => _isChecked;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _isChecked, value);
|
||||||
|
}
|
||||||
|
public AudibleApi.MfaConfigButton MfaConfigButton { get; }
|
||||||
|
public RbValue(AudibleApi.MfaConfigButton mfaConfig)
|
||||||
|
{
|
||||||
|
MfaConfigButton = mfaConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RbValues
|
||||||
|
{
|
||||||
|
private List<RbValue> ButtonValues { get; } = new();
|
||||||
|
|
||||||
|
public AudibleApi.MfaConfigButton CheckedButton => ButtonValues.SingleOrDefault(rb => rb.IsChecked)?.MfaConfigButton;
|
||||||
|
|
||||||
|
public void AddButton(RbValue rbValue)
|
||||||
|
{
|
||||||
|
if (ButtonValues.Contains(rbValue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
rbValue.PropertyChanged += RbValue_PropertyChanged;
|
||||||
|
rbValue.IsChecked = ButtonValues.Count == 0;
|
||||||
|
ButtonValues.Add(rbValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RbValue_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var button = sender as RbValue;
|
||||||
|
|
||||||
|
if (button.IsChecked)
|
||||||
|
{
|
||||||
|
foreach (var rb in ButtonValues.Where(rb => rb != button))
|
||||||
|
rb.IsChecked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="140" d:DesignHeight="100"
|
||||||
|
MinWidth="140" MinHeight="100"
|
||||||
|
MaxWidth="140" MaxHeight="100"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.Login._2faCodeDialog"
|
||||||
|
Title="2FA Code"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Margin="5"
|
||||||
|
TextAlignment="Center"
|
||||||
|
Text="Enter 2FA Code" />
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
Margin="5,0,5,0"
|
||||||
|
Grid.Row="1"
|
||||||
|
Text="{Binding Code, Mode=TwoWay}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Margin="5"
|
||||||
|
Grid.Row="2"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Content="Submit"
|
||||||
|
Click="Submit_Click" />
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs.Login
|
||||||
|
{
|
||||||
|
public partial class _2faCodeDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
public _2faCodeDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { Code });
|
||||||
|
|
||||||
|
return base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,7 +28,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void GoToGithub_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private void GoToGithub_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var url = "https://github.com/rmcrackan/Libation/issues";
|
var url = "https://github.com/rmcrackan/Libation/issues";
|
||||||
try
|
try
|
||||||
@ -37,7 +37,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
await MessageBox.Show($"Error opening url\r\n{url}", "Error opening url", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"Error opening url\r\n{url}", "Error opening url", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
await MessageBox.Show($"Error opening folder\r\n{dir}", "Error opening folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"Error opening folder\r\n{dir}", "Error opening folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,12 +30,16 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
settingsDisp.SaveSettings(config);
|
if (!settingsDisp.SaveSettings(config))
|
||||||
|
return;
|
||||||
|
|
||||||
await MessageBox.VerboseLoggingWarning_ShowIfTrue();
|
await MessageBox.VerboseLoggingWarning_ShowIfTrue();
|
||||||
await base.SaveAndCloseAsync();
|
await base.SaveAndCloseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> await SaveAndCloseAsync();
|
=> await SaveAndCloseAsync();
|
||||||
|
|
||||||
@ -44,7 +48,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
Go.To.Folder(((LongPath)Configuration.Instance.LibationFiles).ShortPathName);
|
Go.To.Folder(((LongPath)Configuration.Instance.LibationFiles).ShortPathName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async void EditFolderTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EditFolderTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var newTemplate = await editTemplate(Templates.Folder, settingsDisp.DownloadDecryptSettings.FolderTemplate);
|
var newTemplate = await editTemplate(Templates.Folder, settingsDisp.DownloadDecryptSettings.FolderTemplate);
|
||||||
@ -52,7 +55,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
settingsDisp.DownloadDecryptSettings.FolderTemplate = newTemplate;
|
settingsDisp.DownloadDecryptSettings.FolderTemplate = newTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var newTemplate = await editTemplate(Templates.File, settingsDisp.DownloadDecryptSettings.FileTemplate);
|
var newTemplate = await editTemplate(Templates.File, settingsDisp.DownloadDecryptSettings.FileTemplate);
|
||||||
@ -60,7 +62,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
settingsDisp.DownloadDecryptSettings.FileTemplate = newTemplate;
|
settingsDisp.DownloadDecryptSettings.FileTemplate = newTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var newTemplate = await editTemplate(Templates.ChapterFile, settingsDisp.DownloadDecryptSettings.ChapterFileTemplate);
|
var newTemplate = await editTemplate(Templates.ChapterFile, settingsDisp.DownloadDecryptSettings.ChapterFileTemplate);
|
||||||
@ -68,7 +69,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
settingsDisp.DownloadDecryptSettings.ChapterFileTemplate = newTemplate;
|
settingsDisp.DownloadDecryptSettings.ChapterFileTemplate = newTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async void EditCharReplacementButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EditCharReplacementButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var form = new LibationWinForms.Dialogs.EditReplacementChars(config);
|
var form = new LibationWinForms.Dialogs.EditReplacementChars(config);
|
||||||
@ -83,7 +83,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<string> editTemplate(Templates template, string existingTemplate)
|
private async Task<string> editTemplate(Templates template, string existingTemplate)
|
||||||
{
|
{
|
||||||
var form = new EditTemplateDialog(template, existingTemplate);
|
var form = new EditTemplateDialog(template, existingTemplate);
|
||||||
@ -96,7 +95,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
internal interface ISettingsDisplay
|
internal interface ISettingsDisplay
|
||||||
{
|
{
|
||||||
void LoadSettings(Configuration config);
|
void LoadSettings(Configuration config);
|
||||||
void SaveSettings(Configuration config);
|
bool SaveSettings(Configuration config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SettingsPages : ISettingsDisplay
|
public class SettingsPages : ISettingsDisplay
|
||||||
@ -119,19 +118,19 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
AudioSettings = new(config);
|
AudioSettings = new(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings(Configuration config)
|
public bool SaveSettings(Configuration config)
|
||||||
{
|
{
|
||||||
ImportantSettings.SaveSettings(config);
|
var result = ImportantSettings.SaveSettings(config);
|
||||||
ImportSettings.SaveSettings(config);
|
result &= ImportSettings.SaveSettings(config);
|
||||||
DownloadDecryptSettings.SaveSettings(config);
|
result &= DownloadDecryptSettings.SaveSettings(config);
|
||||||
AudioSettings.SaveSettings(config);
|
result &= AudioSettings.SaveSettings(config);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImportantSettings : ISettingsDisplay
|
public class ImportantSettings : ISettingsDisplay
|
||||||
{
|
{
|
||||||
private static Func<string, string> desc { get; } = Configuration.GetDescription;
|
|
||||||
|
|
||||||
public ImportantSettings(Configuration config)
|
public ImportantSettings(Configuration config)
|
||||||
{
|
{
|
||||||
LoadSettings(config);
|
LoadSettings(config);
|
||||||
@ -145,12 +144,27 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
BetaOptIn = config.BetaOptIn;
|
BetaOptIn = config.BetaOptIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings(Configuration config)
|
public bool SaveSettings(Configuration config)
|
||||||
{
|
{
|
||||||
|
#region validation
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(BooksDirectory))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
LongPath lonNewBooks = BooksDirectory;
|
||||||
|
if (!System.IO.Directory.Exists(lonNewBooks))
|
||||||
|
System.IO.Directory.CreateDirectory(lonNewBooks);
|
||||||
config.Books = BooksDirectory;
|
config.Books = BooksDirectory;
|
||||||
config.SavePodcastsToParentFolder = SavePodcastsToParentFolder;
|
config.SavePodcastsToParentFolder = SavePodcastsToParentFolder;
|
||||||
config.LogLevel = LoggingLevel;
|
config.LogLevel = LoggingLevel;
|
||||||
config.BetaOptIn = BetaOptIn;
|
config.BetaOptIn = BetaOptIn;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -161,10 +175,10 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
Configuration.KnownDirectories.MyDocs
|
Configuration.KnownDirectories.MyDocs
|
||||||
};
|
};
|
||||||
|
|
||||||
public string BooksText => desc(nameof(Configuration.Books));
|
public string BooksText { get; } = Configuration.GetDescription(nameof(Configuration.Books));
|
||||||
public string SavePodcastsToParentFolderText => desc(nameof(Configuration.SavePodcastsToParentFolder));
|
public string SavePodcastsToParentFolderText { get; } = Configuration.GetDescription(nameof(Configuration.SavePodcastsToParentFolder));
|
||||||
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
|
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
|
||||||
public string BetaOptInText => desc(nameof(Configuration.BetaOptIn));
|
public string BetaOptInText { get; } = Configuration.GetDescription(nameof(Configuration.BetaOptIn));
|
||||||
|
|
||||||
public string BooksDirectory { get; set; }
|
public string BooksDirectory { get; set; }
|
||||||
public bool SavePodcastsToParentFolder { get; set; }
|
public bool SavePodcastsToParentFolder { get; set; }
|
||||||
@ -172,11 +186,8 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
public bool BetaOptIn { get; set; }
|
public bool BetaOptIn { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class ImportSettings : ISettingsDisplay
|
public class ImportSettings : ISettingsDisplay
|
||||||
{
|
{
|
||||||
private static Func<string, string> desc { get; } = Configuration.GetDescription;
|
|
||||||
|
|
||||||
public ImportSettings(Configuration config)
|
public ImportSettings(Configuration config)
|
||||||
{
|
{
|
||||||
LoadSettings(config);
|
LoadSettings(config);
|
||||||
@ -191,20 +202,21 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
AutoDownloadEpisodes = config.AutoDownloadEpisodes;
|
AutoDownloadEpisodes = config.AutoDownloadEpisodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings(Configuration config)
|
public bool SaveSettings(Configuration config)
|
||||||
{
|
{
|
||||||
config.AutoScan = AutoScan;
|
config.AutoScan = AutoScan;
|
||||||
config.ShowImportedStats = ShowImportedStats;
|
config.ShowImportedStats = ShowImportedStats;
|
||||||
config.ImportEpisodes = ImportEpisodes;
|
config.ImportEpisodes = ImportEpisodes;
|
||||||
config.DownloadEpisodes = DownloadEpisodes;
|
config.DownloadEpisodes = DownloadEpisodes;
|
||||||
config.AutoDownloadEpisodes = AutoDownloadEpisodes;
|
config.AutoDownloadEpisodes = AutoDownloadEpisodes;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AutoScanText => desc(nameof(Configuration.AutoScan));
|
public string AutoScanText { get; } = Configuration.GetDescription(nameof(Configuration.AutoScan));
|
||||||
public string ShowImportedStatsText => desc(nameof(Configuration.ShowImportedStats));
|
public string ShowImportedStatsText { get; } = Configuration.GetDescription(nameof(Configuration.ShowImportedStats));
|
||||||
public string ImportEpisodesText => desc(nameof(Configuration.ImportEpisodes));
|
public string ImportEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.ImportEpisodes));
|
||||||
public string DownloadEpisodesText => desc(nameof(Configuration.DownloadEpisodes));
|
public string DownloadEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadEpisodes));
|
||||||
public string AutoDownloadEpisodesText => desc(nameof(Configuration.AutoDownloadEpisodes));
|
public string AutoDownloadEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.AutoDownloadEpisodes));
|
||||||
|
|
||||||
public bool AutoScan { get; set; }
|
public bool AutoScan { get; set; }
|
||||||
public bool ShowImportedStats { get; set; }
|
public bool ShowImportedStats { get; set; }
|
||||||
@ -215,7 +227,6 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
|
|
||||||
public class DownloadDecryptSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
public class DownloadDecryptSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
||||||
{
|
{
|
||||||
private static Func<string, string> desc { get; } = Configuration.GetDescription;
|
|
||||||
|
|
||||||
private bool _badBookAsk;
|
private bool _badBookAsk;
|
||||||
private bool _badBookAbort;
|
private bool _badBookAbort;
|
||||||
@ -246,8 +257,28 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
: Configuration.GetKnownDirectory(config.InProgress);
|
: Configuration.GetKnownDirectory(config.InProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings(Configuration config)
|
public bool SaveSettings(Configuration config)
|
||||||
{
|
{
|
||||||
|
static void validationError(string text, string caption)
|
||||||
|
=> MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
|
||||||
|
// these 3 should do nothing. Configuration will only init these with a valid value. EditTemplateDialog ensures valid before returning
|
||||||
|
if (!Templates.Folder.IsValid(FolderTemplate))
|
||||||
|
{
|
||||||
|
validationError($"Not saving change to folder naming template. Invalid format.", "Invalid folder template");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Templates.File.IsValid(FileTemplate))
|
||||||
|
{
|
||||||
|
validationError($"Not saving change to file naming template. Invalid format.", "Invalid file template");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Templates.ChapterFile.IsValid(ChapterFileTemplate))
|
||||||
|
{
|
||||||
|
validationError($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
config.BadBook
|
config.BadBook
|
||||||
= BadBookAbort ? Configuration.BadBookAction.Abort
|
= BadBookAbort ? Configuration.BadBookAction.Abort
|
||||||
: BadBookRetry ? Configuration.BadBookAction.Retry
|
: BadBookRetry ? Configuration.BadBookAction.Retry
|
||||||
@ -260,24 +291,25 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
config.InProgress
|
config.InProgress
|
||||||
= InProgressDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
|
= InProgressDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
|
||||||
: Configuration.GetKnownDirectoryPath(InProgressDirectory);
|
: Configuration.GetKnownDirectoryPath(InProgressDirectory);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BadBookGroupboxText => desc(nameof(Configuration.BadBook));
|
public string BadBookGroupboxText { get; } = Configuration.GetDescription(nameof(Configuration.BadBook));
|
||||||
public string BadBookAskText { get; } = Configuration.BadBookAction.Ask.GetDescription();
|
public string BadBookAskText { get; } = Configuration.BadBookAction.Ask.GetDescription();
|
||||||
public string BadBookAbortText { get; } = Configuration.BadBookAction.Abort.GetDescription();
|
public string BadBookAbortText { get; } = Configuration.BadBookAction.Abort.GetDescription();
|
||||||
public string BadBookRetryText { get; } = Configuration.BadBookAction.Retry.GetDescription();
|
public string BadBookRetryText { get; } = Configuration.BadBookAction.Retry.GetDescription();
|
||||||
public string BadBookIgnoreText { get; } = Configuration.BadBookAction.Ignore.GetDescription();
|
public string BadBookIgnoreText { get; } = Configuration.BadBookAction.Ignore.GetDescription();
|
||||||
public string FolderTemplateText => desc(nameof(Configuration.FolderTemplate));
|
public string FolderTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||||
public string FileTemplateText => desc(nameof(Configuration.FileTemplate));
|
public string FileTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||||
public string ChapterFileTemplateText => desc(nameof(Configuration.ChapterFileTemplate));
|
public string ChapterFileTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||||
public string EditCharReplacementText => desc(nameof(Configuration.ReplacementCharacters));
|
public string EditCharReplacementText { get; } = Configuration.GetDescription(nameof(Configuration.ReplacementCharacters));
|
||||||
public string InProgressDescriptionText => desc(nameof(Configuration.InProgress));
|
public string InProgressDescriptionText { get; } = Configuration.GetDescription(nameof(Configuration.InProgress));
|
||||||
|
|
||||||
public string FolderTemplate { get => _folderTemplate; set { this.RaiseAndSetIfChanged(ref _folderTemplate, value); } }
|
public string FolderTemplate { get => _folderTemplate; set { this.RaiseAndSetIfChanged(ref _folderTemplate, value); } }
|
||||||
public string FileTemplate { get => _fileTemplate; set { this.RaiseAndSetIfChanged(ref _fileTemplate, value); } }
|
public string FileTemplate { get => _fileTemplate; set { this.RaiseAndSetIfChanged(ref _fileTemplate, value); } }
|
||||||
public string ChapterFileTemplate { get => _chapterFileTemplate; set { this.RaiseAndSetIfChanged(ref _chapterFileTemplate, value); } }
|
public string ChapterFileTemplate { get => _chapterFileTemplate; set { this.RaiseAndSetIfChanged(ref _chapterFileTemplate, value); } }
|
||||||
|
|
||||||
|
|
||||||
public bool BadBookAsk
|
public bool BadBookAsk
|
||||||
{
|
{
|
||||||
get => _badBookAsk;
|
get => _badBookAsk;
|
||||||
@ -346,7 +378,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
private int _lameBitrate;
|
private int _lameBitrate;
|
||||||
private int _lameVBRQuality;
|
private int _lameVBRQuality;
|
||||||
private string _chapterTitleTemplate;
|
private string _chapterTitleTemplate;
|
||||||
private static Func<string, string> desc { get; } = Configuration.GetDescription;
|
|
||||||
public AudioSettings(Configuration config)
|
public AudioSettings(Configuration config)
|
||||||
{
|
{
|
||||||
LoadSettings(config);
|
LoadSettings(config);
|
||||||
@ -371,7 +403,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
LameVBRQuality = config.LameVBRQuality;
|
LameVBRQuality = config.LameVBRQuality;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings(Configuration config)
|
public bool SaveSettings(Configuration config)
|
||||||
{
|
{
|
||||||
config.CreateCueSheet = CreateCueSheet;
|
config.CreateCueSheet = CreateCueSheet;
|
||||||
config.AllowLibationFixup = AllowLibationFixup;
|
config.AllowLibationFixup = AllowLibationFixup;
|
||||||
@ -389,17 +421,19 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
config.LameMatchSourceBR = LameMatchSource;
|
config.LameMatchSourceBR = LameMatchSource;
|
||||||
config.LameBitrate = LameBitrate;
|
config.LameBitrate = LameBitrate;
|
||||||
config.LameVBRQuality = LameVBRQuality;
|
config.LameVBRQuality = LameVBRQuality;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CreateCueSheetText => desc(nameof(Configuration.CreateCueSheet));
|
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));
|
||||||
public string AllowLibationFixupText => desc(nameof(Configuration.AllowLibationFixup));
|
public string AllowLibationFixupText { get; } = Configuration.GetDescription(nameof(Configuration.AllowLibationFixup));
|
||||||
public string DownloadCoverArtText => desc(nameof(Configuration.DownloadCoverArt));
|
public string DownloadCoverArtText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadCoverArt));
|
||||||
public string RetainAaxFileText => desc(nameof(Configuration.RetainAaxFile));
|
public string RetainAaxFileText { get; } = Configuration.GetDescription(nameof(Configuration.RetainAaxFile));
|
||||||
public string SplitFilesByChapterText => desc(nameof(Configuration.SplitFilesByChapter));
|
public string SplitFilesByChapterText { get; } = Configuration.GetDescription(nameof(Configuration.SplitFilesByChapter));
|
||||||
public string MergeOpeningEndCreditsText => desc(nameof(Configuration.MergeOpeningAndEndCredits));
|
public string MergeOpeningEndCreditsText { get; } = Configuration.GetDescription(nameof(Configuration.MergeOpeningAndEndCredits));
|
||||||
public string StripAudibleBrandingText => desc(nameof(Configuration.StripAudibleBrandAudio));
|
public string StripAudibleBrandingText { get; } = Configuration.GetDescription(nameof(Configuration.StripAudibleBrandAudio));
|
||||||
public string StripUnabridgedText => desc(nameof(Configuration.StripUnabridged));
|
public string StripUnabridgedText { get; } = Configuration.GetDescription(nameof(Configuration.StripUnabridged));
|
||||||
public string ChapterTitleTemplateText => desc(nameof(Configuration.ChapterTitleTemplate));
|
public string ChapterTitleTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.ChapterTitleTemplate));
|
||||||
|
|
||||||
public bool CreateCueSheet { get; set; }
|
public bool CreateCueSheet { get; set; }
|
||||||
public bool DownloadCoverArt { get; set; }
|
public bool DownloadCoverArt { get; set; }
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
{
|
{
|
||||||
private void Configure_Export() { }
|
private void Configure_Export() { }
|
||||||
|
|
||||||
public async void exportLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void exportLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -37,11 +37,11 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await MessageBox.Show("Library exported to:\r\n" + saveFileDialog.FileName);
|
MessageBox.Show("Library exported to:\r\n" + saveFileDialog.FileName);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await MessageBox.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex);
|
MessageBox.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
|
||||||
// re-apply last good filter
|
// re-apply last good filter
|
||||||
await performFilter(lastGoodFilter);
|
await performFilter(lastGoodFilter);
|
||||||
|
|||||||
@ -40,7 +40,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
|
|
||||||
public async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var result = await MessageBox.Show(
|
var result = MessageBox.Show(
|
||||||
"This converts all m4b titles in your library to mp3 files. Original files are not deleted."
|
"This converts all m4b titles in your library to mp3 files. Original files are not deleted."
|
||||||
+ "\r\nFor large libraries this will take a long time and will take up more disk space."
|
+ "\r\nFor large libraries this will take a long time and will take up more disk space."
|
||||||
+ "\r\n\r\nContinue?"
|
+ "\r\n\r\nContinue?"
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
SetQueueCollapseState(collapseState);
|
SetQueueCollapseState(collapseState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ProductsDisplay_LiberateClicked(object sender, LibraryBook libraryBook)
|
public void ProductsDisplay_LiberateClicked(object sender, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -38,7 +38,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
if (!Go.To.File(filePath?.ShortPathName))
|
if (!Go.To.File(filePath?.ShortPathName))
|
||||||
{
|
{
|
||||||
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
|
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
|
||||||
await MessageBox.Show($"File not found" + suffix);
|
MessageBox.Show($"File not found" + suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
// in autoScan, new books SHALL NOT show dialog
|
// in autoScan, new books SHALL NOT show dialog
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LibraryCommands.ImportAccountAsync(Login.WinformLoginChoiceEager.ApiExtendedFunc, accounts);
|
await LibraryCommands.ImportAccountAsync(Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
|
|
||||||
public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
|
MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
|
||||||
await new Dialogs.AccountsDialog().ShowDialog(this);
|
await new Dialogs.AccountsDialog().ShowDialog(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,15 +63,15 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(Login.WinformLoginChoiceEager.ApiExtendedFunc, accounts);
|
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||||
|
|
||||||
// this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop
|
// this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop
|
||||||
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
||||||
await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await MessageBox.ShowAdminAlert(
|
MessageBox.ShowAdminAlert(
|
||||||
this,
|
this,
|
||||||
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
|
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
|
||||||
"Error importing library",
|
"Error importing library",
|
||||||
|
|||||||
@ -14,6 +14,6 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
public async void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => await new Dialogs.SettingsDialog().ShowDialog(this);
|
public async void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => await new Dialogs.SettingsDialog().ShowDialog(this);
|
||||||
|
|
||||||
public async void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> await MessageBox.Show($"Running Libation version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}");
|
=> MessageBox.Show($"Running Libation version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
|
|
||||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||||
|
|
||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
"Are you sure you want to replace tags in {0}?",
|
"Are you sure you want to replace tags in {0}?",
|
||||||
@ -71,7 +71,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
|
|
||||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||||
|
|
||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
"Are you sure you want to replace downloaded status in {0}?",
|
"Are you sure you want to replace downloaded status in {0}?",
|
||||||
@ -89,7 +89,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
{
|
{
|
||||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||||
|
|
||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
"Are you sure you want to remove {0} from Libation's library?",
|
"Are you sure you want to remove {0} from Libation's library?",
|
||||||
|
|||||||
@ -67,8 +67,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
|
|
||||||
private async void MainWindow_Opened(object sender, EventArgs e)
|
private async void MainWindow_Opened(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
//var dialog = new EditReplacementChars();
|
|
||||||
//await dialog.ShowDialog(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProductsDisplay_Initialized1(object sender, EventArgs e)
|
public void ProductsDisplay_Initialized1(object sender, EventArgs e)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user