Skip to content

Commit 56c42ae

Browse files
committed
Add a widget on the titlebar to login and logout
1 parent b505369 commit 56c42ae

4 files changed

Lines changed: 278 additions & 26 deletions

File tree

src/UniGetUI/MainWindow.xaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
xmlns:local="using:UniGetUI"
99
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
1010
xmlns:widgets="using:UniGetUI.Interface.Widgets"
11-
xmlns:winex="using:WinUIEx"
11+
xmlns:winex="using:WinUIEx" xmlns:services="using:UniGetUI.Services"
1212
Title="UniGetUI"
1313
mc:Ignorable="d">
1414

@@ -32,18 +32,21 @@
3232
x:Name="TitleBar"
3333
Title="UniGetUI"
3434
Grid.Row="0"
35-
Margin="0,4"
35+
Margin="0,0,0,-4"
36+
BackRequested="TitleBar_OnBackRequested"
37+
IsBackButtonVisible="False"
3638
IsPaneToggleButtonVisible="True"
3739
PaneToggleRequested="TitleBar_PaneToggleRequested"
38-
IsBackButtonVisible="False"
39-
BackRequested="TitleBar_OnBackRequested"
4040
Visibility="Collapsed">
4141
<winex:TitleBar.IconSource>
4242
<ImageIconSource ImageSource="ms-appx:///Assets/Images/icon.png" />
4343
</winex:TitleBar.IconSource>
4444
<!--winex:TitleBar.Content>
4545
<UserControl Height="10" />
4646
</winex:TitleBar.Content-->
47+
<winex:TitleBar.Footer>
48+
<services:UserAvatar x:Name="UserAvatar" />
49+
</winex:TitleBar.Footer>
4750
</winex:TitleBar>
4851
<StackPanel
4952
Grid.Row="1"

src/UniGetUI/Services/GitHubAuthService.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using System;
2+
using System.Net;
3+
using System.Text;
24
using System.Threading.Tasks;
5+
using Octokit;
6+
using UniGetUI.Core.Data;
37
using UniGetUI.Core.Logging;
48
using UniGetUI.Core.SecureSettings;
59
using UniGetUI.Core.SettingsEngine;
610
using Windows.System;
7-
using Octokit;
8-
using System.Net;
9-
using System.Text;
11+
using YamlDotNet.Core.Tokens;
1012

1113
namespace UniGetUI.Services
1214
{
@@ -24,6 +26,22 @@ public GitHubAuthService()
2426
_client = new GitHubClient(new ProductHeaderValue("UniGetUI"));
2527
}
2628

29+
public async Task<GitHubClient?> CreateGitHubClientAsync()
30+
{
31+
var token = await GetAccessTokenAsync();
32+
33+
if (string.IsNullOrEmpty(token))
34+
{
35+
Logger.Error("GitHub access token is not available. Cannot perform Gist operation.");
36+
return null;
37+
}
38+
39+
return new GitHubClient(new ProductHeaderValue("UniGetUI", CoreData.VersionName))
40+
{
41+
Credentials = new Credentials(token)
42+
};
43+
}
44+
2745
public async Task<bool> SignInAsync()
2846
{
2947
HttpListener httpListener = null;

src/UniGetUI/Services/GitHubBackupService.cs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,14 @@ public GitHubBackupService(GitHubAuthService authService)
3535
GistFileKey = $"{PackageBackup_StartingKey} {DeviceUserUniqueIdentifier}";
3636
}
3737

