LANCommander/LANCommander.Launcher.Avalonia/ViewModels/Components/LibrarySidebarViewModel.cs
Pat Hartl 81a6a3a0fc Rework views / interactions
- Added a horizontal and list view for depot/library
- Games only show covers (with fallback cover)
- Added install addon / directory select dialog
- Fixed download queue
- Added footer somewhat matching Blazor launcher
- Temporarily removed sidebar
- Implemented custom window chrome
- Added profile button
- Reworked game details to better match Blazor launcher
- Added grouping by genre, collection, or first letter
2026-03-27 18:59:17 -04:00

183 lines
5.2 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LANCommander.Launcher.Data.Models;
using LANCommander.Launcher.Services;
using LANCommander.SDK.Enums;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace LANCommander.Launcher.Avalonia.ViewModels.Components;
/// <summary>
/// ViewModel for the library sidebar showing user's games
/// </summary>
public partial class LibrarySidebarViewModel : ViewModelBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<LibrarySidebarViewModel> _logger;
[ObservableProperty]
private ObservableCollection<LibraryItemViewModel> _items = new();
[ObservableProperty]
private LibraryItemViewModel? _selectedItem;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private bool _isDepotSelected = true;
[ObservableProperty]
private string _statusMessage = string.Empty;
[ObservableProperty]
private bool _isOfflineMode;
public event EventHandler? DepotSelected;
public event EventHandler<LibraryItemViewModel>? ItemSelected;
public event EventHandler? RefreshRequested;
public event EventHandler? LogoutRequested;
public event EventHandler? SettingsRequested;
public event EventHandler? GoOnlineRequested;
public event EventHandler? GoOfflineRequested;
// Prevents OnSelectedItemChanged from firing ItemSelected during programmatic selection
private bool _suppressItemSelected;
public LibrarySidebarViewModel(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_logger = serviceProvider.GetRequiredService<ILogger<LibrarySidebarViewModel>>();
}
partial void OnSelectedItemChanged(LibraryItemViewModel? value)
{
if (!_suppressItemSelected && value != null)
{
IsDepotSelected = false;
ItemSelected?.Invoke(this, value);
}
}
[RelayCommand]
public async Task LoadAsync()
{
IsLoading = true;
Items.Clear();
_logger.LogDebug("Loading library items...");
try
{
using var scope = _serviceProvider.CreateScope();
var libraryService = scope.ServiceProvider.GetRequiredService<LibraryService>();
var mediaService = scope.ServiceProvider.GetRequiredService<MediaService>();
var items = await libraryService.GetItemsAsync();
foreach (var item in items ?? [])
{
if (item.DataItem is Game game)
{
var iconPath = GetIconPath(game, mediaService);
Items.Add(new LibraryItemViewModel(game, iconPath));
}
}
StatusMessage = IsOfflineMode ? $"{Items.Count} games (offline)" : $"{Items.Count} games";
_logger.LogDebug("Loaded {Count} library items", Items.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load library items");
StatusMessage = "Failed to load";
}
finally
{
IsLoading = false;
}
}
private string? GetIconPath(Game game, MediaService mediaService)
{
var icon = game.Media?.FirstOrDefault(m => m.Type == MediaType.Icon);
if (icon == null) return null;
var path = mediaService.GetImagePath(icon);
return mediaService.FileExists(icon) ? path : null;
}
[RelayCommand]
private void ShowDepot()
{
SelectedItem = null;
IsDepotSelected = true;
DepotSelected?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void SelectItem(LibraryItemViewModel? item)
{
if (item == null) return;
SelectedItem = item;
// OnSelectedItemChanged handles IsDepotSelected and ItemSelected event
}
[RelayCommand]
private void Refresh()
{
RefreshRequested?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void Logout()
{
LogoutRequested?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void Settings()
{
SettingsRequested?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void GoOnline()
{
GoOnlineRequested?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private void GoOffline()
{
GoOfflineRequested?.Invoke(this, EventArgs.Empty);
}
public void ClearSelection()
{
SelectedItem = null;
IsDepotSelected = false;
}
public void SelectDepot()
{
SelectedItem = null;
IsDepotSelected = true;
}
public void SelectItemById(Guid id)
{
var item = Items.FirstOrDefault(i => i.Id == id);
if (item != null)
{
_suppressItemSelected = true;
SelectedItem = item;
_suppressItemSelected = false;
IsDepotSelected = false;
}
}
}