Skip to content

Commit

Permalink
bitbucket: add Avalonia-based UI helper for BB
Browse files Browse the repository at this point in the history
Add a new UI helper for Bitbucket using Avalonia.
  • Loading branch information
mjcheetham committed May 18, 2021
1 parent 1047184 commit bfe20a9
Show file tree
Hide file tree
Showing 14 changed files with 604 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Git-Credential-Manager.sln
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "linux", "linux", "{8F9D7E67
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.UI", "src\shared\GitHub.UI\GitHub.UI.csproj", "{B5F00B46-FE93-45F2-B283-52B74B3E13B9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atlassian.Bitbucket.UI", "src\shared\Atlassian.Bitbucket.UI\Atlassian.Bitbucket.UI.csproj", "{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Git.CredentialManager.UI", "src\shared\Microsoft.Git.CredentialManager.UI\Microsoft.Git.CredentialManager.UI.csproj", "{001846B0-462B-4A27-90CD-2435D4C0F680}"
EndProject
Global
Expand Down Expand Up @@ -335,6 +337,22 @@ Global
{B5F00B46-FE93-45F2-B283-52B74B3E13B9}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
{B5F00B46-FE93-45F2-B283-52B74B3E13B9}.LinuxRelease|Any CPU.ActiveCfg = Release|Any CPU

This comment has been minimized.

Copy link
@tonyhillier

tonyhillier May 28, 2021

{B5F00B46-FE93-45F2-B283-52B74B3E13B9}.LinuxRelease|Any CPU.Build.0 = Release|Any CPU

{B5F00B46-FE93-45F2-B283-52B74B3E13B9}.LinuxRelease|Any CPU.Build.0 = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.MacDebug|Any CPU.Build.0 = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.MacRelease|Any CPU.Build.0 = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.Release|Any CPU.Build.0 = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.LinuxRelease|Any CPU.ActiveCfg = Release|Any CPU
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA}.LinuxRelease|Any CPU.Build.0 = Release|Any CPU
{001846B0-462B-4A27-90CD-2435D4C0F680}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{001846B0-462B-4A27-90CD-2435D4C0F680}.Debug|Any CPU.Build.0 = Debug|Any CPU
{001846B0-462B-4A27-90CD-2435D4C0F680}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -378,6 +396,7 @@ Global
{8F9D7E67-7DD7-4E32-9134-423281AF00E9} = {A7FC1234-95E3-4496-B5F7-4306F41E6A0E}
{AD2A935F-3720-4802-8119-6A9B35B254DF} = {8F9D7E67-7DD7-4E32-9134-423281AF00E9}
{B5F00B46-FE93-45F2-B283-52B74B3E13B9} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
{EB1AA840-6FFF-4464-A9B2-0AEA36F615EA} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
{001846B0-462B-4A27-90CD-2435D4C0F680} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions src/shared/Atlassian.Bitbucket.UI/Atlassian.Bitbucket.UI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RuntimeIdentifiers>osx-x64;linux-x64</RuntimeIdentifiers>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Atlassian.Bitbucket\Atlassian.Bitbucket.csproj" />
<ProjectReference Include="..\Microsoft.Git.CredentialManager.UI\Microsoft.Git.CredentialManager.UI.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="Controls\TesterWindow.axaml.cs">
<DependentUpon>TesterWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>

</Project>
49 changes: 49 additions & 0 deletions src/shared/Atlassian.Bitbucket.UI/Commands/CredentialsCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Threading;
using System.Threading.Tasks;
using Atlassian.Bitbucket.UI.ViewModels;
using Atlassian.Bitbucket.UI.Views;
using Microsoft.Git.CredentialManager;
using Microsoft.Git.CredentialManager.UI;

namespace Atlassian.Bitbucket.UI.Commands
{
internal class CredentialsCommand : HelperCommand
{
public CredentialsCommand(CommandContext context)
: base(context, "userpass", "Show authentication prompt.")
{
AddOption(
new Option<string>("--username", "Username or email.")
);

Handler = CommandHandler.Create<string>(ExecuteAsync);
}

private async Task<int> ExecuteAsync(string userName)
{
var viewModel = new CredentialsViewModel(Context.Environment)
{
UserName = userName
};

await AvaloniaUi.ShowViewAsync<CredentialsView>(viewModel, GetParentHandle(), CancellationToken.None);

if (!viewModel.WindowResult)
{
throw new Exception("User cancelled dialog.");
}

WriteResult(new Dictionary<string, string>
{
["username"] = viewModel.UserName,
["password"] = viewModel.Password,
});

return 0;
}
}
}
40 changes: 40 additions & 0 deletions src/shared/Atlassian.Bitbucket.UI/Commands/OAuthCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Threading;
using System.Threading.Tasks;
using Atlassian.Bitbucket.UI.ViewModels;
using Atlassian.Bitbucket.UI.Views;
using Microsoft.Git.CredentialManager;
using Microsoft.Git.CredentialManager.UI;

namespace Atlassian.Bitbucket.UI.Commands
{
internal class OAuthCommand : HelperCommand
{
public OAuthCommand(CommandContext context)
: base(context, "oauth", "Show OAuth required prompt.")
{
Handler = CommandHandler.Create(ExecuteAsync);
}

private async Task<int> ExecuteAsync()
{
var viewModel = new OAuthViewModel(Context.Environment);
await AvaloniaUi.ShowViewAsync<OAuthView>(viewModel, GetParentHandle(), CancellationToken.None);

if (!viewModel.WindowResult)
{
throw new Exception("User cancelled dialog.");
}

WriteResult(new Dictionary<string, string>
{
["continue"] = "true"
});

return 0;
}
}
}
13 changes: 13 additions & 0 deletions src/shared/Atlassian.Bitbucket.UI/Controls/TesterWindow.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<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="800" d:DesignHeight="450"
x:Class="Atlassian.Bitbucket.UI.Controls.TesterWindow"
Title="Bitbucket Authentication Dialog Tester"
Height="240" Width="420" CanResize="False">
<DockPanel>
<Button Content="Show Credentials Dialog" Padding="10" Click="ShowCredentials" />
<Button Content="Show OAuth Dialog" Padding="10" Click="ShowOAuth" />
</DockPanel>
</Window>
68 changes: 68 additions & 0 deletions src/shared/Atlassian.Bitbucket.UI/Controls/TesterWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Atlassian.Bitbucket.UI.ViewModels;
using Atlassian.Bitbucket.UI.Views;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Microsoft.Git.CredentialManager;
using Microsoft.Git.CredentialManager.Interop.Linux;
using Microsoft.Git.CredentialManager.Interop.MacOS;
using Microsoft.Git.CredentialManager.Interop.Posix;
using Microsoft.Git.CredentialManager.Interop.Windows;
using Microsoft.Git.CredentialManager.UI.Controls;

namespace Atlassian.Bitbucket.UI.Controls
{
public class TesterWindow : Window
{
private readonly IEnvironment _environment;

public TesterWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif

if (PlatformUtils.IsWindows())
{
_environment = new WindowsEnvironment(new WindowsFileSystem());
}
else
{
IFileSystem fs;
if (PlatformUtils.IsMacOS())
{
fs = new MacOSFileSystem();
}
else
{
fs = new LinuxFileSystem();
}

_environment = new PosixEnvironment(fs);
}
}

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

private void ShowCredentials(object sender, RoutedEventArgs e)
{
var vm = new CredentialsViewModel(_environment);
var view = new CredentialsView();
var window = new DialogWindow(view) {DataContext = vm};
window.ShowDialog(this);
}

private void ShowOAuth(object sender, RoutedEventArgs e)
{
var vm = new OAuthViewModel(_environment);
var view = new OAuthView();
var window = new DialogWindow(view) {DataContext = vm};
window.ShowDialog(this);
}
}
}
70 changes: 70 additions & 0 deletions src/shared/Atlassian.Bitbucket.UI/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.CommandLine;
using System.Threading;
using Atlassian.Bitbucket.UI.Commands;
using Atlassian.Bitbucket.UI.Controls;
using Avalonia;
using Microsoft.Git.CredentialManager;
using Microsoft.Git.CredentialManager.UI;

