LANCommander/LANCommander.Launcher/UI/Depot/Components/DepotGameDetails.razor
2025-09-06 13:33:14 -05:00

367 lines
14 KiB
Text

@using LANCommander.Launcher.Data.Models
@using LANCommander.Launcher.Models
@using LANCommander.SDK.Extensions
@inject DepotService DepotService
@inject LibraryService LibraryService
@inject GameService GameService
@inject InstallService InstallService
@inject ImportService ImportService
@inject SDK.Client Client
@inject ILogger<DepotGameDetails> Logger
@inject NavigationManager NavigationManager
@inject ModalService ModalService
@inject IMessageService MessageService
@inject LocalizationService LocalizationService
<Drawer Placement="DrawerPlacement.Bottom" Class="depot-game-details" @bind-Visible="@Visible" OnClose="Hide">
@if (SelectedGame != null)
{
<div @key="SelectedGame.Id" class="game-details no-scrollbar">
<Button Class="game-details-close-btn" Type="@ButtonType.Text" Icon="@IconType.Outline.Close" OnClick="Hide" />
@if (SelectedGame != null)
{
<div class="game-hero">
<SafeImage Media="@(SelectedGame.Media.FirstOrDefault(m => m.Type == SDK.Enums.MediaType.Logo))" Class="game-details-logo">
<h1>@SelectedGame.Title</h1>
</SafeImage>
<SafeImage Media="@(SelectedGame.Media.FirstOrDefault(m => m.Type == SDK.Enums.MediaType.Background))" Fit="ImageFit.Cover" Class="game-details-background">
<div class="game-details-background-fallback"></div>
</SafeImage>
</div>
<GridRow Gutter="32" Class="game-info" Wrap="false">
<GridCol Flex="@("auto")">
<div class="game-action-bar">
<Space Direction="SpaceDirection.Horizontal" Size="SpaceSize.Large">
<SpaceItem>
@if (IsInstalled)
{
if (IsInLibrary)
{
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" OnClick="ViewInLibrary">@LocalizationService.GetString("ViewInLibrary")</Button>
}
else
{
<Button Type="ButtonType.Dashed" Size="ButtonSize.Large" Style="pointer-events: none;">@LocalizationService.GetString("Installed")</Button>
}
}
else
{
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Icon="@IconType.Outline.Download" OnClick="Install" Loading="Installing">@LocalizationService.GetString("Install")</Button>
}
@if (IsInLibrary || SelectedGame.InLibrary)
{
<Tooltip Title="@LocalizationService.GetString("RemoveFromLibrary")">
<Button Size="ButtonSize.Large" Type="@ButtonType.Text" Icon="@IconType.Outline.Close" Danger OnClick="RemoveFromLibrary" Loading="RemovingFromLibrary" />
</Tooltip>
}
else
{
<Tooltip Title="@LocalizationService.GetString("AddToLibrary")">
<Button Size="ButtonSize.Large" Type="@ButtonType.Text" Icon="@IconType.Outline.Plus" OnClick="AddToLibrary" Loading="AddingToLibrary" />
</Tooltip>
}
</SpaceItem>
@if (!IsInstalled && Client.IsConnected())
{
<SpaceItem>
<Statistic Title="@LocalizationService.GetString("DownloadSize")" Value="@ByteSizeLib.ByteSize.FromBytes(GetDownloadSize()).ToString()" />
</SpaceItem>
}
@if (SelectedGame.PlaySessions.HasAny())
{
<SpaceItem>
<Statistic Title="@LocalizationService.GetString("PlayTime")" Value="@GetPlayTime(SelectedGame)" />
</SpaceItem>
<SpaceItem>
<Statistic Title="@LocalizationService.GetString("LastPlayed")" Value="@GetLastPlayed(SelectedGame)" />
</SpaceItem>
}
</Space>
</div>
<div class="game-metadata">
@if (!String.IsNullOrWhiteSpace(SelectedGame.Description))
{
<div class="game-metadata-description">@SelectedGame.Description</div>
}
@if (SelectedGame!.ReleasedOn > DateTime.MinValue)
{
<div class="game-metadata-released-on">
<h3>@LocalizationService.GetString("ReleasedOn")</h3>
<span>@SelectedGame.ReleasedOn.ToString("MMMM d, yyyy")</span>
</div>
}
@if (SelectedGame.Developers.HasAny())
{
<div class="game-metadata-developers">
<h3>@LocalizationService.GetString("Developers")</h3>
<span>@(String.Join(", ", SelectedGame.Developers.Select(c => c.Name)))</span>
</div>
}
@if (SelectedGame.Publishers.HasAny())
{
<div class="game-metadata-publishers">
<h3>@LocalizationService.GetString("Publishers")</h3>
<span>@(String.Join(", ", SelectedGame.Publishers.Select(c => c.Name)))</span>
</div>
}
@if (SelectedGame.Genres.HasAny())
{
<div class="game-metadata-genres">
<h3>@LocalizationService.GetString("Genres")</h3>
<span>@(String.Join(", ", SelectedGame.Genres.Select(g => g.Name)))</span>
</div>
}
@if (SelectedGame.Tags.HasAny())
{
<div class="game-metadata-tags">
<h3>@LocalizationService.GetString("Tags")</h3>
<span>@(String.Join(", ", SelectedGame.Tags.Select(t => t.Name)))</span>
</div>
}
</div>
</GridCol>
<GridCol Flex="@("256px")" Class="game-cover">
@if (SelectedGame.Media.HasAny(m => m.Type == SDK.Enums.MediaType.Cover))
{
<SafeImage Media="@(SelectedGame.Media.First(m => m.Type == SDK.Enums.MediaType.Cover))">
<div class="depot-game-default-cover">
<span>@SelectedGame.Title</span>
</div>
</SafeImage>
}
</GridCol>
</GridRow>
}
</div>
}
</Drawer>
@code {
[Parameter] public Guid ItemId { get; set; }
[Parameter] public EventCallback OnClose { get; set; }
SDK.Models.Game? SelectedGame { get; set; }
bool Visible { get; set; } = false;
bool IsInstalled { get; set; } = false;
bool IsInLibrary { get; set; } = false;
bool Installing { get; set; } = false;
bool AddingToLibrary { get; set; } = false;
bool RemovingFromLibrary { get; set; } = false;
Settings Settings = SettingService.GetSettings();
protected override async Task OnParametersSetAsync()
{
Installing = false;
AddingToLibrary = false;
RemovingFromLibrary = false;
try
{
if (ItemId != Guid.Empty)
{
SelectedGame = await Client.Games.GetAsync(ItemId);
var game = await GameService.GetAsync(ItemId);
IsInstalled = game?.Installed ?? false;
IsInLibrary = LibraryService.IsInLibrary(ItemId);
}
}
catch (Exception ex)
{
SelectedGame = null;
Logger?.LogError(ex, "Could not load game with ID {GameId}", ItemId);
}
await InvokeAsync(StateHasChanged);
}
public async Task Show()
{
Visible = true;
await InvokeAsync(StateHasChanged);
}
public async Task Hide()
{
Visible = false;
if (OnClose.HasDelegate)
await OnClose.InvokeAsync();
await InvokeAsync(StateHasChanged);
}
async Task Install()
{
if (SelectedGame == null)
{
MessageService.Success($"No game selected. Aborting installation!");
return;
}
Installing = true;
StateHasChanged();
await Task.Yield();
if (!SelectedGame.InLibrary)
await AddToLibrary();
var libraryItem = LibraryService.GetItem(SelectedGame.Id);
var game = libraryItem.DataItem as Game;
if (game == null)
{
MessageService.Success($"No game added to library. Unable to proceed!");
return;
}
await Hide();
if (Settings.Games.InstallDirectories.Length > 1 || (game?.DependentGames?.Any(g => g.Type.IsIn(Data.Enums.GameType.Expansion, Data.Enums.GameType.Mod)) ?? false))
{
var modalOptions = new ModalOptions()
{
Title = $"Install {SelectedGame.Title}",
Maximizable = false,
DefaultMaximized = false,
Closable = true,
OkText = "Install",
Draggable = true,
Centered = true,
WrapClassName = "ant-modal-wrap-no-padding",
Footer = null,
};
var modalRef = ModalService.CreateModal<InstallDialog, Models.ListItem, string>(modalOptions, libraryItem);
}
else
{
await InstallService.Add(game!);
}
}
async Task AddToLibrary()
{
if (!Installing)
{
AddingToLibrary = true;
StateHasChanged();
await Task.Yield();
}
if (SelectedGame == null)
{
MessageService.Success($"No game selected. Aborting adding to library!");
return;
}
try
{
await ImportService.ImportGameAsync(SelectedGame.Id);
await LibraryService.AddToLibraryAsync(SelectedGame.Id);
await LibraryService.RefreshItemsAsync();
MessageService.Success($"{SelectedGame.Title} was added to your library!");
SelectedGame.InLibrary = IsInLibrary = LibraryService.IsInLibrary(SelectedGame.Id);
}
catch (Exception ex)
{
Logger?.LogError(ex, $"{SelectedGame.Title} ({SelectedGame.Id}) could not be added to your library!");
MessageService.Error($"{SelectedGame.Title} could not be added to your library!");
}
AddingToLibrary = false;
StateHasChanged();
await Task.Yield();
}
async Task RemoveFromLibrary()
{
if (!Installing)
{
RemovingFromLibrary = true;
StateHasChanged();
await Task.Yield();
}
if (SelectedGame == null)
{
MessageService.Success($"No game selected. Aborting removing from library!");
return;
}
try
{
await LibraryService.RemoveFromLibraryAsync(SelectedGame.Id);
await LibraryService.RefreshItemsAsync();
MessageService.Success($"{SelectedGame.Title} was removed from your library!");
SelectedGame.InLibrary = IsInLibrary = LibraryService.IsInLibrary(SelectedGame.Id);
}
catch (Exception ex)
{
Logger?.LogError(ex, $"{SelectedGame.Title} ({SelectedGame.Id}) could not be removed from your library!");
MessageService.Error($"{SelectedGame.Title} could not be removed from your library!");
}
RemovingFromLibrary = false;
StateHasChanged();
await Task.Yield();
}
long GetDownloadSize()
{
long size = 0;
if (SelectedGame != null && SelectedGame.Archives.Any())
size = SelectedGame.Archives.OrderByDescending(a => a.CreatedOn).First().CompressedSize;
return size;
}
string GetPlayTime(SDK.Models.Game game)
{
var totalTime = new TimeSpan(game.PlaySessions
.Where(ps => ps.End != null && ps.Start != null)
.Select(ps => ps.End!.Value.Subtract(ps.Start!.Value))
.Sum(ts => ts.Ticks));
if (totalTime.TotalMinutes < 1)
return "None";
else if (totalTime.TotalHours < 1)
return totalTime.TotalMinutes.ToString("0") + " minutes";
else
return totalTime.TotalHours.ToString("0.##") + " hours";
}
string GetLastPlayed(SDK.Models.Game game)
{
var lastSession = game.PlaySessions.Where(ps => ps.End != null && ps.Start != null).OrderByDescending(ps => ps.End).FirstOrDefault();
return lastSession?.End?.ToRelativeDate() ?? "Never";
}
async Task ViewInLibrary()
{
await Hide();
NavigationManager.NavigateTo($"/{SelectedGame.Id}", false);
}
}