1
0
Форкнуть 0
Git-Credential-Manager-for-.../Bitbucket.Authentication/Src/Gui.cs

138 строки
5.3 KiB
C#

/**** Git Credential Manager for Windows ****
*
* Copyright (c) GitHub Corporation
* All rights reserved.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the """"Software""""), to deal
* in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
**/
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using GitHub.Shared.Controls;
using GitHub.Shared.ViewModels;
using Microsoft.Alm.Authentication;
namespace Atlassian.Bitbucket.Authentication
{
public interface IGui : IRuntimeService
{
/// <summary>
/// Presents the user with `<paramref name="windowCreator"/>` with the `<paramref name="viewModel"/>`.
/// <para/>
/// Returns `<see langword="true"/>` if the user completed the dialog; otherwise `<see langword="false"/>` if the user canceled or abandoned the dialog.
/// </summary>
/// <param name="viewModel">The view model passed to the presented window.</param>
/// <param name="windowCreator">Creates the window `<paramref name="viewModel"/>` is passed to.</param>
bool ShowViewModel(DialogViewModel viewModel, Func<AuthenticationDialogWindow> windowCreator);
}
internal class Gui : Microsoft.Alm.Authentication.Base, IGui
{
public Gui(RuntimeContext context)
: base(context)
{ }
public Type ServiceType
=> typeof(IGui);
public bool ShowViewModel(DialogViewModel viewModel, Func<AuthenticationDialogWindow> windowCreator)
{
StartSTATask(() =>
{
EnsureApplicationResources();
var window = windowCreator();
window.DataContext = viewModel;
window.ShowDialog();
})
.Wait();
return viewModel.Result == AuthenticationDialogResult.Ok
&& viewModel.IsValid;
}
private static void EnsureApplicationResources()
{
if (!UriParser.IsKnownScheme("pack"))
{
UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), "pack", -1);
}
var appResourcesUri = new Uri("pack://application:,,,/Bitbucket.Authentication;component/AppResources.xaml", UriKind.RelativeOrAbsolute);
// If we launch two dialogs in the same process (Credential followed by 2fa), calling new
// App() throws an exception stating the Application class can't be created twice.
// Creating an App instance happens to set Application.Current to that instance (it's
// weird). However, if you don't set the ShutdownMode to OnExplicitShutdown, the second
// time you launch a dialog, Application.Current is null even in the same process.
if (Application.Current == null)
{
var app = new Application();
Debug.Assert(Application.Current == app, "Current application not set");
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = appResourcesUri });
}
else
{
// Application.Current exists, but what if in the future, some other code created the
// singleton. Let's make sure our resources are still loaded.
var resourcesExist = Application.Current.Resources.MergedDictionaries.Any(r => r.Source == appResourcesUri);
if (!resourcesExist)
{
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = appResourcesUri });
}
}
}
private static Task StartSTATask(Action action)
{
var completionSource = new TaskCompletionSource<object>();
var thread = new Thread(() =>
{
try
{
action();
completionSource.SetResult(null);
}
catch (Exception e)
{
completionSource.SetException(e);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return completionSource.Task;
}
}
}