@using AntDesign.TableModels @using LANCommander.Server.ImportExport.Factories @using LANCommander.Server.Services.Providers.Metadata @inject MetadataService MetadataService @inject GameService GameService @inject ImportContextFactory ImportContextFactory @inject IMessageService MessageService @inject ILogger Logger @if (_stage == ModalStage.Search) { @if (_errorMessage != null) { } else if (_hasSearched) { @(context.Data.ReleasedOn != default ? context.Data.ReleasedOn.ToString("MM/dd/yyyy") : "—") @string.Join(", ", context.Data.Platforms?.Select(p => p.Name) ?? []) @string.Join(", ", context.Data.Developers?.Select(d => d.Name) ?? [])
@if (_results?.More ?? false) { } } @foreach (var sp in _availableSubProviders) { } } @if (ShowCancel) { } @ChildContent } else if (_stage == ModalStage.Merge) { @if (_errorMessage != null) { } else {
} @if (ShowCancel) { } }
@code { private enum ModalStage { Search, Merge } [Parameter] public Guid GameId { get; set; } [Parameter] public EventCallback OnImported { get; set; } [Parameter] public EventCallback OnCancel { get; set; } [Parameter] public bool AutoFocus { get; set; } [Parameter] public bool ShowCancel { get; set; } = true; [Parameter] public string Search { get; set; } = string.Empty; [Parameter] public EventCallback SearchChanged { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } ITable? ResultsTable; MetadataSearchResultsCollection? _results; IEnumerable> SelectedResults { get; set; } bool _loading = false; bool _hasSearched = false; int _limit = 10; int _offset = 0; string _selectedProvider = string.Empty; IEnumerable _availableProviders = []; string? _selectedSubProvider; IEnumerable<(string Slug, string Name)>? _availableSubProviders; ModalStage _stage = ModalStage.Search; string? _errorMessage = null; LANCommander.SDK.Models.Manifest.Game? _fetchedManifest = null; bool _importing = false; string _importStatus = "Importing..."; bool _importTitle = true; bool _importDescription = true; bool _importReleasedOn = true; bool _importSingleplayer = true; bool _importEngine = true; bool _importDevelopers = true; bool _importPublishers = true; bool _importGenres = true; bool _importTags = true; bool _importMultiplayerModes = true; bool _importSavePaths = true; bool _importExternalIds = true; protected override async Task OnInitializedAsync() { _availableProviders = MetadataService.GetProviderNames(); _selectedProvider = _availableProviders.FirstOrDefault() ?? string.Empty; await UpdateSubProvidersAsync(); } private async Task OnProviderChanged(string provider) { _selectedProvider = provider; _errorMessage = null; await UpdateSubProvidersAsync(); if (!string.IsNullOrWhiteSpace(Search)) await SearchForGame(Search); } private async Task OnSubProviderChanged(string subProvider) { _selectedSubProvider = subProvider; _errorMessage = null; if (!string.IsNullOrWhiteSpace(Search)) await SearchForGame(Search); } private async Task UpdateSubProvidersAsync() { var provider = MetadataService.GetProvider(_selectedProvider); if (provider is not null) { _availableSubProviders = await provider.GetSubProvidersAsync(); _selectedSubProvider = _availableSubProviders?.FirstOrDefault().Slug; } else { _availableSubProviders = null; _selectedSubProvider = null; } } private async Task OnRowClicked(RowData> row) { ResultsTable?.SetSelection([row.Data.Id]); await BeginMerge(); } private async Task SearchFieldOnKeyDown(KeyboardEventArgs e) { if ((e.Code == "Enter" || e.Code == "NumpadEnter") && !String.IsNullOrWhiteSpace(Search)) await SearchForGame(Search); } private async Task SearchForGame(string title, bool newSearch = true) { var provider = MetadataService.GetProvider(_selectedProvider); if (provider is null) { MessageService.Error($"Provider \"{_selectedProvider}\" is not available"); return; } try { _loading = true; _hasSearched = true; _errorMessage = null; if (newSearch) { _results = null; _offset = 0; } _results = _availableSubProviders?.Any() == true ? await provider.SearchGamesAsync(title, _selectedSubProvider, _limit, _offset) : await provider.SearchGamesAsync(title, _limit, _offset); _loading = false; } catch (Exception ex) { Logger.LogError(ex, "An error occurred searching {Provider}", _selectedProvider); _loading = false; _errorMessage = ex.Message; } } private async Task BeginMerge() { var provider = MetadataService.GetProvider(_selectedProvider); if (provider is null) return; _loading = true; _errorMessage = null; try { var selected = SelectedResults.First(); var manifest = await provider.GetGameAsync(selected.Id); if (manifest is null) { _errorMessage = "Could not retrieve game metadata from the provider."; _loading = false; return; } _fetchedManifest = manifest; _importTitle = true; _importDescription = !string.IsNullOrWhiteSpace(manifest.Description); _importReleasedOn = manifest.ReleasedOn != default; _importSingleplayer = true; _importEngine = manifest.Engine != null; _importDevelopers = manifest.Developers?.Any() == true; _importPublishers = manifest.Publishers?.Any() == true; _importGenres = manifest.Genres?.Any() == true; _importTags = manifest.Tags?.Any() == true; _importMultiplayerModes = manifest.MultiplayerModes?.Any() == true; _importSavePaths = manifest.SavePaths?.Any() == true; _importExternalIds = manifest.ExternalIds?.Any() == true; _stage = ModalStage.Merge; } catch (Exception ex) { Logger.LogError(ex, "An error occurred fetching metadata from {Provider}", _selectedProvider); _errorMessage = ex.Message; } finally { _loading = false; } } private async Task ImportWithMerge() { if (_fetchedManifest is null) return; _loading = true; _importing = true; _importStatus = "Importing..."; _errorMessage = null; try { LANCommander.SDK.Models.Manifest.Game? existing = null; if (GameId != Guid.Empty) existing = await GameService.GetManifestAsync(GameId); var manifest = _fetchedManifest; manifest.Id = GameId == Guid.Empty ? Guid.NewGuid() : GameId; if (!_importTitle && existing != null) manifest.Title = existing.Title; if (!_importDescription && existing != null) manifest.Description = existing.Description; if (!_importReleasedOn && existing != null) manifest.ReleasedOn = existing.ReleasedOn; if (!_importSingleplayer && existing != null) manifest.Singleplayer = existing.Singleplayer; if (!_importEngine) manifest.Engine = existing?.Engine; if (!_importDevelopers) manifest.Developers = existing?.Developers ?? new List(); if (!_importPublishers) manifest.Publishers = existing?.Publishers ?? new List(); if (!_importGenres) manifest.Genres = existing?.Genres ?? new List(); if (!_importTags) manifest.Tags = existing?.Tags ?? new List(); if (!_importMultiplayerModes) manifest.MultiplayerModes = existing?.MultiplayerModes ?? new List(); if (!_importSavePaths) manifest.SavePaths = existing?.SavePaths ?? new List(); if (!_importExternalIds) manifest.ExternalIds = existing?.ExternalIds ?? new List(); using var context = ImportContextFactory.Create(); await context.InitializeMetadataUpdateAsync(manifest); await context.ImportQueueAsync(); _loading = false; await OnImported.InvokeAsync(manifest.Id); _importing = false; Reset(); } catch (Exception ex) { Logger.LogError(ex, "An error occurred importing metadata from {Provider}", _selectedProvider); _errorMessage = ex.Message; _loading = false; _importing = false; } } public async Task UpdateStatusAsync(string status) { _importStatus = status; await InvokeAsync(StateHasChanged); } private void BackToSearch() { _stage = ModalStage.Search; _errorMessage = null; _fetchedManifest = null; SelectedResults = []; } private void DismissError() { _errorMessage = null; } private void Reset() { _results = null; _limit = 10; _offset = 0; _hasSearched = false; _stage = ModalStage.Search; _errorMessage = null; _fetchedManifest = null; } private async Task HandleCancel() { Reset(); await OnCancel.InvokeAsync(); } private async Task ShowMore() { _offset += _results?.Limit ?? _limit; await SearchForGame(Search, false); } }