mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Workshop - Added login UI
This commit is contained in:
1 parent
7eccdf079a
commit
990f55b7c5
5 files changed
+274
-113
No files matched your search
@@ -5,9 +5,11 @@ using System.Reactive.Disposables;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Services;
|
||||
using Avalonia.Media.Imaging;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Flurl.Http;
|
||||
using ReactiveUI;
|
||||
using Serilog;
|
||||
@@ -16,21 +18,24 @@ namespace Artemis.UI.Screens.Workshop.CurrentUser;
|
||||
|
||||
public class CurrentUserViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private bool _loading = true;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IWindowService _windowService;
|
||||
private Bitmap? _avatar;
|
||||
private string? _email;
|
||||
private bool _loading = true;
|
||||
private string? _name;
|
||||
private string? _userId;
|
||||
|
||||
public CurrentUserViewModel(ILogger logger, IAuthenticationService authenticationService)
|
||||
public CurrentUserViewModel(ILogger logger, IAuthenticationService authenticationService, IWindowService windowService)
|
||||
{
|
||||
_logger = logger;
|
||||
_authenticationService = authenticationService;
|
||||
_windowService = windowService;
|
||||
Login = ReactiveCommand.CreateFromTask(ExecuteLogin);
|
||||
|
||||
this.WhenActivated(d => ReactiveCommand.CreateFromTask(ExecuteAutoLogin).Execute().Subscribe().DisposeWith(d));
|
||||
this.WhenActivated(d => _authenticationService.IsLoggedIn.Subscribe(_ => LoadCurrentUser().DisposeWith(d)));
|
||||
}
|
||||
|
||||
public bool Loading
|
||||
@@ -72,8 +77,13 @@ public class CurrentUserViewModel : ActivatableViewModelBase
|
||||
|
||||
private async Task ExecuteLogin(CancellationToken cancellationToken)
|
||||
{
|
||||
await _authenticationService.Login();
|
||||
await LoadCurrentUser();
|
||||
ContentDialogResult result = await _windowService.CreateContentDialog()
|
||||
.WithViewModel(out WorkshopLoginViewModel _)
|
||||
.WithTitle("Workshop login")
|
||||
.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary)
|
||||
await LoadCurrentUser();
|
||||
}
|
||||
|
||||
private async Task ExecuteAutoLogin(CancellationToken cancellationToken)
|
||||
@@ -95,21 +105,26 @@ public class CurrentUserViewModel : ActivatableViewModelBase
|
||||
|
||||
private async Task LoadCurrentUser()
|
||||
{
|
||||
if (!_authenticationService.IsLoggedIn)
|
||||
return;
|
||||
|
||||
UserId = _authenticationService.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
|
||||
Name = _authenticationService.Claims.FirstOrDefault(c => c.Type == "name")?.Value;
|
||||
Email = _authenticationService.Claims.FirstOrDefault(c => c.Type == "email")?.Value;
|
||||
|
||||
if (UserId != null)
|
||||
{
|
||||
await LoadAvatar(UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Avatar?.Dispose();
|
||||
Avatar = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAvatar(string userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Avatar?.Dispose();
|
||||
Avatar = new Bitmap(await $"{WorkshopConstants.AUTHORITY_URL}/user/avatar/{userId}".GetStreamAsync());
|
||||
}
|
||||
catch (Exception)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<UserControl 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"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:currentUser="clr-namespace:Artemis.UI.Screens.Workshop.CurrentUser"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.CurrentUser.WorkshopLoginView"
|
||||
x:DataType="currentUser:WorkshopLoginViewModel">
|
||||
<StackPanel Width="400">
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
In order to login you must enter your Artemis credentials in a browser.
|
||||
</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0 15">
|
||||
If you do not have an account yet you can create one in the browser too. Alternatively you can log in with Google or Discord.
|
||||
</TextBlock>
|
||||
|
||||
<ProgressBar IsIndeterminate="True" IsVisible="{CompiledBinding Code, Converter={x:Static StringConverters.IsNullOrEmpty}}"></ProgressBar>
|
||||
<StackPanel IsVisible="{CompiledBinding Code, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<TextBlock TextWrapping="Wrap">When logging in please enter the the code below</TextBlock>
|
||||
<StackPanel Margin="0 10" Orientation="Horizontal" HorizontalAlignment="Center" Spacing="5">
|
||||
<TextBox Width="115" IsReadOnly="True" Text="{CompiledBinding Code}" />
|
||||
<Button Width="32" Height="32" ToolTip.Tip="Copy code to clipboard" Command="{CompiledBinding CopyToClipboard}">
|
||||
<avalonia:MaterialIcon Kind="ContentCopy"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Button Width="152" HorizontalAlignment="Center" Command="{CompiledBinding OpenBrowser}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<avalonia:MaterialIcon Kind="ExternalLink" />
|
||||
<TextBlock>Open browser</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,17 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.CurrentUser;
|
||||
|
||||
public partial class WorkshopLoginView : ReactiveUserControl<WorkshopLoginViewModel>
|
||||
{
|
||||
public WorkshopLoginView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.WebClient.Workshop.Services;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.CurrentUser;
|
||||
|
||||
public class WorkshopLoginViewModel : ContentDialogViewModelBase
|
||||
{
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly CancellationTokenSource _cts;
|
||||
private ObservableAsPropertyHelper<string?>? _deviceCode;
|
||||
private string? _uri;
|
||||
|
||||
public WorkshopLoginViewModel(IAuthenticationService authenticationService, INotificationService notificationService)
|
||||
{
|
||||
_authenticationService = authenticationService;
|
||||
_notificationService = notificationService;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
OpenBrowser = ReactiveCommand.Create(ExecuteOpenBrowser);
|
||||
CopyToClipboard = ReactiveCommand.CreateFromTask(ExecuteCopyToClipboard);
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(Login);
|
||||
|
||||
_deviceCode = _authenticationService.UserCode
|
||||
.Select(uc => uc != null ? string.Join("-", string.Join("-", uc.Chunk(3).Select(c => new string(c)))) : null)
|
||||
.ToProperty(this, vm => vm.Code)
|
||||
.DisposeWith(d);
|
||||
_authenticationService.VerificationUri.Subscribe(u => _uri = u).DisposeWith(d);
|
||||
Disposable.Create(_cts, cts => cts.Cancel()).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> OpenBrowser { get; }
|
||||
public ReactiveCommand<Unit, Unit> CopyToClipboard { get; }
|
||||
|
||||
public string? Code => _deviceCode?.Value;
|
||||
|
||||
private async Task Login()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool result = await _authenticationService.Login(_cts.Token);
|
||||
if (result)
|
||||
_notificationService.CreateNotification().WithTitle("Login succeeded").WithSeverity(NotificationSeverity.Success).Show();
|
||||
ContentDialog?.Hide(result ? ContentDialogResult.Primary : ContentDialogResult.Secondary);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is not TaskCanceledException)
|
||||
_notificationService.CreateNotification().WithTitle("Login failed").WithMessage(e.Message).WithSeverity(NotificationSeverity.Error).Show();
|
||||
ContentDialog?.Hide(ContentDialogResult.Secondary);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteOpenBrowser()
|
||||
{
|
||||
if (_uri == null)
|
||||
return;
|
||||
Utilities.OpenUrl(_uri);
|
||||
}
|
||||
|
||||
private async Task ExecuteCopyToClipboard()
|
||||
{
|
||||
if (Code == null)
|
||||
return;
|
||||
await Shared.UI.Clipboard.SetTextAsync(Code);
|
||||
_notificationService.CreateNotification().WithMessage("Code copied to clipboard.").Show();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user