- tweak: Improve auth page
This commit is contained in:
@@ -1,17 +1,28 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:pages="clr-namespace:Nebula.Launcher.ViewModels.Pages">
|
||||
|
||||
<!-- Base Window Style -->
|
||||
<Style Selector="Window">
|
||||
<Setter Property="Background" Value="{StaticResource DefaultBackground}" />
|
||||
</Style>
|
||||
|
||||
<!-- Common Border Style -->
|
||||
<Style Selector="Border">
|
||||
<Setter Property="CornerRadius" Value="10" />
|
||||
</Style>
|
||||
|
||||
<!-- Common Label Style -->
|
||||
<Style Selector="Label">
|
||||
<Setter Property="Foreground" Value="#f7f7ff" />
|
||||
</Style>
|
||||
|
||||
<!-- Common ItemsControl Style -->
|
||||
<Style Selector="ItemsControl">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<!-- General Button Style -->
|
||||
<Style Selector="Button">
|
||||
<Setter Property="BorderBrush" Value="#343334" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
@@ -20,6 +31,7 @@
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<!-- Button State Overrides -->
|
||||
<Style Selector="Button:pressed">
|
||||
<Setter Property="RenderTransform" Value="{x:Null}" />
|
||||
<Setter Property="BorderThickness" Value="0,0,0,2" />
|
||||
@@ -31,26 +43,33 @@
|
||||
<Setter Property="BorderThickness" Value="0,0,0,0" />
|
||||
</Style>
|
||||
|
||||
<!-- ViewSelectButton Specialization -->
|
||||
<Style Selector="Button.ViewSelectButton">
|
||||
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
||||
<Setter Property="Margin" Value="0,0,0,5" />
|
||||
<Setter Property="Padding" Value="8" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="Button.ViewSelectButton:pressed">
|
||||
<Setter Property="BorderThickness" Value="0,0,0,0" />
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Styles -->
|
||||
<Style Selector="TextBox">
|
||||
<Setter Property="Foreground" Value="#f7f7ff" />
|
||||
<Setter Property="SelectionForegroundBrush" Value="#f7f7ff" />
|
||||
<Setter Property="BorderThickness" Value="0,0,0,0" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="BorderBrush" Value="#f7f7ff" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style Selector="TextBox" />
|
||||
|
||||
<Style Selector="TextBox:focus /template/ Border#PART_BorderElement">
|
||||
<Setter Property="BorderBrush" Value="White" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
||||
</Style>
|
||||
|
||||
<!-- ListBoxItem Styles -->
|
||||
<Style Selector="ListBoxItem /template/ ContentPresenter">
|
||||
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
||||
<Setter Property="Margin" Value="0,0,0,5" />
|
||||
@@ -61,66 +80,37 @@
|
||||
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
||||
<Setter Property="Background">
|
||||
<LinearGradientBrush EndPoint="100%,50%" StartPoint="0%,50%">
|
||||
<GradientStop Color="#ae4c47" Offset="0.0" />
|
||||
<GradientStop Color="#D95F59" Offset="0.2" />
|
||||
<GradientStop Color="#D95F59" Offset="1.0" />
|
||||
</LinearGradientBrush>
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush EndPoint="100%,50%" StartPoint="0%,50%">
|
||||
<GradientStop Color="#ae4c47" Offset="0.0" />
|
||||
<GradientStop Color="#D95F59" Offset="0.2" />
|
||||
<GradientStop Color="#D95F59" Offset="1.0" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="BoxShadow" Value="0 0 15 1 #1212" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ListBoxItem:pointerover">
|
||||
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
||||
<Setter Property="Margin" Value="0,0,5,0" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="ListBoxItem:pressed /template/ ContentPresenter">
|
||||
<Setter Property="CornerRadius" Value="0,8,8,0" />
|
||||
<Setter Property="Background" Value="{StaticResource DefaultSelected}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBox">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style Selector="TextBox:focus /template/ Border#PART_BorderElement">
|
||||
<Setter Property="BorderBrush" Value="White" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="pages|ComplexUnitConfigControl.ConfigBorder">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="BorderThickness" Value="2,2,2,2"/>
|
||||
<Setter Property="BorderBrush" Value="Azure"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="pages|ArrayUnitConfigControl.ConfigBorder">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="BorderThickness" Value="2,2,2,2"/>
|
||||
<Setter Property="BorderBrush" Value="Azure"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="pages|StringUnitConfigControl.ConfigBorder">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="BorderThickness" Value="2,2,2,2"/>
|
||||
<Setter Property="BorderBrush" Value="Azure"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="pages|IntUnitConfigControl.ConfigBorder">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="BorderThickness" Value="2,2,2,2"/>
|
||||
<Setter Property="BorderBrush" Value="Azure"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="pages|FloatUnitConfigControl.ConfigBorder">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="BorderThickness" Value="2,2,2,2"/>
|
||||
<Setter Property="BorderBrush" Value="Azure"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Button.ConfigBorder /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="BorderThickness" Value="2,2,2,2"/>
|
||||
<Setter Property="BorderBrush" Value="Azure"/>
|
||||
<Setter Property="CornerRadius" Value="0"/>
|
||||
</Style>
|
||||
<!-- Combined ConfigBorder Styles -->
|
||||
<Style Selector="pages|ComplexUnitConfigControl.ConfigBorder,
|
||||
pages|ArrayUnitConfigControl.ConfigBorder,
|
||||
pages|StringUnitConfigControl.ConfigBorder,
|
||||
pages|IntUnitConfigControl.ConfigBorder,
|
||||
pages|FloatUnitConfigControl.ConfigBorder">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
<Setter Property="CornerRadius" Value="5"/>
|
||||
<Setter Property="BorderThickness" Value="1,0,1,2" />
|
||||
<Setter Property="BorderBrush" Value="Azure" />
|
||||
</Style>
|
||||
|
||||
</Styles>
|
||||
@@ -7,6 +7,8 @@ namespace Nebula.Launcher;
|
||||
|
||||
public static class LauncherConVar
|
||||
{
|
||||
public static readonly ConVar<bool> DoMigration =
|
||||
ConVarBuilder.Build("migration.doMigrate", true);
|
||||
public static readonly ConVar<ProfileAuthCredentials[]> AuthProfiles =
|
||||
ConVarBuilder.Build<ProfileAuthCredentials[]>("auth.profiles.v2", []);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public partial class MainViewModel : ViewModelBase
|
||||
[GenerateProperty] private PopupMessageService PopupMessageService { get; } = default!;
|
||||
[GenerateProperty] private ContentService ContentService { get; } = default!;
|
||||
[GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!;
|
||||
[GenerateProperty] private FileService FileService { get; } = default!;
|
||||
[GenerateProperty] private ConfigurationService ConfigurationService { get; } = default!;
|
||||
|
||||
private ILogger _logger;
|
||||
|
||||
@@ -83,14 +83,18 @@ public partial class MainViewModel : ViewModelBase
|
||||
|
||||
private void CheckMigration()
|
||||
{
|
||||
if (!ConfigurationService.GetConfigValue(LauncherConVar.DoMigration))
|
||||
return;
|
||||
|
||||
var loadingHandler = ViewHelperService.GetViewModel<LoadingContextViewModel>();
|
||||
loadingHandler.LoadingName = "Migration task, please wait...";
|
||||
loadingHandler.IsCancellable = false;
|
||||
|
||||
if (!ContentService.CheckMigration(loadingHandler))
|
||||
return;
|
||||
|
||||
|
||||
OnPopupRequired(loadingHandler);
|
||||
ConfigurationService.SetConfigValue(LauncherConVar.DoMigration, false);
|
||||
}
|
||||
|
||||
partial void OnSelectedListItemChanged(ListItemTemplate? value)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
@@ -34,6 +35,7 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
[ObservableProperty] private string _currentPassword = string.Empty;
|
||||
|
||||
[ObservableProperty] private bool _isLogged;
|
||||
[ObservableProperty] private bool _doRetryAuth;
|
||||
|
||||
private bool _isProfilesEmpty;
|
||||
[GenerateProperty] private PopupMessageService PopupMessageService { get; } = default!;
|
||||
@@ -62,7 +64,7 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
protected override void Initialise()
|
||||
{
|
||||
_logger = DebugService.GetLogger(this);
|
||||
ReadAuthConfig();
|
||||
Task.Run(ReadAuthConfig);
|
||||
}
|
||||
|
||||
public void AuthByProfile(ProfileAuthCredentials credentials)
|
||||
@@ -95,12 +97,14 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
{
|
||||
try
|
||||
{
|
||||
await TryAuth(CurrentLogin, CurrentPassword, server,code);
|
||||
await CatchAuthError(async () => await TryAuth(CurrentLogin, CurrentPassword, server, code), ()=> message.Dispose());
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = e;
|
||||
var unexpectedError = new Exception("An unexpected error occurred during authentication.", ex);
|
||||
_logger.Error(unexpectedError);
|
||||
PopupMessageService.Popup(unexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,19 +117,35 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
});
|
||||
}
|
||||
|
||||
private async Task TryAuth(string login, string password, string authServer,string? code)
|
||||
private async Task TryAuth(CurrentAuthInfo currentAuthInfo)
|
||||
{
|
||||
CurrentLogin = currentAuthInfo.Login;
|
||||
CurrentAuthServer = currentAuthInfo.AuthServer;
|
||||
await AuthService.SetAuth(currentAuthInfo);
|
||||
IsLogged = true;
|
||||
}
|
||||
|
||||
private async Task TryAuth(string login, string password, string authServer, string? code)
|
||||
{
|
||||
await AuthService.Auth(new AuthLoginPassword(login, password, authServer), code);
|
||||
CurrentLogin = login;
|
||||
CurrentPassword = password;
|
||||
CurrentAuthServer = authServer;
|
||||
IsLogged = true;
|
||||
ConfigurationService.SetConfigValue(LauncherConVar.AuthCurrent, AuthService.SelectedAuth);
|
||||
}
|
||||
|
||||
private async Task CatchAuthError(Func<Task> a, Action? onError)
|
||||
{
|
||||
DoRetryAuth = false;
|
||||
|
||||
try
|
||||
{
|
||||
await AuthService.Auth(new AuthLoginPassword(login, password, authServer), code);
|
||||
CurrentLogin = login;
|
||||
CurrentPassword = password;
|
||||
CurrentAuthServer = authServer;
|
||||
IsLogged = true;
|
||||
ConfigurationService.SetConfigValue(LauncherConVar.AuthCurrent, AuthService.SelectedAuth);
|
||||
await a();
|
||||
}
|
||||
catch (AuthException e)
|
||||
{
|
||||
onError?.Invoke();
|
||||
switch (e.Error.Code)
|
||||
{
|
||||
case AuthenticateDenyCode.TfaRequired:
|
||||
@@ -136,13 +156,34 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
_logger.Log("TFA required");
|
||||
break;
|
||||
case AuthenticateDenyCode.InvalidCredentials:
|
||||
PopupMessageService.Popup("Invalid Credentials!");
|
||||
_logger.Error($"Invalid credentials");
|
||||
PopupError("Invalid Credentials! Please, try another password or login!", e);
|
||||
break;
|
||||
default:
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
onError?.Invoke();
|
||||
switch (e.HttpRequestError)
|
||||
{
|
||||
case HttpRequestError.ConnectionError:
|
||||
PopupError("Failed to connect to the authentication server.", e);
|
||||
DoRetryAuth = true;
|
||||
break;
|
||||
|
||||
case HttpRequestError.NameResolutionError:
|
||||
PopupError("Unable to resolve the server address.", e);
|
||||
DoRetryAuth = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
var authError = new Exception("An error occurred during authentication.", e);
|
||||
_logger.Error(authError);
|
||||
PopupMessageService.Popup(authError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTfaEntered(string code)
|
||||
@@ -182,7 +223,7 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
Accounts.Add(alpm);
|
||||
}
|
||||
|
||||
private async void ReadAuthConfig()
|
||||
private async Task ReadAuthConfig()
|
||||
{
|
||||
var message = ViewHelperService.GetViewModel<InfoPopupViewModel>();
|
||||
message.InfoText = "Read configuration file, please wait...";
|
||||
@@ -198,25 +239,35 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
var authUrls = ConfigurationService.GetConfigValue(LauncherConVar.AuthServers)!;
|
||||
foreach (var url in authUrls) AuthUrls.Add(url);
|
||||
if(authUrls.Length > 0) AuthItemSelect = authUrls[0];
|
||||
|
||||
message.Dispose();
|
||||
|
||||
DoCurrentAuth();
|
||||
}
|
||||
|
||||
public async void DoCurrentAuth()
|
||||
{
|
||||
var message = ViewHelperService.GetViewModel<InfoPopupViewModel>();
|
||||
message.InfoText = "Trying to auth with saved data...";
|
||||
message.IsInfoClosable = false;
|
||||
PopupMessageService.Popup(message);
|
||||
|
||||
var currProfile = ConfigurationService.GetConfigValue(LauncherConVar.AuthCurrent);
|
||||
|
||||
if (currProfile != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentLogin = currProfile.Login;
|
||||
CurrentAuthServer = currProfile.AuthServer;
|
||||
|
||||
IsLogged = await AuthService.SetAuth(currProfile);
|
||||
await CatchAuthError(async () => await TryAuth(currProfile), () => message.Dispose());
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
message.Dispose();
|
||||
PopupMessageService.Popup(e);
|
||||
var unexpectedError = new Exception("An unexpected error occurred during authentication.", ex);
|
||||
_logger.Error(unexpectedError);
|
||||
PopupMessageService.Popup(unexpectedError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
message.Dispose();
|
||||
}
|
||||
|
||||
@@ -237,6 +288,17 @@ public partial class AccountInfoViewModel : ViewModelBase
|
||||
DirtyProfile();
|
||||
}
|
||||
|
||||
private void PopupError(string message, Exception e)
|
||||
{
|
||||
message = "An error occurred during authentication: " + message;
|
||||
_logger.Error(new Exception(message, e));
|
||||
|
||||
var messageView = ViewHelperService.GetViewModel<InfoPopupViewModel>();
|
||||
messageView.InfoText = message;
|
||||
messageView.IsInfoClosable = true;
|
||||
PopupMessageService.Popup(messageView);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OnExpandAuthUrl()
|
||||
{
|
||||
|
||||
@@ -102,6 +102,17 @@
|
||||
Margin="0,0,0,20"
|
||||
Path="/Assets/svg/user.svg" />
|
||||
<StackPanel HorizontalAlignment="Center">
|
||||
<StackPanel IsVisible="{Binding DoRetryAuth}">
|
||||
<Border Background="{StaticResource DefaultSelected}" BoxShadow="{StaticResource DefaultShadow}">
|
||||
<Button
|
||||
Command="{Binding DoCurrentAuth}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center">
|
||||
<Label>Retry previous auth</Label>
|
||||
</Button>
|
||||
</Border>
|
||||
<Label HorizontalAlignment="Center">Or try another account</Label>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center">
|
||||
Login:
|
||||
|
||||
@@ -53,25 +53,21 @@ public class AuthService(
|
||||
SelectedAuth = null;
|
||||
}
|
||||
|
||||
public async Task<bool> SetAuth(CurrentAuthInfo info)
|
||||
public async Task SetAuth(CurrentAuthInfo info)
|
||||
{
|
||||
SelectedAuth = info;
|
||||
return await EnsureToken();
|
||||
await EnsureToken();
|
||||
}
|
||||
|
||||
public async Task<bool> EnsureToken()
|
||||
public async Task EnsureToken()
|
||||
{
|
||||
if (SelectedAuth is null) return false;
|
||||
if (SelectedAuth is null) throw new Exception("Auth info is not set!");
|
||||
|
||||
var authUrl = new Uri($"{SelectedAuth.AuthServer}api/auth/ping");
|
||||
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, authUrl);
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("SS14Auth", SelectedAuth.Token.Token);
|
||||
using var resp = await _httpClient.SendAsync(requestMessage, cancellationService.Token);
|
||||
|
||||
if (!resp.IsSuccessStatusCode) SelectedAuth = null;
|
||||
|
||||
return resp.IsSuccessStatusCode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user