LANCommander/LANCommander.Launcher/ViewModels/MainWindowViewModel.cs
Pat Hartl b183e4b18c Remove old launcher, rename Avalonia launcher
Also adds ARM builds for Linux + Windows launcher
2026-06-06 05:49:24 -05:00

222 lines
8.5 KiB
C#

using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LANCommander.Launcher.Services;
using LANCommander.SDK.Providers;
using LANCommander.SDK.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace LANCommander.Launcher.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
private readonly IServiceProvider _serviceProvider;
private readonly IConnectionClient _connectionClient;
private readonly AuthenticationService _authenticationService;
private readonly SettingsProvider<Settings.Settings> _settingsProvider;
private readonly ILogger<MainWindowViewModel> _logger;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsLogoVisible))]
private ViewModelBase _currentView;
[ObservableProperty]
private string _title = "LANCommander Launcher";
[ObservableProperty]
private bool _isShellActive;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsNotBigScreenMode))]
private bool _isBigScreenMode;
public bool IsNotBigScreenMode => !IsBigScreenMode;
public bool IsLogoVisible => CurrentView != ServerSelectionViewModel && CurrentView != LoginViewModel;
public bool ShowTitlebarTint => !IsShellActive || ShellViewModel.IsTitlebarTinted;
public event EventHandler? BigScreenModeChanged;
public event EventHandler? ExitLauncherRequested;
[RelayCommand]
private void EnterBigScreenMode()
{
IsBigScreenMode = true;
_settingsProvider.Update(s => s.Window.BigScreenMode = true);
BigScreenModeChanged?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void ExitBigScreenMode()
{
IsBigScreenMode = false;
_settingsProvider.Update(s => s.Window.BigScreenMode = false);
BigScreenModeChanged?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void ExitLauncher()
{
ExitLauncherRequested?.Invoke(this, EventArgs.Empty);
}
partial void OnCurrentViewChanged(ViewModelBase value)
{
IsShellActive = value is ShellViewModel;
OnPropertyChanged(nameof(ShowTitlebarTint));
}
public SplashViewModel SplashViewModel { get; }
public ServerSelectionViewModel ServerSelectionViewModel { get; }
public LoginViewModel LoginViewModel { get; }
public ShellViewModel ShellViewModel { get; }
public MainWindowViewModel(
IServiceProvider serviceProvider,
IConnectionClient connectionClient,
AuthenticationService authenticationService,
SettingsProvider<Settings.Settings> settingsProvider)
{
_serviceProvider = serviceProvider;
_connectionClient = connectionClient;
_authenticationService = authenticationService;
_settingsProvider = settingsProvider;
_logger = serviceProvider.GetRequiredService<ILogger<MainWindowViewModel>>();
SplashViewModel = new SplashViewModel();
ServerSelectionViewModel = new ServerSelectionViewModel(connectionClient, settingsProvider, serviceProvider.GetRequiredService<BeaconClient>());
LoginViewModel = new LoginViewModel(connectionClient, authenticationService, serviceProvider.GetRequiredService<AuthenticationClient>(), settingsProvider);
ShellViewModel = new ShellViewModel(serviceProvider);
// Propagate shell titlebar tint changes
ShellViewModel.PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(ShellViewModel.IsTitlebarTinted))
OnPropertyChanged(nameof(ShowTitlebarTint));
};
// Wire up navigation events
ServerSelectionViewModel.ServerConnected += OnServerConnected;
LoginViewModel.LoginSucceeded += OnLoginSucceeded;
LoginViewModel.ChangeServerRequested += OnChangeServerRequested;
ShellViewModel.LogoutRequested += OnLogoutRequested;
// Start with splash screen
_currentView = SplashViewModel;
// Restore big screen mode from settings or command line
if (settingsProvider.CurrentValue.Window.BigScreenMode)
_isBigScreenMode = true;
}
/// <summary>
/// Enables big screen mode from an external source (e.g. command line).
/// Must be called before InitializeAsync so the event fires after the window is ready.
/// </summary>
public void SetBigScreenMode()
{
_isBigScreenMode = true;
}
public async Task InitializeAsync()
{
SplashViewModel.UpdateStatus("Checking connection...");
// Check if we have a saved server address and valid token
var settings = _settingsProvider.CurrentValue;
if (settings.Authentication?.ServerAddress != null)
{
SplashViewModel.UpdateStatus("Connecting to server...");
await _connectionClient.UpdateServerAddressAsync(settings.Authentication.ServerAddress.ToString());
// Check if server is reachable
var serverOnline = await _connectionClient.PingAsync();
if (_authenticationService.HasStoredCredentials())
{
if (serverOnline)
{
try
{
SplashViewModel.UpdateStatus("Authenticating...");
// Try to login with stored credentials
await _authenticationService.Login();
if (_connectionClient.IsConnected())
{
SplashViewModel.UpdateStatus("Loading library...");
// Token is valid - go directly to shell in online mode
ShellViewModel.SetOfflineMode(false);
await ShellViewModel.InitializeAsync();
CurrentView = ShellViewModel;
return;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Token validation failed");
// Token validation failed - continue to check offline mode
}
}
else
{
// Server offline but we have stored credentials - go to shell in offline mode
_logger.LogInformation("Server unreachable, starting in offline mode with stored credentials");
SplashViewModel.UpdateStatus("Server offline, starting in offline mode...");
await _connectionClient.EnableOfflineModeAsync();
ShellViewModel.SetOfflineMode(true);
await ShellViewModel.InitializeAsync();
CurrentView = ShellViewModel;
return;
}
}
// We have a server but no valid token - go to login
// If server is offline and no credentials, user stays on login (can't proceed)
LoginViewModel.ServerAddress = settings.Authentication.ServerAddress.ToString();
LoginViewModel.IsServerOffline = !serverOnline;
if (serverOnline)
await LoginViewModel.LoadAuthenticationProvidersAsync();
CurrentView = LoginViewModel;
return;
}
// No saved server - show server selection
CurrentView = ServerSelectionViewModel;
}
private async void OnServerConnected(object? sender, EventArgs e)
{
LoginViewModel.ServerAddress = _connectionClient.GetServerAddress()?.ToString() ?? string.Empty;
LoginViewModel.IsServerOffline = false;
await LoginViewModel.LoadAuthenticationProvidersAsync();
CurrentView = LoginViewModel;
}
private async void OnLoginSucceeded(object? sender, EventArgs e)
{
// Show the splash screen with a loading message while initializing
SplashViewModel.UpdateStatus("Loading library...");
CurrentView = SplashViewModel;
// Initialize shell fully before switching view to avoid rendering uninitialized state
ShellViewModel.SetOfflineMode(false);
await ShellViewModel.InitializeAsync();
CurrentView = ShellViewModel;
}
private void OnChangeServerRequested(object? sender, EventArgs e)
{
CurrentView = ServerSelectionViewModel;
}
private void OnLogoutRequested(object? sender, EventArgs e)
{
CurrentView = LoginViewModel;
}
}