Skip to content

Commit

Permalink
bb-winui: update WPF based Bitbucket prompt
Browse files Browse the repository at this point in the history
Update the WPF-based Bitbucket authentication prompt in a similar
fashion to the Avalonia ones.
  • Loading branch information
mjcheetham committed Jun 22, 2022
1 parent 6ceab40 commit 5ef61a3
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 138 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,30 @@
mc:Ignorable="d"
Title="Bitbucket Authentication Dialog Tester"
Height="240" Width="420" ResizeMode="NoResize">
<DockPanel>
<Button Content="Show Credentials Dialog" Padding="10" Click="ShowCredentials" />
<Button Content="Show OAuth Dialog" Padding="10" Click="ShowOAuth" />
</DockPanel>
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Auth Modes" />
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Horizontal" VerticalAlignment="Center">
<CheckBox Content="Browser" x:Name="showOAuth" MinWidth="90" IsChecked="True" />
<CheckBox Content="Basic" x:Name="showBasic" MinWidth="80" IsChecked="True" />
</StackPanel>
<Label Grid.Row="1" Grid.Column="0" Content="URL" />
<TextBox Grid.Row="1" Grid.Column="1" x:Name="url" />
<Label Grid.Row="2" Grid.Column="0" Content="Username" />
<TextBox Grid.Row="2" Grid.Column="1" x:Name="username" />
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10">
<Button Content="Show" Click="ShowCredentials" Padding="8,4"/>
</StackPanel>
</StackPanel>
</Window>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Windows;
using Atlassian.Bitbucket.UI.ViewModels;
using Atlassian.Bitbucket.UI.Views;
Expand All @@ -19,17 +20,17 @@ private void ShowCredentials(object sender, RoutedEventArgs e)
{
var vm = new CredentialsViewModel(_environment)
{
ShowOAuth = true
ShowOAuth = showOAuth.IsChecked ?? false,
ShowBasic = showBasic.IsChecked ?? false,
UserName = username.Text
};
var view = new CredentialsView();
var window = new DialogWindow(view) { DataContext = vm };
window.ShowDialog();
}

private void ShowOAuth(object sender, RoutedEventArgs e)
{
var vm = new OAuthViewModel(_environment);
var view = new OAuthView();
if (Uri.TryCreate(url.Text, UriKind.Absolute, out Uri uri))
{
vm.Url = uri;
}

var view = new CredentialsView();
var window = new DialogWindow(view) { DataContext = vm };
window.ShowDialog();
}
Expand Down
1 change: 0 additions & 1 deletion src/windows/Atlassian.Bitbucket.UI.Windows/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public static async Task Main(string[] args)
}

app.RegisterCommand(new CredentialsCommandImpl(context));
app.RegisterCommand(new OAuthCommandImpl(context));

int exitCode = app.RunAsync(args)
.ConfigureAwait(false)
Expand Down
108 changes: 79 additions & 29 deletions src/windows/Atlassian.Bitbucket.UI.Windows/Views/CredentialsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<ResourceDictionary Source="../Assets/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sharedConverters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<sharedConverters:NonNullToVisibleConverter x:Key="NonNullToVisibleConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<DockPanel LastChildFill="True">
Expand All @@ -34,38 +35,87 @@
</StackPanel>

<StackPanel DockPanel.Dock="Top" Margin="0,0,0,30">
<Image HorizontalAlignment="Center" Source="/Assets/atlassian-logo.png" />
<Image HorizontalAlignment="Center" Source="/Assets/atlassian-logo.png" Height="90" />
<TextBlock HorizontalAlignment="Center"
FontSize="14"
FontSize="14" Margin="0,-10,0,0"
Text="Log in to your account"/>
<TextBlock HorizontalAlignment="Center"
Text="{Binding Url}" FontSize="14"
Visibility="{Binding Url, Converter={StaticResource NonNullToVisibleConverter}}"
Margin="0,10,0,0"/>
</StackPanel>

<StackPanel Width="288">
<sharedControls:PromptTextBox x:Name="userNameTextBox"
Margin="0,0,0,10"
HorizontalAlignment="Stretch"
PromptText="Email or username"
Text="{Binding UserName}" />
<sharedControls:PasswordPromptTextBox x:Name="passwordTextBox"
Margin="0,0,0,20"
HorizontalAlignment="Stretch"
PromptText="Password"
Password="{Binding Password, UpdateSourceTrigger=PropertyChanged, Delay=300, Mode=OneWayToSource}" />
<Button IsDefault="True"
Margin="0,0,0,10"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding LoginCommand}"
Content="Continue"
Width="140" Height="40"
Style="{StaticResource AccentButton}"/>
<TextBlock FontSize="14" TextAlignment="Center"
Visibility="{Binding ShowOAuth, Converter={StaticResource BooleanToVisibilityConverter}}">
<Hyperlink Command="{Binding OAuthCommand}">
Sign in with OAuth
</Hyperlink>
</TextBlock>
</StackPanel>
<TabControl x:Name="tabControl"
BorderThickness="0"
Background="Transparent">
<TabControl.Resources>
<Style TargetType="TabPanel">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" Margin="10,0,10,10"
BorderThickness="0,0,0,2"
BorderBrush="Transparent">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="0,0,0,5"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>

