feat: Add library management (add/remove game from library)
This commit is contained in:
parent
e3b3d0ab90
commit
ac2ebeef29
3 changed files with 160 additions and 7 deletions
|
|
@ -68,7 +68,20 @@ public partial class GameDetailViewModel : ViewModelBase
|
|||
[ObservableProperty]
|
||||
private bool _isLoadingMedia;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isInLibrary;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isAddingToLibrary;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isRemovingFromLibrary;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _statusMessage;
|
||||
|
||||
public event EventHandler? BackRequested;
|
||||
public event EventHandler? LibraryChanged;
|
||||
|
||||
public GameDetailViewModel(IServiceProvider serviceProvider)
|
||||
{
|
||||
|
|
@ -88,9 +101,14 @@ public partial class GameDetailViewModel : ViewModelBase
|
|||
ReleasedOn = game.ReleasedOn ?? DateTime.MinValue;
|
||||
ReleaseYear = game.ReleasedOn?.Year > 1 ? game.ReleasedOn.Value.Year.ToString() : "Unknown";
|
||||
Singleplayer = game.Singleplayer;
|
||||
StatusMessage = null;
|
||||
|
||||
// Check library status
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var libraryService = scope.ServiceProvider.GetRequiredService<LibraryService>();
|
||||
IsInLibrary = libraryService.IsInLibrary(game.Id);
|
||||
|
||||
// Get media paths from local storage
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var mediaService = scope.ServiceProvider.GetRequiredService<MediaService>();
|
||||
|
||||
BannerPath = GetLocalMediaPath(game.Media, MediaType.Cover, mediaService);
|
||||
|
|
@ -145,6 +163,12 @@ public partial class GameDetailViewModel : ViewModelBase
|
|||
ReleasedOn = game.ReleasedOn;
|
||||
ReleaseYear = game.ReleasedOn.Year > 1 ? game.ReleasedOn.Year.ToString() : "Unknown";
|
||||
Singleplayer = game.Singleplayer;
|
||||
StatusMessage = null;
|
||||
|
||||
// Check library status
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var libraryService = scope.ServiceProvider.GetRequiredService<LibraryService>();
|
||||
IsInLibrary = libraryService.IsInLibrary(game.Id);
|
||||
|
||||
// Reset media paths while loading
|
||||
BannerPath = null;
|
||||
|
|
@ -192,7 +216,6 @@ public partial class GameDetailViewModel : ViewModelBase
|
|||
IsLoadingMedia = true;
|
||||
try
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var mediaClient = scope.ServiceProvider.GetRequiredService<MediaClient>();
|
||||
|
||||
BannerPath = await GetOrDownloadMediaPathAsync(game.Media, MediaType.Cover, mediaClient);
|
||||
|
|
@ -210,6 +233,88 @@ public partial class GameDetailViewModel : ViewModelBase
|
|||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddToLibraryAsync()
|
||||
{
|
||||
if (IsInLibrary || IsAddingToLibrary) return;
|
||||
|
||||
IsAddingToLibrary = true;
|
||||
StatusMessage = "Adding to library...";
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var importService = scope.ServiceProvider.GetRequiredService<ImportService>();
|
||||
var libraryService = scope.ServiceProvider.GetRequiredService<LibraryService>();
|
||||
|
||||
_logger.LogInformation("Adding game {GameId} ({Title}) to library", Id, Title);
|
||||
|
||||
// Import the game data
|
||||
await importService.ImportGameAsync(Id);
|
||||
|
||||
// Add to library
|
||||
await libraryService.AddToLibraryAsync(Id);
|
||||
|
||||
// Refresh library items
|
||||
await libraryService.RefreshItemsAsync();
|
||||
|
||||
IsInLibrary = true;
|
||||
StatusMessage = "Added to library!";
|
||||
_logger.LogInformation("Game {GameId} ({Title}) added to library", Id, Title);
|
||||
|
||||
// Notify that library has changed
|
||||
LibraryChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to add game {GameId} ({Title}) to library", Id, Title);
|
||||
StatusMessage = $"Failed to add to library: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsAddingToLibrary = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RemoveFromLibraryAsync()
|
||||
{
|
||||
if (!IsInLibrary || IsRemovingFromLibrary) return;
|
||||
|
||||
IsRemovingFromLibrary = true;
|
||||
StatusMessage = "Removing from library...";
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var libraryService = scope.ServiceProvider.GetRequiredService<LibraryService>();
|
||||
|
||||
_logger.LogInformation("Removing game {GameId} ({Title}) from library", Id, Title);
|
||||
|
||||
// Remove from library
|
||||
await libraryService.RemoveFromLibraryAsync(Id);
|
||||
|
||||
// Refresh library items
|
||||
await libraryService.RefreshItemsAsync();
|
||||
|
||||
IsInLibrary = false;
|
||||
StatusMessage = "Removed from library";
|
||||
_logger.LogInformation("Game {GameId} ({Title}) removed from library", Id, Title);
|
||||
|
||||
// Notify that library has changed
|
||||
LibraryChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to remove game {GameId} ({Title}) from library", Id, Title);
|
||||
StatusMessage = $"Failed to remove from library: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRemovingFromLibrary = false;
|
||||
}
|
||||
}
|
||||
|
||||
private string? GetLocalMediaPath(System.Collections.Generic.ICollection<Data.Models.Media>? mediaCollection, MediaType type, MediaService mediaService)
|
||||
{
|
||||
var media = mediaCollection?.FirstOrDefault(m => m.Type == type);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public partial class ShellViewModel : ViewModelBase
|
|||
|
||||
GamesListViewModel.GameSelected += OnGameSelected;
|
||||
GameDetailViewModel.BackRequested += OnBackFromGameDetail;
|
||||
GameDetailViewModel.LibraryChanged += OnLibraryChanged;
|
||||
|
||||
// Import library from server and load data
|
||||
await ImportAndLoadAsync();
|
||||
|
|
@ -148,4 +149,15 @@ public partial class ShellViewModel : ViewModelBase
|
|||
{
|
||||
ShowDepot();
|
||||
}
|
||||
|
||||
private async void OnLibraryChanged(object? sender, EventArgs e)
|
||||
{
|
||||
_logger.LogInformation("Library changed, refreshing sidebar and games list...");
|
||||
|
||||
// Refresh the sidebar to show updated library
|
||||
await Sidebar.LoadAsync();
|
||||
|
||||
// Refresh the games list to update "In Library" status
|
||||
await GamesListViewModel.LoadGamesAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,11 +169,47 @@
|
|||
Padding="16"
|
||||
CornerRadius="8"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Actions" FontWeight="SemiBold" />
|
||||
<TextBlock Text="Install, Play, and other game actions will be available here in a future update."
|
||||
Opacity="0.6"
|
||||
TextWrapping="Wrap" />
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Library" FontWeight="SemiBold" FontSize="16" />
|
||||
|
||||
<!-- Status message -->
|
||||
<TextBlock Text="{Binding StatusMessage}"
|
||||
Opacity="0.7"
|
||||
IsVisible="{Binding StatusMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||
|
||||
<!-- Add to Library button (shown when not in library) -->
|
||||
<Button Command="{Binding AddToLibraryCommand}"
|
||||
IsVisible="{Binding !IsInLibrary}"
|
||||
IsEnabled="{Binding !IsAddingToLibrary}"
|
||||
HorizontalAlignment="Left"
|
||||
Padding="16,10">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="+" FontWeight="Bold" IsVisible="{Binding !IsAddingToLibrary}" />
|
||||
<TextBlock Text="Add to Library" IsVisible="{Binding !IsAddingToLibrary}" />
|
||||
<TextBlock Text="Adding..." IsVisible="{Binding IsAddingToLibrary}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- In Library indicator + Remove button (shown when in library) -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="12" IsVisible="{Binding IsInLibrary}">
|
||||
<Border Background="{DynamicResource SystemAccentColor}"
|
||||
CornerRadius="4"
|
||||
Padding="12,8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="✓" Foreground="White" />
|
||||
<TextBlock Text="In Library" Foreground="White" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Button Command="{Binding RemoveFromLibraryCommand}"
|
||||
IsEnabled="{Binding !IsRemovingFromLibrary}"
|
||||
Padding="12,8">
|
||||
<Panel>
|
||||
<TextBlock Text="Remove from Library" Foreground="IndianRed" IsVisible="{Binding !IsRemovingFromLibrary}" />
|
||||
<TextBlock Text="Removing..." Foreground="IndianRed" IsVisible="{Binding IsRemovingFromLibrary}" />
|
||||
</Panel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue