- tweak: Improve auth page

This commit is contained in:
2025-06-20 16:27:42 +03:00
parent 10d317c867
commit 875dacaa18
6 changed files with 152 additions and 87 deletions

View File

@@ -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">
<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">
<!-- 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="BorderThickness" Value="2,2,2,2"/>
<Setter Property="BorderBrush" Value="Azure"/>
</Style>
<Setter Property="CornerRadius" Value="5"/>
<Setter Property="BorderThickness" Value="1,0,1,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>
</Styles>

View File

@@ -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", []);

View File

@@ -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,6 +83,9 @@ 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;
@@ -91,6 +94,7 @@ public partial class MainViewModel : ViewModelBase
return;
OnPopupRequired(loadingHandler);
ConfigurationService.SetConfigValue(LauncherConVar.DoMigration, false);
}
partial void OnSelectedListItemChanged(ListItemTemplate? value)

View File

@@ -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,9 +117,15 @@ public partial class AccountInfoViewModel : ViewModelBase
});
}
private async Task TryAuth(string login, string password, string authServer,string? code)
private async Task TryAuth(CurrentAuthInfo currentAuthInfo)
{
try
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;
@@ -124,8 +134,18 @@ public partial class AccountInfoViewModel : ViewModelBase
IsLogged = true;
ConfigurationService.SetConfigValue(LauncherConVar.AuthCurrent, AuthService.SelectedAuth);
}
private async Task CatchAuthError(Func<Task> a, Action? onError)
{
DoRetryAuth = false;
try
{
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,6 +239,17 @@ 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);
@@ -205,15 +257,14 @@ public partial class AccountInfoViewModel : ViewModelBase
{
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;
}
}
@@ -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()
{

View File

@@ -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:

View File

@@ -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;
}
}