Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop WPF-based UI helpers on Windows #1417

Merged
merged 6 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
msauth: replace WPF progress window with Avalonia
  • Loading branch information
mjcheetham committed Sep 20, 2023
commit 1e904d1035641393ce87c8297670574ff0197f32
96 changes: 10 additions & 86 deletions src/shared/Core/Authentication/MicrosoftAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
using System.Text;
using System.Threading;
using GitCredentialManager.UI;
using GitCredentialManager.UI.Controls;
using GitCredentialManager.UI.ViewModels;
using GitCredentialManager.UI.Views;
using Microsoft.Identity.Client.AppConfig;

#if NETFRAMEWORK
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Identity.Client.Broker;
#endif

Expand Down Expand Up @@ -118,10 +117,6 @@ public class MicrosoftAuthentication : AuthenticationBase, IMicrosoftAuthenticat
"live", "liveconnect", "liveid",
};

#if NETFRAMEWORK
private DummyWindow _dummyWindow;
#endif

public MicrosoftAuthentication(ICommandContext context)
: base(context) { }

Expand All @@ -130,6 +125,8 @@ public MicrosoftAuthentication(ICommandContext context)
public async Task<IMicrosoftAuthenticationResult> GetTokenForUserAsync(
string authority, string clientId, Uri redirectUri, string[] scopes, string userName, bool msaPt)
{
var uiCts = new CancellationTokenSource();

// Check if we can and should use OS broker authentication
bool useBroker = CanUseBroker();
Context.Trace.WriteLine(useBroker
Expand All @@ -144,7 +141,7 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenForUserAsync(
try
{
// Create the public client application for authentication
IPublicClientApplication app = await CreatePublicClientApplicationAsync(authority, clientId, redirectUri, useBroker, msaPt);
IPublicClientApplication app = await CreatePublicClientApplicationAsync(authority, clientId, redirectUri, useBroker, msaPt, uiCts);

AuthenticationResult result = null;

Expand Down Expand Up @@ -261,10 +258,8 @@ public async Task<IMicrosoftAuthenticationResult> GetTokenForUserAsync(
}
finally
{
#if NETFRAMEWORK
// If we created a dummy window during authentication we should dispose of it now that we're done
_dummyWindow?.Dispose();
#endif
// If we created some global UI (e.g. progress) during authentication we should dismiss them now that we're done
uiCts.Cancel();
}
}

Expand Down Expand Up @@ -451,8 +446,8 @@ private async Task<AuthenticationResult> GetAccessTokenSilentlyAsync(
}
}

private async Task<IPublicClientApplication> CreatePublicClientApplicationAsync(
string authority, string clientId, Uri redirectUri, bool enableBroker, bool msaPt)
private async Task<IPublicClientApplication> CreatePublicClientApplicationAsync(string authority,
string clientId, Uri redirectUri, bool enableBroker, bool msaPt, CancellationTokenSource uiCts)
{
var httpFactoryAdaptor = new MsalHttpClientFactoryAdaptor(Context.HttpClientFactory);

Expand Down Expand Up @@ -495,11 +490,8 @@ private async Task<IPublicClientApplication> CreatePublicClientApplicationAsync(
}
else if (enableBroker) // Only actually need to set a parent window when using the Windows broker
{
#if NETFRAMEWORK
Context.Trace.WriteLine($"Using dummy parent window for MSAL authentication dialogs.");
_dummyWindow = new DummyWindow();
appBuilder.WithParentActivityOrWindow(_dummyWindow.ShowAndGetHandle);
#endif
Context.Trace.WriteLine("Using progress parent window for MSAL authentication dialogs.");
appBuilder.WithParentActivityOrWindow(() => ProgressWindow.ShowAndGetHandle(uiCts.Token));
}
}
}
Expand Down Expand Up @@ -899,73 +891,5 @@ public MsalResult(AuthenticationResult msalResult)
public string AccessToken => _msalResult.AccessToken;
public string AccountUpn => _msalResult.Account?.Username;
}

#if NETFRAMEWORK
private class DummyWindow : IDisposable
{
private readonly Thread _staThread;
private readonly ManualResetEventSlim _readyEvent;
private Form _window;
private IntPtr _handle;

public DummyWindow()
{
_staThread = new Thread(ThreadProc);
_staThread.SetApartmentState(ApartmentState.STA);
_readyEvent = new ManualResetEventSlim();
}

public IntPtr ShowAndGetHandle()
{
_staThread.Start();
_readyEvent.Wait();
return _handle;
}

public void Dispose()
{
_window?.Invoke(() => _window.Close());

if (_staThread.IsAlive)
{
_staThread.Join();
}
}

private void ThreadProc()
{
System.Windows.Forms.Application.EnableVisualStyles();
_window = new Form
{
TopMost = true,
ControlBox = false,
MaximizeBox = false,
MinimizeBox = false,
ClientSize = new Size(182, 46),
FormBorderStyle = FormBorderStyle.None,
StartPosition = FormStartPosition.CenterScreen,
};

var progress = new ProgressBar
{
Style = ProgressBarStyle.Marquee,
Location = new Point(12, 12),
Size = new Size(158, 23),
MarqueeAnimationSpeed = 30,
};

_window.Controls.Add(progress);
_window.Shown += (s, e) =>
{
_handle = _window.Handle;
_readyEvent.Set();
};

_window.ShowDialog();
_window.Dispose();
_window = null;
}
}
#endif
}
}
14 changes: 14 additions & 0 deletions src/shared/Core/UI/Controls/ProgressWindow.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<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="182" d:DesignHeight="46"
SizeToContent="WidthAndHeight" CanResize="False" Topmost="True"
ExtendClientAreaChromeHints="NoChrome" ExtendClientAreaToDecorationsHint="True"
ShowInTaskbar="False" Title="Git Credential Manager" WindowStartupLocation="CenterScreen"
x:Class="GitCredentialManager.UI.Controls.ProgressWindow">
<ProgressBar Orientation="Horizontal"
IsIndeterminate="True"
Margin="20"
Width="158" Height="23" />
</Window>
37 changes: 37 additions & 0 deletions src/shared/Core/UI/Controls/ProgressWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace GitCredentialManager.UI.Controls;

public partial class ProgressWindow : Window
{
public ProgressWindow()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}

public static IntPtr ShowAndGetHandle(CancellationToken ct)
{
var tsc = new TaskCompletionSource<IntPtr>();

Window CreateWindow()
{
var window = new ProgressWindow();
window.Loaded += (s, e) => tsc.SetResult(window.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero);
return window;
}

Task _ = AvaloniaUi.ShowWindowAsync(CreateWindow, IntPtr.Zero, ct);

return tsc.Task.GetAwaiter().GetResult();
}
}