LANCommander/LANCommander.Launcher/UI/Library/Index.razor
Pat Hartl 5a32bbf890 Add localization support to launcher
Adds localization across the launcher and includes English, German, Spanish, French, Italian, Japanese, Korean, Dutch, Portuguese, Ukranian, and Chinese. These localizations were generated by Claude 3.5 and need to be reviewed by a human for accuracy.
2025-08-18 21:29:38 -05:00

406 lines
17 KiB
Text

@page "/"
@page "/{id:guid}"
@using LANCommander.Launcher.Data.Models
@using LANCommander.Launcher.Models.Enums
@using LANCommander.Launcher.Models
@using System.Diagnostics
@using LANCommander.Launcher.UI.Library.Components
@using LANCommander.SDK.Helpers
@inject SDK.Client Client
@inject NavigationManager NavigationManager
@inject LibraryService LibraryService
@inject InstallService InstallService
@inject GameService GameService
@inject ModalService ModalService
@inject ConfirmService ConfirmService
@inject ImportService ImportService
@inject LocalizationService LocalizationService
<Layout>
<Content Class="library">
<LibraryList SelectedItem="@(SelectedGame?.Id)" OnItemSelected="@((key) => SelectLibraryItem(key))" />
<div class="game-details no-scrollbar">
@if (SelectedGame != null)
{
<div class="game-hero">
@if (SelectedGame.Media.Any(m => m.Type == SDK.Enums.MediaType.Logo))
{
<MediaImage Id="@(SelectedGame.Media.First(m => m.Type == SDK.Enums.MediaType.Logo).Id)" Key="GameDetailsLogo" Class="game-details-logo" />
}
else
{
<h1>@SelectedGame.Title</h1>
}
@if (SelectedGame.Media.Any(m => m.Type == SDK.Enums.MediaType.Background))
{
<MediaImage Id="@(SelectedGame.Media.First(m => m.Type == SDK.Enums.MediaType.Background).Id)" Key="GameDetailsBackground" Class="game-details-background" />
}
</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 (SelectedItem.State == ListItemState.Installed)
{
<PlayButton LibraryItem="SelectedItem" />
}
else if (SelectedItem.State == ListItemState.NotInstalled)
{
<ConnectionStateView>
<Online>
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Icon="@IconType.Outline.Download" OnClick="() => Install(SelectedItem)">@LocalizationService.GetString("Install")</Button>
</Online>
<Offline>
<Tooltip Title="@LocalizationService.GetString("YouAreCurrentlyOffline")">
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Icon="@IconType.Outline.Download" Disabled>@LocalizationService.GetString("Install")</Button>
</Tooltip>
</Offline>
</ConnectionStateView>
}
else if (SelectedItem.State == ListItemState.Queued)
{
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Icon="@IconType.Outline.Bars" Disabled="true">@LocalizationService.GetString("Queued")</Button>
}
else if (SelectedItem.State == ListItemState.UpdateAvailable)
{
<ConnectionStateView>
<Online>
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Icon="@IconType.Outline.Download" OnClick="() => Update(SelectedItem)">@LocalizationService.GetString("Update")</Button>
</Online>
<Offline>
<Tooltip Title="@LocalizationService.GetString("YouAreCurrentlyOffline")">
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Icon="@IconType.Outline.Download" Disabled>@LocalizationService.GetString("Update")</Button>
</Tooltip>
</Offline>
</ConnectionStateView>
}
else if (SelectedItem.State == ListItemState.Installing)
{
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Loading Disabled="true">@LocalizationService.GetString("Installing")</Button>
}
else if (SelectedItem.State == ListItemState.Uninstalling)
{
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" Loading Disabled="true">@LocalizationService.GetString("Uninstalling")</Button>
}
</SpaceItem>
@if (SelectedItem.State == ListItemState.NotInstalled && Connected)
{
<ConnectionStateView>
<Online>
<SpaceItem>
<Statistic Title="@LocalizationService.GetString("DownloadSize")" Value="@ByteSizeLib.ByteSize.FromBytes(GetDownloadSize()).ToString()"/>
</SpaceItem>
</Online>
</ConnectionStateView>
}
<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.HasValue)
{
<div class="game-metadata-released-on">
<h3>@LocalizationService.GetString("ReleasedOn")</h3>
<span>@SelectedGame.ReleasedOn.Value.ToString("MMMM d, yyyy")</span>
</div>
}
@if (SelectedGame.Developers != null && SelectedGame.Developers.Any())
{
<div class="game-metadata-developers">
<h3>@LocalizationService.GetString("Developers")</h3>
<span>@(String.Join(", ", SelectedGame.Developers.Select(c => c.Name)))</span>
</div>
}
@if (SelectedGame.Publishers != null && SelectedGame.Publishers.Any())
{
<div class="game-metadata-publishers">
<h3>@LocalizationService.GetString("Publishers")</h3>
<span>@(String.Join(", ", SelectedGame.Publishers.Select(c => c.Name)))</span>
</div>
}
@if (SelectedGame.Genres != null && SelectedGame.Genres.Any())
{
<div class="game-metadata-genres">
<h3>@LocalizationService.GetString("Genres")</h3>
<span>@(String.Join(", ", SelectedGame.Genres.Select(g => g.Name)))</span>
</div>
}
@if (SelectedGame.Tags != null && SelectedGame.Tags.Any())
{
<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.Any(m => m.Type == SDK.Enums.MediaType.Cover))
{
<MediaImage Key="GameDetailsCover" Id="@(SelectedGame.Media.First(m => m.Type == SDK.Enums.MediaType.Cover).Id)" />
}
</GridCol>
</GridRow>
}
</div>
</Content>
</Layout>
<div class="logo">
<img src="assets/logo-cut.svg" />
</div>
<LANCommander.Launcher.UI.Components.Footer />
@code {
[Parameter] public Guid Id { get; set; }
[CascadingParameter] public bool Connected { get; set; }
[CascadingParameter] public bool OfflineMode { get; set; }
Models.ListItem SelectedItem { get; set; }
Data.Models.Game SelectedGame { get; set; }
SDK.Models.Game RemoteGame { get; set; }
Settings Settings = SettingService.GetSettings();
protected override async Task OnInitializedAsync()
{
InstallService.OnQueueChanged += OnQueueChanged;
InstallService.OnInstallFail += OnInstallFail;
InstallService.OnInstallComplete += OnInstallComplete;
GameService.OnUninstall += OnUninstall;
GameService.OnUninstallComplete += OnUninstallComplete;
await LoadData();
}
protected override async Task OnParametersSetAsync()
{
if (Id != Guid.Empty)
{
await OnLibraryItemSelected(await LibraryService.GetItemAsync(Id));
if (SelectedGame != null && (Connected && !OfflineMode))
RemoteGame = await Client.Games.GetAsync(SelectedGame.Id);
}
await InvokeAsync(StateHasChanged);
}
async Task LoadData()
{
await LibraryService.RefreshItemsAsync();
await InvokeAsync(StateHasChanged);
}
async Task SelectLibraryItem(Guid id)
{
NavigationManager.NavigateTo($"/{id}");
}
async Task OnLibraryItemSelected(Models.ListItem item)
{
if (item != null && item.DataItem is Data.Models.Game)
{
SelectedItem = item;
SelectedGame = item.DataItem as Data.Models.Game;
await InvokeAsync(StateHasChanged);
}
}
async Task Install(Models.ListItem item)
{
var game = item.DataItem as Game;
var addons = await Client.Games.GetAddonsAsync(game.Id);
if (Settings.Games.InstallDirectories.Length > 1 || (addons != null && addons.Any()))
{
var modalOptions = new ModalOptions()
{
Title = LocalizationService.GetString("InstallGame", item.Name),
Maximizable = false,
DefaultMaximized = false,
Closable = true,
OkText = LocalizationService.GetString("Install"),
Draggable = true,
Centered = true,
WrapClassName = "ant-modal-wrap-no-padding",
Footer = null,
};
var modalRef = ModalService.CreateModal<InstallDialog, Models.ListItem, string>(modalOptions, item);
}
else
{
await InstallService.Add(game);
}
}
async Task Update(Models.ListItem item)
{
var game = item.DataItem as Game;
await InstallService.Add(game);
}
async Task OnQueueChanged()
{
var queueItem = InstallService.Queue.FirstOrDefault(i => SelectedItem != null && i.Id == SelectedItem.Key);
if (queueItem != null)
{
SelectedItem = await LibraryService.GetItemAsync(SelectedItem);
switch (queueItem.Status)
{
case SDK.Enums.InstallStatus.Downloading:
case SDK.Enums.InstallStatus.InstallingRedistributables:
case SDK.Enums.InstallStatus.InstallingMods:
case SDK.Enums.InstallStatus.InstallingExpansions:
case SDK.Enums.InstallStatus.RunningScripts:
case SDK.Enums.InstallStatus.DownloadingSaves:
SelectedItem.State = ListItemState.Installing;
break;
case SDK.Enums.InstallStatus.Queued:
SelectedItem.State = ListItemState.Queued;
break;
case SDK.Enums.InstallStatus.Failed:
case SDK.Enums.InstallStatus.Canceled:
SelectedItem.State = ListItemState.NotInstalled;
break;
case SDK.Enums.InstallStatus.Complete:
SelectedItem.State = ListItemState.Installed;
break;
}
await OnLibraryItemSelected(SelectedItem);
await InvokeAsync(StateHasChanged);
}
}
async Task OnInstallComplete(Data.Models.Game game)
{
await LoadData();
if (SelectedItem.DataItem is Game selectedGame)
{
if (selectedGame.Id == game.Id)
await OnLibraryItemSelected(LibraryService.Items.FirstOrDefault(i => i.Key == selectedGame.Id));
}
}
async Task OnInstallFail(Data.Models.Game game)
{
var result = await ConfirmService.Show(
LocalizationService.GetString("InstallationFailedMessage", game.Title),
LocalizationService.GetString("InstallationFailed"),
ConfirmButtons.RetryCancel,
ConfirmIcon.Error
);
await LibraryService.LibraryChanged();
if (result == ConfirmResult.Retry)
{
var libraryItem = LibraryService.GetItem(game.Id);
Install(libraryItem);
}
}
async Task OnUninstall(Data.Models.Game game)
{
if (SelectedItem.Key == game.Id)
{
SelectedItem.State = ListItemState.Uninstalling;
await Task.Yield();
await InvokeAsync(StateHasChanged);
}
}
async Task OnUninstallComplete(Data.Models.Game game)
{
await LoadData();
if (SelectedItem.Key == game.Id)
{
SelectedItem.State = ListItemState.NotInstalled;
await OnLibraryItemSelected(LibraryService.Items.FirstOrDefault(i => i.Key == SelectedGame.Id));
}
}
long GetDownloadSize()
{
long size = 0;
if (RemoteGame != null && RemoteGame.Archives.Any())
size = RemoteGame.Archives.OrderByDescending(a => a.CreatedOn).First().CompressedSize;
return size;
}
string GetPlayTime(Data.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 LocalizationService.GetString("None");
else if (totalTime.TotalHours < 1)
return LocalizationService.GetString("PlayTimeMinutes", totalTime.TotalMinutes.ToString("0"));
else
return LocalizationService.GetString("PlayTimeHours", totalTime.TotalHours.ToString("0.##"));
}
string GetLastPlayed(Data.Models.Game game)
{
var lastSession = game.PlaySessions.Where(ps => ps.End != null && ps.Start != null).OrderByDescending(ps => ps.End).FirstOrDefault();
if (lastSession == null)
return LocalizationService.GetString("Never");
else
return lastSession.End.Value.ToRelativeDate();
}
public void Dispose()
{
InstallService.OnQueueChanged -= OnQueueChanged;
InstallService.OnInstallFail -= OnInstallFail;
InstallService.OnInstallComplete -= OnInstallComplete;
GameService.OnUninstall -= OnUninstall;
GameService.OnUninstallComplete -= OnUninstallComplete;
}
}