Use WebLoginDialog as primary login method on Win10+
This commit is contained in:
parent
53b5c1b902
commit
df2936e0b6
46
Source/LibationWinForms/Dialogs/Login/WebLoginDialog.Designer.cs
generated
Normal file
46
Source/LibationWinForms/Dialogs/Login/WebLoginDialog.Designer.cs
generated
Normal file
@ -0,0 +1,46 @@
|
||||
namespace LibationWinForms.Login
|
||||
{
|
||||
partial class WebLoginDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
SuspendLayout();
|
||||
//
|
||||
// WebLoginDialog
|
||||
//
|
||||
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
ClientSize = new System.Drawing.Size(484, 761);
|
||||
Name = "WebLoginDialog";
|
||||
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
Text = "Audible Login";
|
||||
ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
67
Source/LibationWinForms/Dialogs/Login/WebLoginDialog.cs
Normal file
67
Source/LibationWinForms/Dialogs/Login/WebLoginDialog.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using Dinah.Core;
|
||||
using Microsoft.Web.WebView2.WinForms;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.Login
|
||||
{
|
||||
public partial class WebLoginDialog : Form
|
||||
{
|
||||
public string ResponseUrl { get; private set; }
|
||||
private readonly string loginUrl;
|
||||
private readonly string accountID;
|
||||
private readonly WebView2 webView = new();
|
||||
public WebLoginDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
webView.Dock = DockStyle.Fill;
|
||||
Controls.Add(webView);
|
||||
Shown += WebLoginDialog_Shown;
|
||||
this.SetLibationIcon();
|
||||
}
|
||||
|
||||
public WebLoginDialog(string accountID, string loginUrl) : this()
|
||||
{
|
||||
this.accountID = ArgumentValidator.EnsureNotNullOrWhiteSpace(accountID, nameof(accountID));
|
||||
this.loginUrl = ArgumentValidator.EnsureNotNullOrWhiteSpace(loginUrl, nameof(loginUrl));
|
||||
}
|
||||
|
||||
private async void WebLoginDialog_Shown(object sender, EventArgs e)
|
||||
{
|
||||
await webView.EnsureCoreWebView2Async();
|
||||
webView.CoreWebView2.NavigationStarting += CoreWebView2_NavigationStarting;
|
||||
webView.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
||||
webView.CoreWebView2.Navigate(loginUrl);
|
||||
}
|
||||
|
||||
private async void CoreWebView2_DOMContentLoaded(object sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs e)
|
||||
{
|
||||
await webView.CoreWebView2.ExecuteScriptAsync(getScript(accountID));
|
||||
}
|
||||
|
||||
private static string getScript(string accountID) => $$"""
|
||||
(function() {
|
||||
var inputs = document.getElementsByTagName('input');
|
||||
for (index = 0; index < inputs.length; ++index) {
|
||||
if (inputs[index].name.includes('email')) {
|
||||
inputs[index].value = '{{accountID}}';
|
||||
}
|
||||
if (inputs[index].name.includes('password')) {
|
||||
inputs[index].focus();
|
||||
}
|
||||
}
|
||||
})()
|
||||
""";
|
||||
|
||||
private void CoreWebView2_NavigationStarting(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs e)
|
||||
{
|
||||
if (new Uri(e.Uri).AbsolutePath.Contains("/ap/maplanding"))
|
||||
{
|
||||
ResponseUrl = e.Uri;
|
||||
e.Cancel = true;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Source/LibationWinForms/Dialogs/Login/WebLoginDialog.resx
Normal file
60
Source/LibationWinForms/Dialogs/Login/WebLoginDialog.resx
Normal file
@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@ -1,15 +1,22 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.Dialogs.Login
|
||||
{
|
||||
public abstract class WinformLoginBase
|
||||
{
|
||||
/// <returns>True if ShowDialog's DialogResult == OK</returns>
|
||||
protected static bool ShowDialog(System.Windows.Forms.Form dialog)
|
||||
private readonly IWin32Window _owner;
|
||||
protected WinformLoginBase(IWin32Window owner)
|
||||
{
|
||||
var result = dialog.ShowDialog();
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <returns>True if ShowDialog's DialogResult == OK</returns>
|
||||
protected bool ShowDialog(Form dialog)
|
||||
{
|
||||
var result = dialog.ShowDialog(_owner);
|
||||
Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result });
|
||||
return result == System.Windows.Forms.DialogResult.OK;
|
||||
return result == DialogResult.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using AudibleApi;
|
||||
using AudibleUtilities;
|
||||
using LibationWinForms.Dialogs.Login;
|
||||
@ -12,7 +13,7 @@ namespace LibationWinForms.Login
|
||||
|
||||
public string DeviceName { get; } = "Libation";
|
||||
|
||||
public WinformLoginCallback(Account account)
|
||||
public WinformLoginCallback(Account account, IWin32Window owner) : base(owner)
|
||||
{
|
||||
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using AudibleApi;
|
||||
using AudibleUtilities;
|
||||
using LibationWinForms.Dialogs.Login;
|
||||
@ -9,20 +10,31 @@ namespace LibationWinForms.Login
|
||||
public class WinformLoginChoiceEager : WinformLoginBase, 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 WinformLoginChoiceEager(account));
|
||||
public static Func<Account, Task<ApiExtended>> CreateApiExtendedFunc(IWin32Window owner) => a => ApiExtendedFunc(a, owner);
|
||||
|
||||
private static async Task<ApiExtended> ApiExtendedFunc(Account account, IWin32Window owner)
|
||||
=> await ApiExtended.CreateAsync(account, new WinformLoginChoiceEager(account, owner));
|
||||
|
||||
public ILoginCallback LoginCallback { get; private set; }
|
||||
|
||||
private Account _account { get; }
|
||||
|
||||
public WinformLoginChoiceEager(Account account)
|
||||
private WinformLoginChoiceEager(Account account, IWin32Window owner) : base(owner)
|
||||
{
|
||||
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||
LoginCallback = new WinformLoginCallback(_account);
|
||||
LoginCallback = new WinformLoginCallback(_account, owner);
|
||||
}
|
||||
|
||||
public Task<ChoiceOut> StartAsync(ChoiceIn choiceIn)
|
||||
{
|
||||
if (Environment.OSVersion.Version.Major >= 10)
|
||||
{
|
||||
using var browserLogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
|
||||
|
||||
if (ShowDialog(browserLogin))
|
||||
return Task.FromResult(ChoiceOut.External(browserLogin.ResponseUrl));
|
||||
}
|
||||
|
||||
using var dialog = new LoginChoiceEagerDialog(_account);
|
||||
|
||||
if (!ShowDialog(dialog) || (dialog.LoginMethod is LoginMethod.Api && string.IsNullOrWhiteSpace(dialog.Password)))
|
||||
@ -33,13 +45,13 @@ namespace LibationWinForms.Login
|
||||
case LoginMethod.Api:
|
||||
return Task.FromResult(ChoiceOut.WithApi(dialog.Email, dialog.Password));
|
||||
case LoginMethod.External:
|
||||
{
|
||||
using var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl);
|
||||
{
|
||||
using var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl);
|
||||
return Task.FromResult(
|
||||
ShowDialog(externalDialog)
|
||||
? ChoiceOut.External(externalDialog.ResponseUrl)
|
||||
: null);
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Exception($"Unknown {nameof(LoginMethod)} value");
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ namespace LibationWinForms
|
||||
// in autoScan, new books SHALL NOT show dialog
|
||||
try
|
||||
{
|
||||
Task importAsync() => LibraryCommands.ImportAccountAsync(Login.WinformLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||
Task importAsync() => LibraryCommands.ImportAccountAsync(Login.WinformLoginChoiceEager.CreateApiExtendedFunc(this), accounts);
|
||||
if (InvokeRequired)
|
||||
await Invoke(importAsync);
|
||||
else
|
||||
|
||||
@ -74,7 +74,7 @@ namespace LibationWinForms
|
||||
{
|
||||
try
|
||||
{
|
||||
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(Login.WinformLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(Login.WinformLoginChoiceEager.CreateApiExtendedFunc(this), accounts);
|
||||
|
||||
// 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)
|
||||
|
||||
@ -279,7 +279,7 @@ namespace LibationWinForms.GridView
|
||||
.Select(lbe => lbe.LibraryBook)
|
||||
.Where(lb => !lb.Book.HasLiberated());
|
||||
|
||||
var removedBooks = await LibraryCommands.FindInactiveBooks(Login.WinformLoginChoiceEager.ApiExtendedFunc, lib, accounts);
|
||||
var removedBooks = await LibraryCommands.FindInactiveBooks(Login.WinformLoginChoiceEager.CreateApiExtendedFunc(this), lib, accounts);
|
||||
|
||||
var removable = allBooks.Where(lbe => removedBooks.Any(rb => rb.Book.AudibleProductId == lbe.AudibleProductId)).ToList();
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="7.2.2.1" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1777-prerelease" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user