LANCommander/LANCommander/Pages/Games/Edit.razor
Pat Hartl 2287ac9922 Refactored archive uploader to use binding instead of Game parameter
May fix issues where game metadata is wiped after uploading an archive.
2023-10-18 18:11:20 -05:00

322 lines
12 KiB
Text

@page "/Games/{id:guid}"
@page "/Games/{id:guid}/{panel}"
@page "/Games/Add"
@using LANCommander.Components.FileManagerComponents;
@using LANCommander.Models;
@using LANCommander.Pages.Games.Components
@using System.IO.Compression;
@attribute [Authorize(Roles = "Administrator")]
@inject GameService GameService
@inject CompanyService CompanyService
@inject GenreService GenreService
@inject TagService TagService
@inject ArchiveService ArchiveService
@inject ScriptService ScriptService
@inject IMessageService MessageService
@inject NavigationManager NavigationManager
@inject ModalService ModalService
<Layout Class="panel-layout" Style="padding: 24px 0;">
<Sider Width="200">
<Menu Mode="@MenuMode.Inline" Style="height: 100%;">
<MenuItem RouterLink="@($"/Games/{Game.Id}/General")">General</MenuItem>
@if (Game != null && Game.Id != Guid.Empty)
{
<MenuItem RouterLink="@($"/Games/{Game.Id}/Actions")">Actions</MenuItem>
<MenuItem RouterLink="@($"/Games/{Game.Id}/Multiplayer")">Multiplayer</MenuItem>
<MenuItem RouterLink="@($"/Games/{Game.Id}/SavePaths")">Save Paths</MenuItem>
<MenuItem RouterLink="@($"/Games/{Game.Id}/Keys")">Keys</MenuItem>
<MenuItem RouterLink="@($"/Games/{Game.Id}/Scripts")">Scripts</MenuItem>
<MenuItem RouterLink="@($"/Games/{Game.Id}/Archives")">Archives</MenuItem>
}
</Menu>
</Sider>
<Content>
<PageHeader>
<PageHeaderTitle>
@if (Panel == null)
{
<Text>Add New Game</Text>
}
else if (PanelDisplayNames.ContainsKey(Panel))
{
@PanelDisplayNames[Panel]
}
else
{
@Panel
}
</PageHeaderTitle>
<PageHeaderExtra>
<Button Type="@ButtonType.Primary" OnClick="Save">Save</Button>
</PageHeaderExtra>
</PageHeader>
<div class="panel-layout-content">
<div data-panel="General">
<Form Model="@Game" Layout="@FormLayout.Vertical">
<FormItem Label="Title">
<Space Style="display: flex">
<SpaceItem Style="flex-grow: 1">
<Input @bind-Value="@context.Title" BindOnInput="true" />
</SpaceItem>
<SpaceItem>
<GameMetadataLookup ButtonText="Lookup" GameTitle="@context.Title" OnResultSelected="OnGameLookupResultSelected" />
</SpaceItem>
</Space>
</FormItem>
<FormItem Label="Sort Title">
<Input @bind-Value="@context.SortTitle" />
</FormItem>
<FormItem Label="Icon">
<FilePicker @bind-Value="context.Icon" ArchiveId="@LatestArchiveId" AllowDirectories="true" />
</FormItem>
<FormItem Label="Notes">
<TextArea @bind-Value="@context.Notes" MaxLength=2000 ShowCount />
</FormItem>
<FormItem Label="Description">
<TextArea @bind-Value="@context.Description" MaxLength=500 ShowCount />
</FormItem>
<FormItem Label="Released On">
<DatePicker TValue="DateTime?" @bind-Value="@context.ReleasedOn" Picker="@DatePickerType.Date" />
</FormItem>
<FormItem Label="Singleplayer">
<Checkbox @bind-Checked="@context.Singleplayer" />
</FormItem>
<FormItem Label="Developers">
<TagsInput Entities="Companies" @bind-Values="Game.Developers" OptionLabelSelector="c => c.Name" TItem="Company" />
</FormItem>
<FormItem Label="Publishers">
<TagsInput Entities="Companies" @bind-Values="Game.Publishers" OptionLabelSelector="c => c.Name" TItem="Company" />
</FormItem>
<FormItem Label="Genres">
<TagsInput Entities="Genres" @bind-Values="Game.Genres" OptionLabelSelector="c => c.Name" TItem="Genre" />
</FormItem>
<FormItem Label="Tags">
<TagsInput Entities="Tags" @bind-Values="Game.Tags" OptionLabelSelector="c => c.Name" TItem="Data.Models.Tag" />
</FormItem>
</Form>
</div>
@if (Game != null && Game.Id != Guid.Empty)
{
<div data-panel="Actions">
<ActionEditor @bind-Actions="Game.Actions" GameId="Game.Id" ArchiveId="@LatestArchiveId" />
</div>
<div data-panel="Multiplayer">
<MultiplayerModeEditor @bind-Value="Game.MultiplayerModes" />
</div>
<div data-panel="SavePaths">
<SavePathEditor @bind-Value="Game.SavePaths" GameId="Game.Id" ArchiveId="@LatestArchiveId" />
</div>
<div data-panel="Keys">
<KeysEditor @ref="KeysEditor" @bind-Keys="Game.Keys" GameId="Game.Id" />
<Button OnClick="() => KeysEditor.Edit()">Edit</Button>
</div>
<div data-panel="Scripts">
<ScriptEditor @bind-Scripts="Game.Scripts" GameId="Game.Id" ArchiveId="@LatestArchiveId" />
</div>
<div data-panel="Archives">
<ArchiveEditor @bind-Archives="Game.Archives" GameId="Game.Id" />
</div>
}
</div>
</Content>
</Layout>
@if (!String.IsNullOrWhiteSpace(Panel))
{
<style>
.panel-layout [data-panel="@Panel"] {
display: block;
}
</style>
}
else
{
<style>
.panel-layout [data-panel="General"] {
display: block;
}
</style>
}
@code {
[Parameter] public Guid Id { get; set; }
[Parameter] public string Panel { get; set; }
bool Success;
string[] Errors = { };
IEnumerable<Company> Companies;
IEnumerable<Genre> Genres;
IEnumerable<Data.Models.Tag> Tags;
FilePickerDialog ArchiveFilePickerDialog;
Modal FileSelectorModal;
private string value = "blazor";
private Game Game;
private KeysEditor? KeysEditor;
private GameMetadataLookup? GameMetadataLookup;
private Dictionary<string, string> PanelDisplayNames = new Dictionary<string, string>
{
{ "SavePaths", "Save Paths" }
};
private Guid LatestArchiveId
{
get
{
if (Game != null && Game.Archives != null && Game.Archives.Count > 0)
return Game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Id;
else
return Guid.Empty;
}
}
private int KeysAvailable { get {
return Game.Keys.Count(k =>
{
return (k.AllocationMethod == KeyAllocationMethod.MacAddress && String.IsNullOrWhiteSpace(k.ClaimedByMacAddress))
||
(k.AllocationMethod == KeyAllocationMethod.UserAccount && k.ClaimedByUser == null);
});
} }
protected override async Task OnInitializedAsync()
{
if (Id == Guid.Empty)
Game = new Game();
else
Game = await GameService.Get(Id);
Companies = await CompanyService.Get();
Genres = await GenreService.Get();
Tags = await TagService.Get();
}
private async Task Save()
{
try
{
if (Game.Id != Guid.Empty)
{
Game = await GameService.Update(Game);
await MessageService.Success("Game updated!");
}
else
{
Game = await GameService.Add(Game);
NavigationManager.LocationChanged += NotifyGameAdded;
NavigationManager.NavigateTo($"/Games/{Game.Id}");
}
}
catch (Exception ex)
{
await MessageService.Error("Could not save!");
}
}
private void NotifyGameAdded(object? sender, LocationChangedEventArgs e)
{
NavigationManager.LocationChanged -= NotifyGameAdded;
MessageService.Success("Game added!");
}
private async Task BrowseForIcon()
{
var modalOptions = new ModalOptions()
{
Title = "Choose Icon",
Maximizable = false,
DefaultMaximized = true,
Closable = true,
OkText = "Select File"
};
var browserOptions = new FilePickerOptions()
{
ArchiveId = Game.Archives.FirstOrDefault().Id,
Select = true,
Multiple = false
};
var modalRef = await ModalService.CreateModalAsync<FilePickerDialog, FilePickerOptions, IEnumerable<IFileManagerEntry>>(modalOptions, browserOptions);
modalRef.OnOk = (results) =>
{
Game.Icon = results.FirstOrDefault().Path;
StateHasChanged();
return Task.CompletedTask;
};
}
private async Task OnGameLookupResultSelected(GameLookupResult result)
{
Game.Title = result.IGDBMetadata.Name;
Game.Description = result.IGDBMetadata.Summary;
Game.ReleasedOn = result.IGDBMetadata.FirstReleaseDate.GetValueOrDefault().UtcDateTime;
Game.MultiplayerModes = result.MultiplayerModes.ToList();
Game.Developers = new List<Company>();
Game.Publishers = new List<Company>();
Game.Genres = new List<Genre>();
Game.Tags = new List<Data.Models.Tag>();
if (result.IGDBMetadata.GameModes != null && result.IGDBMetadata.GameModes.Values != null)
Game.Singleplayer = result.IGDBMetadata.GameModes.Values.Any(gm => gm.Name == "Singleplayer");
if (result.IGDBMetadata.InvolvedCompanies != null && result.IGDBMetadata.InvolvedCompanies.Values != null)
{
// Make sure companie
var developers = result.IGDBMetadata.InvolvedCompanies.Values.Where(c => c.Developer.GetValueOrDefault()).Select(c => c.Company.Value.Name);
var publishers = result.IGDBMetadata.InvolvedCompanies.Values.Where(c => c.Publisher.GetValueOrDefault()).Select(c => c.Company.Value.Name);
foreach (var developer in developers)
{
Game.Developers.Add(await CompanyService.AddMissing(c => c.Name == developer, new Company { Name = developer }));
}
foreach (var publisher in publishers)
{
Game.Publishers.Add(await CompanyService.AddMissing(c => c.Name == publisher, new Company { Name = publisher }));
}
}
if (result.IGDBMetadata.Genres != null && result.IGDBMetadata.Genres.Values != null)
{
var genres = result.IGDBMetadata.Genres.Values.Select(g => g.Name);
foreach (var genre in genres)
{
Game.Genres.Add(await GenreService.AddMissing(g => g.Name == genre, new Genre { Name = genre }));
}
}
if (result.IGDBMetadata.Keywords != null && result.IGDBMetadata.Keywords.Values != null)
{
var tags = result.IGDBMetadata.Keywords.Values.Select(t => t.Name).Take(20);
foreach (var tag in tags)
{
Game.Tags.Add(await TagService.AddMissing(t => t.Name == tag, new Data.Models.Tag { Name = tag }));
}
}
}
}