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 _settingsProvider; private readonly ILogger _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 settingsProvider) { _serviceProvider = serviceProvider; _connectionClient = connectionClient; _authenticationService = authenticationService; _settingsProvider = settingsProvider; _logger = serviceProvider.GetRequiredService>(); SplashViewModel = new SplashViewModel(); ServerSelectionViewModel = new ServerSelectionViewModel(connectionClient, settingsProvider, serviceProvider.GetRequiredService()); LoginViewModel = new LoginViewModel(connectionClient, authenticationService, serviceProvider.GetRequiredService(), 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; } /// /// 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. /// 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; if (serverOnline) await LoginViewModel.TryAutoRedirectToProviderAsync(); 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; await LoginViewModel.TryAutoRedirectToProviderAsync(); } 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; } }