<TabItem IsEnabled="{Binding ShowOAuth}"
Visibility="{Binding ShowOAuth, Converter={StaticResource BooleanToVisibilityConverter}}">
<TabItem.Header>
<TextBlock Text="Browser" FontSize="12" />
</TabItem.Header>
<StackPanel x:Name="oauthPanel"
Margin="0,10">
<Button x:Name="oauthButton"
Content="Sign in with your browser"
IsDefault="True"
Command="{Binding OAuthCommand}"
HorizontalAlignment="Center"
Margin="0,0,0,10"
Style="{StaticResource AccentButton}"/>
</StackPanel>
</TabItem>

<TabItem IsEnabled="{Binding ShowBasic}"
Visibility="{Binding ShowBasic, Converter={StaticResource BooleanToVisibilityConverter}}">
<TabItem.Header>
<TextBlock Text="Password" FontSize="12" />
</TabItem.Header>
<StackPanel x:Name="basicPanel"
Margin="0,10">
<sharedControls:PromptTextBox x:Name="userNameTextBox"
Margin="0,0,0,10"
PromptText="Email or username"
Text="{Binding UserName}"/>
<sharedControls:PasswordPromptTextBox x:Name="passwordTextBox"
Margin="0,0,0,10"
PromptText="Password or token"
Password="{Binding Password, UpdateSourceTrigger=PropertyChanged, Delay=300, Mode=OneWayToSource}"/>
<Button Content="Sign in"
IsDefault="True"
Command="{Binding LoginCommand}"
HorizontalAlignment="Center"
Width="140" Height="40"
Style="{StaticResource AccentButton}"/>
</StackPanel>
</TabItem>
</TabControl>
</DockPanel>
</UserControl>
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Atlassian.Bitbucket.UI.ViewModels;
using GitCredentialManager.UI.Controls;

Expand All @@ -11,20 +13,60 @@ public CredentialsView()
InitializeComponent();
}

// Set focus on a UIElement the next time it becomes visible
private static void OnIsVisibleChangedOneTime(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is UIElement element)
{
// Unsubscribe to prevent re-triggering
element.IsVisibleChanged -= OnIsVisibleChangedOneTime;

// Set logical focus
element.Focus();

// Set keyboard focus
Keyboard.Focus(element);
}
}

public void SetFocus()
{
if (!(DataContext is CredentialsViewModel vm))
{
return;
}

if (string.IsNullOrWhiteSpace(vm.UserName))
//
// Select the best available authentication mechanism that is visible
// and make the textbox/button focused when it next made visible.
//
// In WPF the controls in a TabItem are not part of the visual tree until
// the TabControl has been switched to that tab, so we must delay focusing
// on the textbox/button until it becomes visible.
//
// This means as the user first moves through the tabs, the "correct" control
// will be given focus in that tab.
//
void SetFocusOnNextVisible(UIElement element)
{
element.IsVisibleChanged += OnIsVisibleChangedOneTime;
}

// Set up focus events on all controls
SetFocusOnNextVisible(
string.IsNullOrWhiteSpace(vm.UserName)
? userNameTextBox
: passwordTextBox);
SetFocusOnNextVisible(oauthButton);

// Switch to the preferred tab
if (vm.ShowOAuth)
{
userNameTextBox.Focus();
tabControl.SelectedIndex = 0;
}
else
else if (vm.ShowBasic)
{
passwordTextBox.Focus();
tabControl.SelectedIndex = 1;
}
}
}
Expand Down
54 changes: 0 additions & 54 deletions src/windows/Atlassian.Bitbucket.UI.Windows/Views/OAuthView.xaml

This file was deleted.

18 changes: 0 additions & 18 deletions src/windows/Atlassian.Bitbucket.UI.Windows/Views/OAuthView.xaml.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,18 @@ public virtual object ConvertBack(object value, Type targetType, object paramete
return Binding.DoNothing;
}
}

[ValueConversion(typeof(string), typeof(Visibility))]
public class NonNullToVisibleConverter : IValueConverter
{
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ConverterHelper.GetConditionalVisibility(value != null, parameter);
}

public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

0 comments on commit 5ef61a3

Please sign in to comment.