From efda7bf03d0874b98a25f1f10fdfffaab9c8d6ca Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Sat, 4 Feb 2023 12:18:10 -0600 Subject: [PATCH] Partial implementation of game edit page using Blazor components and Razor Pages --- LANCommander/App.razor | 10 + .../Areas/Identity/Pages/_ViewImports.cshtml | 1 + .../Components/GameMetadataLookup.razor | 198 ++++++++++++++++++ LANCommander/Components/NavMenu.razor | 4 + LANCommander/Helpers/RazorHelpers.cs | 18 ++ LANCommander/LANCommander.csproj | 2 + LANCommander/Pages/Games/Browse.razor | 23 ++ LANCommander/Pages/Games/Edit.razor | 103 +++++++++ LANCommander/Pages/Games/Index.razor | 51 +++++ LANCommander/Pages/_Host.cshtml | 8 + LANCommander/Pages/_Layout.cshtml | 25 +++ LANCommander/Program.cs | 21 +- LANCommander/Shared/MainLayout.razor | 31 +++ LANCommander/_Imports.razor | 11 + 14 files changed, 495 insertions(+), 11 deletions(-) create mode 100644 LANCommander/App.razor create mode 100644 LANCommander/Components/GameMetadataLookup.razor create mode 100644 LANCommander/Components/NavMenu.razor create mode 100644 LANCommander/Helpers/RazorHelpers.cs create mode 100644 LANCommander/Pages/Games/Browse.razor create mode 100644 LANCommander/Pages/Games/Edit.razor create mode 100644 LANCommander/Pages/Games/Index.razor create mode 100644 LANCommander/Pages/_Host.cshtml create mode 100644 LANCommander/Pages/_Layout.cshtml create mode 100644 LANCommander/Shared/MainLayout.razor create mode 100644 LANCommander/_Imports.razor diff --git a/LANCommander/App.razor b/LANCommander/App.razor new file mode 100644 index 00000000..c5ac499c --- /dev/null +++ b/LANCommander/App.razor @@ -0,0 +1,10 @@ + + + + + + +

Sorry, there's nothing at this address.