namespace Atlassian.Bitbucket.UI
{
public static class Program
{
public static void Main(string[] args)
{
// If we have no arguments then just start the app with the test window.
if (args.Length == 0)
{
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
return;
}

// Create the dispatcher on the main thread. This is required
// for some platform UI services such as macOS that mandates
// all controls are created/accessed on the initial thread
// created by the process (the process entry thread).
Dispatcher.Initialize();

// Run AppMain in a new thread and keep the main thread free
// to process the dispatcher's job queue.
var appMain = new Thread(AppMain) {Name = nameof(AppMain)};
appMain.Start(args);

// Process the dispatcher job queue (aka: message pump, run-loop, etc...)
// We must ensure to run this on the same thread that it was created on
// (the main thread) so we cannot use any async/await calls between
// Dispatcher.Create and Run.
Dispatcher.MainThread.Run();

// Execution should never reach here as AppMain terminates the process on completion.
throw new InvalidOperationException("Main dispatcher job queue shutdown unexpectedly");
}

private static void AppMain(object o)
{
string[] args = (string[]) o;

string appPath = ApplicationBase.GetEntryApplicationPath();
using (var context = new CommandContext(appPath))
using (var app = new HelperApplication(context))
{
app.RegisterCommand(new CredentialsCommand(context));
app.RegisterCommand(new OAuthCommand(context));

// Run!
int exitCode = app.RunAsync(args)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();

Environment.Exit(exitCode);
}
}

public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure(() => new AvaloniaApp(() => new TesterWindow()))
.UsePlatformDetect()
.LogToTrace();
}
}
Loading

0 comments on commit bfe20a9

Please sign in to comment.