38-
private async Task<GitHubClient?> CreateClientAsync()
39-
{
40-
var token = await _authService.GetAccessTokenAsync();
41-
42-
if (string.IsNullOrEmpty(token))
43-
{
44-
Logger.Error("GitHub access token is not available. Cannot perform Gist operation.");
45-
return null;
46-
}
47-
48-
return new GitHubClient(new ProductHeaderValue("UniGetUI", CoreData.VersionName))
49-
{
50-
Credentials = new Credentials(token)
51-
};
52-
}
53-
5438
/// <summary>
5539
/// Assuming authentication is set up, upload the given bundleContents to GitHub
5640
/// </summary>
5741
/// <param name="bundleContents"></param>
5842
/// <returns>A boolean representing the success of the operation</returns>
5943
public async Task UploadPackageBundle(string bundleContents)
6044
{
61-
var GHClient = await CreateClientAsync();
45+
var GHClient = await _authService.CreateGitHubClientAsync();
6246
if (GHClient is null)
6347
throw new Exception("The GitHub user is not authenticated");
6448

@@ -122,7 +106,7 @@ private static Task<Gist> _createBackupGistAsync(GitHubClient client)
122106
/// <returns></returns>
123107
public async Task<IEnumerable<string>> GetAvailableBackups()
124108
{
125-
var GHClient = await CreateClientAsync();
109+
var GHClient = await _authService.CreateGitHubClientAsync();
126110
if (GHClient is null)
127111
throw new Exception("The GitHub user is not authenticated");
128112

@@ -145,7 +129,7 @@ public async Task<IEnumerable<string>> GetAvailableBackups()
145129
/// <exception cref="Exception"></exception>
146130
public async Task<string?> GetBackupContents(string backupName)
147131
{
148-
var GHClient = await CreateClientAsync();
132+
var GHClient = await _authService.CreateGitHubClientAsync();
149133
if (GHClient is null)
150134
throw new Exception("The GitHub user is not authenticated");
151135

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Security.Authentication;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Microsoft.UI;
8+
using Microsoft.UI.Xaml;
9+
using Microsoft.UI.Xaml.Controls;
10+
using Microsoft.UI.Xaml.Controls.Primitives;
11+
using Microsoft.UI.Xaml.Media;
12+
using Microsoft.UI.Xaml.Media.Imaging;
13+
using UniGetUI.Core.Logging;
14+
using UniGetUI.Core.Tools;
15+
using UniGetUI.Pages.DialogPages;
16+
using UniGetUI.Pages.SettingsPages.GeneralPages;
17+
18+
namespace UniGetUI.Services
19+
{
20+
public partial class UserAvatar: UserControl
21+
{
22+
public UserAvatar()
23+
{
24+
VerticalContentAlignment = VerticalAlignment.Center;
25+
HorizontalContentAlignment = HorizontalAlignment.Center;
26+
_ = RefreshStatus();
27+
}
28+
29+
public async Task RefreshStatus()
30+
{
31+
SetLoading();
32+
var client = new GitHubAuthService();
33+
await Task.Delay(1000);
34+
if (await client.IsAuthenticatedAsync())
35+
{
36+
Content = await GenerateLogoutControl();
37+
}
38+
else
39+
{
40+
Content = GenerateLoginControl();
41+
}
42+
}
43+
44+
private async void LoginButton_Click(object sender, RoutedEventArgs e)
45+
{
46+
SetLoading();
47+
try
48+
{
49+
var client = new GitHubAuthService();
50+
if (await client.IsAuthenticatedAsync())
51+
{
52+
Logger.Warn("Login invoked when the client was already logged in!");
53+
await RefreshStatus();
54+
return;
55+
}
56+
57+
await client.SignInAsync();
58+
await RefreshStatus();
59+
}
60+
catch (Exception ex)
61+
{
62+
DialogHelper.ShowDismissableBalloon(
63+
CoreTools.Translate("Error"),
64+
CoreTools.Translate("Log in failed: ") + ex.Message
65+
);
66+
await RefreshStatus();
67+
}
68+
}
69+
70+
private async void LogoutButton_Click(object sender, RoutedEventArgs e)
71+
{
72+
SetLoading();
73+
try
74+
{
75+
var client = new GitHubAuthService();
76+
if (await client.IsAuthenticatedAsync())
77+
{
78+
await client.SignOutAsync();
79+
}
80+
81+
await RefreshStatus();
82+
}
83+
catch (Exception ex)
84+
{
85+
DialogHelper.ShowDismissableBalloon(
86+
CoreTools.Translate("Error"),
87+
CoreTools.Translate("Log out failed: ") + ex.Message
88+
);
89+
await RefreshStatus();
90+
}
91+
}
92+
93+
private void SetLoading()
94+
{
95+
this.Content = new ProgressRing() { IsIndeterminate = true, Width = 24, Height = 24 };
96+
}
97+
98+
private Button GenerateLoginControl()
99+
{
100+
var personPicture = new PersonPicture
101+
{
102+
Width = 36,
103+
Height = 36,
104+
Initials = "?"
105+
};
106+
107+
var translatedTextBlock = new TextBlock
108+
{
109+
Margin = new Thickness(4),
110+
TextWrapping = TextWrapping.WrapWholeWords,
111+
Text = CoreTools.Translate("Log in with GitHub to enable cloud package backup.")
112+
};
113+
114+
var hyperlinkButton = new HyperlinkButton
115+
{
116+
Padding = new Thickness(0),
117+
HorizontalAlignment = HorizontalAlignment.Stretch,
118+
Content = CoreTools.Translate("More details"),
119+
NavigateUri = new Uri("https://www.marticliment.com/unigetui/help/cloud-backup-overview/"),
120+
FontSize = 12
121+
};
122+
123+
var loginButton = new Button
124+
{
125+
HorizontalAlignment = HorizontalAlignment.Stretch,
126+
Content = CoreTools.Translate("Log in")
127+
};
128+
loginButton.Click += LoginButton_Click;
129+
130+
var stackPanel = new StackPanel
131+
{
132+
MaxWidth = 200,
133+
Margin = new Thickness(-8),
134+
Orientation = Orientation.Vertical,
135+
Spacing = 8
136+
};
137+
stackPanel.Children.Add(translatedTextBlock);
138+
stackPanel.Children.Add(hyperlinkButton);
139+
stackPanel.Children.Add(loginButton);
140+
141+
var flyout = new Flyout
142+
{
143+
LightDismissOverlayMode = LightDismissOverlayMode.Off,
144+
Placement = FlyoutPlacementMode.Bottom,
145+
Content = stackPanel
146+
};
147+
148+
return new Button
149+
{
150+
Margin = new Thickness(0),
151+
Padding = new Thickness(4),
152+
Background = new SolidColorBrush(Colors.Transparent),
153+
BorderThickness = new Thickness(0),
154+
CornerRadius = new CornerRadius(100),
155+
Content = personPicture,
156+
Flyout = flyout
157+
};
158+
}
159+
160+
private async Task<Button> GenerateLogoutControl()
161+
{
162+
var authClient = new GitHubAuthService();
163+
var token = await authClient.GetAccessTokenAsync();
164+
var GHClient = await authClient.CreateGitHubClientAsync();
165+
if(GHClient is null)
166+
{
167+
Logger.Error("Client did not report valid authentication");
168+
return GenerateLoginControl();
169+
}
170+
171+
var user = await GHClient.User.Current();
172+
173+
var personPicture = new PersonPicture
174+
{
175+
Width = 36,
176+
Height = 36,
177+
ProfilePicture = new BitmapImage(new Uri(user.AvatarUrl))
178+
};
179+
180+
var text1 = new TextBlock
181+
{
182+
Margin = new Thickness(4),
183+
TextWrapping = TextWrapping.WrapWholeWords,
184+
Text = CoreTools.Translate("You are logged in as {0} (@{1})", user.Name, user.Login)
185+
};
186+
187+
var text2 = new TextBlock
188+
{
189+
Margin = new Thickness(4),
190+
TextWrapping = TextWrapping.WrapWholeWords,
191+
FontSize = 12,
192+
FontWeight = new(500),
193+
Text = CoreTools.Translate("If you have cloud backup enabled, it will be saved as a GitHub Gist on this account")
194+
};
195+
196+
var hyperlinkButton = new HyperlinkButton
197+
{
198+
Padding = new Thickness(0),
199+
HorizontalAlignment = HorizontalAlignment.Stretch,
200+
Content = "Backup settings",
201+
FontSize = 12
202+
};
203+
hyperlinkButton.Click += (_, _) => MainApp.Instance.MainWindow.NavigationPage.OpenSettingsPage(typeof(Backup));
204+
205+
var loginButton = new Button
206+
{
207+
HorizontalAlignment = HorizontalAlignment.Stretch,
208+
Content = "Log out",
209+
Background = new SolidColorBrush(ActualTheme is ElementTheme.Dark? Colors.DarkRed: Colors.Red),
210+
BorderThickness = new(0)
211+
};
212+
loginButton.Click += LogoutButton_Click;
213+
214+
var stackPanel = new StackPanel
215+
{
216+
MaxWidth = 200,
217+
Margin = new Thickness(-8),
218+
Orientation = Orientation.Vertical,
219+
Spacing = 8
220+
};
221+
stackPanel.Children.Add(text1);
222+
stackPanel.Children.Add(text2);
223+
stackPanel.Children.Add(hyperlinkButton);
224+
stackPanel.Children.Add(loginButton);
225+
226+
var flyout = new Flyout
227+
{
228+
LightDismissOverlayMode = LightDismissOverlayMode.Off,
229+
Placement = FlyoutPlacementMode.Bottom,
230+
Content = stackPanel
231+
};
232+
233+
return new Button
234+
{
235+
Margin = new Thickness(0),
236+
Padding = new Thickness(4),
237+
Background = new SolidColorBrush(Colors.Transparent),
238+
BorderThickness = new Thickness(0),
239+
CornerRadius = new CornerRadius(100),
240+
Content = personPicture,
241+
Flyout = flyout
242+
};
243+
}
244+
245+
246+
}
247+
}

0 commit comments

Comments
 (0)