+
+
+
\ No newline at end of file diff --git a/LANCommander/Areas/Identity/Pages/_ViewImports.cshtml b/LANCommander/Areas/Identity/Pages/_ViewImports.cshtml index cb76ee19..011f9f6b 100644 --- a/LANCommander/Areas/Identity/Pages/_ViewImports.cshtml +++ b/LANCommander/Areas/Identity/Pages/_ViewImports.cshtml @@ -3,3 +3,4 @@ @using LANCommander.Areas.Identity.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @using LANCommander.Data.Models +@using LANCommander.Components \ No newline at end of file diff --git a/LANCommander/Components/GameMetadataLookup.razor b/LANCommander/Components/GameMetadataLookup.razor new file mode 100644 index 00000000..4a6f7c3b --- /dev/null +++ b/LANCommander/Components/GameMetadataLookup.razor @@ -0,0 +1,198 @@ +@using LANCommander.Data.Enums +@using LANCommander.Models +@using LANCommander.PCGamingWiki +@inject IGDBService IGDBService +@inject CompanyService CompanyService +@inject GenreService GenreService +@inject TagService TagService +@inject ISnackbar Snackbar + + + + + + Results for @GameTitle + + + + @if (Results == null) + { + + } + else + { + + + Title + Released + Developers + + + + + @context.Title + @context.ReleasedOn?.ToString("MM/dd/yyyy") + @String.Join(", ", context.Developers?.Select(d => d.Name)) + + Select + + + + } + + + Cancel + + + +@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } + + [Parameter] public string GameTitle { get; set; } + + private IEnumerable Results { get; set; } + private PCGamingWikiClient PCGamingWikiClient { get; set; } + + protected override async Task OnInitializedAsync() + { + PCGamingWikiClient = new PCGamingWikiClient(); + + await SearchForGame(GameTitle); + } + + private void Cancel() + { + MudDialog.Cancel(); + } + + public async Task SearchForGame(string title) + { + var results = await IGDBService.Search(GameTitle, "involved_companies.*", "involved_companies.company.*"); + + if (results == null) + Results = new List(); + else + { + Results = results.Select(r => + { + var result = new Game() + { + IGDBId = r.Id.GetValueOrDefault(), + Title = r.Name, + ReleasedOn = r.FirstReleaseDate.GetValueOrDefault().UtcDateTime, + Developers = new List() + }; + + if (r.InvolvedCompanies != null && r.InvolvedCompanies.Values != null) + { + result.Developers = r.InvolvedCompanies.Values.Where(c => c.Developer.HasValue && c.Developer.GetValueOrDefault() && c.Company != null && c.Company.Value != null).Select(c => new Company() + { + Name = c.Company.Value.Name + }).ToList(); + } + + return result; + }); + } + } + + private async Task SelectGame(Game game) + { + Results = null; + + var result = await IGDBService.Get(game.IGDBId.GetValueOrDefault(), "genres.*", "game_modes.*", "multiplayer_modes.*", "release_dates.*", "platforms.*", "keywords.*", "involved_companies.*", "involved_companies.company.*", "cover.*"); + + game.Title = result.Name; + game.Description = result.Summary; + game.ReleasedOn = result.FirstReleaseDate.GetValueOrDefault().UtcDateTime; + game.MultiplayerModes = await GetMultiplayerModes(result.Name); + game.Developers = new List(); + game.Publishers = new List(); + game.Genres = new List(); + game.Tags = new List(); + + if (result.GameModes != null && result.GameModes.Values != null) + game.Singleplayer = result.GameModes.Values.Any(gm => gm.Name == "Singleplayer"); + + if (result.InvolvedCompanies != null && result.InvolvedCompanies.Values != null) + { + // Make sure companie + var developers = result.InvolvedCompanies.Values.Where(c => c.Developer.GetValueOrDefault()).Select(c => c.Company.Value.Name); + var publishers = result.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.Genres != null && result.Genres.Values != null) + { + var genres = result.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.Keywords != null && result.Keywords.Values != null) + { + var tags = result.Keywords.Values.Select(t => t.Name).Take(20); + + foreach (var tag in tags) + { + game.Tags.Add(await TagService.AddMissing(t => t.Name == tag, new Tag { Name = tag })); + } + } + + MudDialog.Close(DialogResult.Ok(game)); + } + + private async Task> GetMultiplayerModes(string gameTitle) + { + var multiplayerModes = new List(); + + var playerCounts = await PCGamingWikiClient.GetMultiplayerPlayerCounts(gameTitle); + + if (playerCounts != null) + { + foreach (var playerCount in playerCounts) + { + MultiplayerType type; + + switch (playerCount.Key) + { + case "Local Play": + type = MultiplayerType.Local; + break; + + case "LAN Play": + type = MultiplayerType.Lan; + break; + + case "Online Play": + type = MultiplayerType.Online; + break; + + default: + continue; + } + + multiplayerModes.Add(new MultiplayerMode() + { + Type = type, + MaxPlayers = playerCount.Value, + MinPlayers = 2 + }); + } + } + + return multiplayerModes; + } +} \ No newline at end of file diff --git a/LANCommander/Components/NavMenu.razor b/LANCommander/Components/NavMenu.razor new file mode 100644 index 00000000..82a1ea08 --- /dev/null +++ b/LANCommander/Components/NavMenu.razor @@ -0,0 +1,4 @@ + + Dashboard + Games + \ No newline at end of file diff --git a/LANCommander/Helpers/RazorHelpers.cs b/LANCommander/Helpers/RazorHelpers.cs new file mode 100644 index 00000000..090652b5 --- /dev/null +++ b/LANCommander/Helpers/RazorHelpers.cs @@ -0,0 +1,18 @@ +using Castle.DynamicProxy.Generators.Emitters.SimpleAST; +using System.ComponentModel.DataAnnotations; +using System.Linq.Expressions; +using System.Reflection; + +namespace LANCommander.Helpers +{ + public static class DisplayName + { + public static string For(Expression> accessor) + { + var expression = (MemberExpression)accessor.Body; + var value = expression.Member.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute; + + return value?.Name ?? expression.Member.Name; + } + } +} diff --git a/LANCommander/LANCommander.csproj b/LANCommander/LANCommander.csproj index 5ec6ca2d..2dcae152 100644 --- a/LANCommander/LANCommander.csproj +++ b/LANCommander/LANCommander.csproj @@ -38,12 +38,14 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/LANCommander/Pages/Games/Browse.razor b/LANCommander/Pages/Games/Browse.razor new file mode 100644 index 00000000..cbeea7a1 --- /dev/null +++ b/LANCommander/Pages/Games/Browse.razor @@ -0,0 +1,23 @@ +@page "/Games/{id:guid}/Browse" +@inject GameService GameService + + + + +
+
+ +@code { + [Parameter] public Guid Id { get; set; } + + private Game Game { get; set; } + private Archive Archive { get; set; } + + protected override async Task OnInitializedAsync() + { + Game = await GameService.Get(Id); + + if (Game.Archives != null && Game.Archives.Count > 0) + Archive = Game.Archives.OrderByDescending(a => a.CreatedOn).First(); + } +} diff --git a/LANCommander/Pages/Games/Edit.razor b/LANCommander/Pages/Games/Edit.razor new file mode 100644 index 00000000..ea138e77 --- /dev/null +++ b/LANCommander/Pages/Games/Edit.razor @@ -0,0 +1,103 @@ +@page "/Games/{id:guid}/Edit" +@inject GameService GameService +@inject IDialogService DialogService + + + + + + + + + + + + + + @foreach (var developer in Game.Developers) + { + @developer.Name + } + + + + @foreach (var publisher in Game.Publishers) + { + @publisher.Name + } + + + + @foreach (var genre in Game.Genres) + { + @genre.Name + } + + + + @foreach (var tags in Game.Tags) + { + @tags.Name + } + + +
+ Save +
+
+
+
+
+ +@code { + [Parameter] public Guid Id { get; set; } + + bool Success; + string[] Errors = { }; + MudForm Form; + + private Game Game { get; set; } + + protected override async Task OnInitializedAsync() + { + Game = await GameService.Get(Id); + } + + private void Save() + { + + } + + private void BrowseForIcon() + { + + } + + private async void LookupGameMetadata() + { + var parameters = new DialogParameters + { + ["GameTitle"] = Game.Title + }; + + var dialog = await DialogService.ShowAsync("Game Lookup", parameters); + var result = await dialog.Result; + + if (!result.Canceled) + { + var info = result.Data as Game; + + Game.Title = info.Title; + Game.Description = info.Description; + Game.ReleasedOn = info.ReleasedOn; + Game.MultiplayerModes = info.MultiplayerModes; + Game.Developers = info.Developers; + Game.Publishers = info.Publishers; + Game.Genres = info.Genres; + Game.Tags = info.Tags; + Game.Singleplayer = info.Singleplayer; + + StateHasChanged(); + } + } +} diff --git a/LANCommander/Pages/Games/Index.razor b/LANCommander/Pages/Games/Index.razor new file mode 100644 index 00000000..e57f9006 --- /dev/null +++ b/LANCommander/Pages/Games/Index.razor @@ -0,0 +1,51 @@ +@page "/Games" +@inject GameService GameService + + + + Games + + + + + + + Title + Sort Title + Released + Created + Created By + Updated + Updated By + + + + + + + @context.Title + @context.SortTitle + @context.ReleasedOn?.ToString("MM/dd/yyyy") + @context.CreatedOn + @context.CreatedBy?.UserName + @context.UpdatedOn + @context.UpdatedBy?.UserName + + Edit + + + + + + + + +@code { + private ICollection Games { get; set; } + private string Search { get; set; } + + protected override async Task OnInitializedAsync() + { + Games = GameService.Get(); + } +} diff --git a/LANCommander/Pages/_Host.cshtml b/LANCommander/Pages/_Host.cshtml new file mode 100644 index 00000000..59fa018f --- /dev/null +++ b/LANCommander/Pages/_Host.cshtml @@ -0,0 +1,8 @@ +@page "/" +@namespace LANCommander.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@{ + Layout = "_Layout"; +} + + diff --git a/LANCommander/Pages/_Layout.cshtml b/LANCommander/Pages/_Layout.cshtml new file mode 100644 index 00000000..291ef7fe --- /dev/null +++ b/LANCommander/Pages/_Layout.cshtml @@ -0,0 +1,25 @@ +@using Microsoft.AspNetCore.Components.Web +@using LANCommander.Components +@namespace LANCommander.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + + + + + + + + + + + + +
+ @RenderBody() +
+ + + + + diff --git a/LANCommander/Program.cs b/LANCommander/Program.cs index 52dc235a..0b690025 100644 --- a/LANCommander/Program.cs +++ b/LANCommander/Program.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; +using MudBlazor.Services; using System.Text; var builder = WebApplication.CreateBuilder(args); @@ -17,6 +18,9 @@ ConfigurationManager configuration = builder.Configuration; var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); var settings = SettingService.GetSettings(); +builder.Services.AddRazorPages(); +builder.Services.AddServerSideBlazor(); + builder.WebHost.ConfigureKestrel(options => { // Configure as HTTP only @@ -65,7 +69,8 @@ builder.Services.AddControllersWithViews().AddJsonOptions(x => { x.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; }); -builder.Services.AddServerSideBlazor(); + +builder.Services.AddMudServices(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -81,6 +86,8 @@ builder.Services.AddScoped(); if (settings.Beacon) builder.Services.AddHostedService(); +builder.WebHost.UseStaticWebAssets(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -104,16 +111,8 @@ app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); - -app.UseEndpoints(endpoints => -{ - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - endpoints.MapBlazorHub(); -}); - -app.MapRazorPages(); +app.MapBlazorHub(); +app.MapFallbackToPage("/_Host"); if (!Directory.Exists("Upload")) Directory.CreateDirectory("Upload"); diff --git a/LANCommander/Shared/MainLayout.razor b/LANCommander/Shared/MainLayout.razor new file mode 100644 index 00000000..bdad1014 --- /dev/null +++ b/LANCommander/Shared/MainLayout.razor @@ -0,0 +1,31 @@ +@inherits LayoutComponentBase + + + + + + + + + LANCommander + + + + + + + + + @Body + + + + +@code { + bool _drawerOpen = true; + + void DrawerToggle() + { + _drawerOpen = !_drawerOpen; + } +} \ No newline at end of file diff --git a/LANCommander/_Imports.razor b/LANCommander/_Imports.razor new file mode 100644 index 00000000..ff420ec8 --- /dev/null +++ b/LANCommander/_Imports.razor @@ -0,0 +1,11 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop +@using MudBlazor +@using LANCommander.Components +@using LANCommander.Shared +@using LANCommander.Services +@using LANCommander.Data.Models \ No newline at end of file