1
0
mirror of synced 2024-11-27 23:50:49 +01:00

Update WebUI

This commit is contained in:
asesidaa 2023-02-21 22:45:51 +08:00
parent 8900cca29e
commit 4d0e351abd
28 changed files with 342 additions and 134 deletions

View File

@ -1,23 +1,31 @@
namespace Application.Game.Card.Management; using Application.Common.Helpers;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Management;
public record CardRegisterCommand(long CardId, string Data) : IRequestWrapper<string>; public record CardRegisterCommand(long CardId, string Data) : IRequestWrapper<string>;
public class RegisterCommandHandler : RequestHandlerBase<CardRegisterCommand, string> public class RegisterCommandHandler : RequestHandlerBase<CardRegisterCommand, string>
{ {
public RegisterCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) private readonly ILogger<RegisterCommandHandler> logger;
public RegisterCommandHandler(ICardDependencyAggregate aggregate, ILogger<RegisterCommandHandler> logger) : base(aggregate)
{ {
this.logger = logger;
} }
public override async Task<ServiceResult<string>> Handle(CardRegisterCommand request, CancellationToken cancellationToken) public override async Task<ServiceResult<string>> Handle(CardRegisterCommand request, CancellationToken cancellationToken)
{ {
var exists = CardDbContext.CardMains.Any(card => card.CardId == request.CardId); var exists = CardDbContext.CardMains.Any(card => card.CardId == request.CardId);
if (!exists) if (exists)
{ {
return ServiceResult.Failed<string>(ServiceError.CustomMessage($"Card {request.CardId} already exists!")); return ServiceResult.Failed<string>(ServiceError.CustomMessage($"Card {request.CardId} already exists!"));
} }
var card = request.Data.DeserializeCardData<CardDto>().CardDtoToCardMain(); var card = request.Data.DeserializeCardData<CardDto>().CardDtoToCardMain();
card.CardId = request.CardId; card.CardId = request.CardId;
card.Created = TimeHelper.CurrentTimeToString();
card.Modified = card.Created;
logger.LogInformation("New card {{Id: {Id}, Player Name: {Name}}} registered", card.CardId, card.PlayerName);
CardDbContext.CardMains.Add(card); CardDbContext.CardMains.Add(card);
await CardDbContext.SaveChangesAsync(cancellationToken); await CardDbContext.SaveChangesAsync(cancellationToken);

View File

@ -18,7 +18,7 @@ public class ReadQueryHandler : RequestHandlerBase<ReadCardQuery, string>
var card = await CardDbContext.CardMains.FirstOrDefaultAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken); var card = await CardDbContext.CardMains.FirstOrDefaultAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (card is null) if (card is null)
{ {
logger.LogInformation("Card with {CardId} does not exist! Registering a new one...", request.CardId); logger.LogInformation("Card of id: {CardId} does not exist! Registering a new one...", request.CardId);
return ServiceResult.Failed<string>(new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered)); return ServiceResult.Failed<string>(new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
} }

View File

@ -26,6 +26,7 @@ public class WriteCommandHandler : RequestHandlerBase<WriteCardCommand, string>
logger.LogInformation("Creating new card {CardId}", request.CardId); logger.LogInformation("Creating new card {CardId}", request.CardId);
card = dto.CardDtoToCardMain(); card = dto.CardDtoToCardMain();
card.Created = TimeHelper.CurrentTimeToString(); card.Created = TimeHelper.CurrentTimeToString();
card.Modified = TimeHelper.CurrentTimeToString();
CardDbContext.CardMains.Add(card); CardDbContext.CardMains.Add(card);
} }
else else

View File

@ -17,7 +17,7 @@ public class GetDataQueryHandler : IRequestHandler<GetDataQuery, string>
public Task<string> Handle(GetDataQuery request, CancellationToken cancellationToken) public Task<string> Handle(GetDataQuery request, CancellationToken cancellationToken)
{ {
var response = "count=0\n" + var response = "count=0\n" +
"nexttime=0\n"; "nexttime=180\n";
if (!eventManagerService.UseEvents()) if (!eventManagerService.UseEvents())
{ {
return Task.FromResult(response); return Task.FromResult(response);
@ -38,7 +38,7 @@ public class GetDataQueryHandler : IRequestHandler<GetDataQuery, string>
} }
response = $"count={count}\n" + response = $"count={count}\n" +
"nexttime=1\n" + "nexttime=180\n" +
$"{dataString}"; $"{dataString}";
return Task.FromResult(response); return Task.FromResult(response);

View File

@ -16,7 +16,5 @@ public class GameConfig
public int TitleCount { get; set; } public int TitleCount { get; set; }
public List<int> UnlockableSongIds { get; set; } = new();
public List<UnlockRewardConfig> UnlockRewards { get; set; } = new(); public List<UnlockRewardConfig> UnlockRewards { get; set; } = new();
} }

View File

@ -6,4 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta06" PrivateAssets="all" ExcludeAssets="runtime"/>
</ItemGroup>
</Project> </Project>

View File

@ -23,5 +23,5 @@ public enum CardReturnCode
/// <summary> /// <summary>
/// Server side validation error /// Server side validation error
/// </summary> /// </summary>
Unknown = -1 Unknown = 9999
} }

View File

@ -1,8 +1,20 @@
namespace Domain.Enums; using System.ComponentModel.DataAnnotations;
using NetEscapades.EnumGenerators;
namespace Domain.Enums;
[EnumExtensions]
public enum ShowFastSlowOption : long public enum ShowFastSlowOption : long
{ {
[Display(Name = "Default option")]
Default = 0,
[Display(Name = "Show fast/slow near avatars")]
NearAvatar = 1, NearAvatar = 1,
[Display(Name = "Show fast/show near judgement text")]
NearJudgement = 2, NearJudgement = 2,
[Display(Name = "Do not show fast/slow")]
NotShow = 3 NotShow = 3
} }

View File

@ -1,7 +1,17 @@
namespace Domain.Enums; using System.ComponentModel.DataAnnotations;
using NetEscapades.EnumGenerators;
namespace Domain.Enums;
[EnumExtensions]
public enum ShowFeverTranceOption : long public enum ShowFeverTranceOption : long
{ {
[Display(Name = "Default option")]
Default = 0,
[Display(Name = "Show fever/trance")]
Show = 1, Show = 1,
[Display(Name = "Do not show fever/trance")]
NotShow = 2 NotShow = 2
} }

View File

@ -1,4 +1,4 @@
{ {
"CardDbName": "card1.db3", "CardDbName": "card.db3",
"MusicDbName": "music471omni.db3" "MusicDbName": "music471omni.db3"
} }

View File

@ -6,13 +6,6 @@
"SkinCount": 21, "SkinCount": 21,
"SeCount": 26, "SeCount": 26,
"TitleCount": 5530, "TitleCount": 5530,
"UnlockableSongIds":
[
11, 13, 149, 273, 291, 320, 321, 371, 378, 384, 464, 471, 474, 475, 492,
494, 498, 520, 548, 551, 558, 561, 565, 570, 577, 583, 612, 615, 622,
632, 659, 666, 668, 670, 672, 676, 680, 682, 685, 686, 697, 700, 701,
711, 720, 749, 875, 876, 877
],
"UnlockRewards": [ "UnlockRewards": [
{ {
"RewardId" : 1, "RewardId" : 1,

View File

@ -9,14 +9,14 @@ namespace MainServer.Controllers.API;
public class PlayOptionController : BaseController<PlayOptionController> public class PlayOptionController : BaseController<PlayOptionController>
{ {
[HttpGet("{cardId:long}")] [HttpGet("{cardId:long}")]
public async Task<ActionResult<ServiceResult<PlayOptionData>>> GetPlayOptionById(long cardId) public async Task<ServiceResult<PlayOptionData>> GetPlayOptionById(long cardId)
{ {
var result = await Mediator.Send(new GetPlayOptionQuery(cardId)); var result = await Mediator.Send(new GetPlayOptionQuery(cardId));
return result; return result;
} }
[HttpPost] [HttpPost]
public async Task<ActionResult<ServiceResult<bool>>> SetPlayOption(PlayOptionData data) public async Task<ServiceResult<bool>> SetPlayOption(PlayOptionData data)
{ {
var result = await Mediator.Send(new SetPlayOptionCommand(data)); var result = await Mediator.Send(new SetPlayOptionCommand(data));
return result; return result;

View File

@ -10,28 +10,28 @@ namespace MainServer.Controllers.API;
public class ProfilesController : BaseController<ProfilesController> public class ProfilesController : BaseController<ProfilesController>
{ {
[HttpGet] [HttpGet]
public async Task<ActionResult<ServiceResult<List<ClientCardDto>>>> GetAllCards() public async Task<ServiceResult<List<ClientCardDto>>> GetAllCards()
{ {
var result = await Mediator.Send(new GetCardsQuery()); var result = await Mediator.Send(new GetCardsQuery());
return result; return result;
} }
[HttpGet("{cardId:long}")] [HttpGet("{cardId:long}")]
public async Task<ActionResult<ServiceResult<TotalResultData>>> GetCardTotalResultById(long cardId) public async Task<ServiceResult<TotalResultData>> GetCardTotalResultById(long cardId)
{ {
var result = await Mediator.Send(new GetTotalResultQuery(cardId)); var result = await Mediator.Send(new GetTotalResultQuery(cardId));
return result; return result;
} }
[HttpPost("Favorite")] [HttpPost("Favorite")]
public async Task<ActionResult<ServiceResult<bool>>> SetFavoriteMusic(MusicDetailDto detail) public async Task<ServiceResult<bool>> SetFavoriteMusic(MusicDetailDto detail)
{ {
var result = await Mediator.Send(new SetFavoriteMusicCommand(detail)); var result = await Mediator.Send(new SetFavoriteMusicCommand(detail));
return result; return result;
} }
[HttpPost("PlayerName")] [HttpPost("PlayerName")]
public async Task<ActionResult<ServiceResult<bool>>> SetPlayerName(ClientCardDto card) public async Task<ServiceResult<bool>> SetPlayerName(ClientCardDto card)
{ {
var result = await Mediator.Send(new SetPlayerNameCommand(card)); var result = await Mediator.Send(new SetPlayerNameCommand(card));
return result; return result;

View File

@ -47,6 +47,7 @@ public class ApiExceptionFilterService : ExceptionFilterAttribute
return; return;
} }
logger.LogError(context.Exception, "An unknown exception happens");
HandleUnknownException(context); HandleUnknownException(context);
} }

View File

@ -8,10 +8,10 @@ using Infrastructure.Persistence;
using MainServer.Filters; using MainServer.Filters;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using Serilog; using Serilog;
using Serilog.Extensions.Logging; using Serilog.Extensions.Logging;
using Throw; using Throw;
using Shared.SerializerContexts;
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.WriteTo.Console() .WriteTo.Console()
@ -57,7 +57,8 @@ try
}); });
builder.Services.AddControllers(options => builder.Services.AddControllers(options =>
options.Filters.Add<ApiExceptionFilterService>()); options.Filters.Add<ApiExceptionFilterService>())
.AddJsonOptions(options => options.JsonSerializerOptions.AddContext<SourceGenerationContext>());
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
@ -99,6 +100,7 @@ try
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
app.UseWebAssemblyDebugging();
} }
// Add content type for .cmp and .evt files as static files with unknown file extensions return 404 by default // Add content type for .cmp and .evt files as static files with unknown file extensions return 404 by default

View File

@ -12,7 +12,7 @@
"MainServer": { "MainServer": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5107", "applicationUrl": "http://localhost:5107",
"environmentVariables": { "environmentVariables": {

View File

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
using Shared.Dto.Api;
using Shared.Models;
namespace Shared.SerializerContexts;
[JsonSourceGenerationOptions(WriteIndented = true, GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(ServiceResult<List<ClientCardDto>>))]
[JsonSerializable(typeof(ServiceResult<PlayOptionData>))]
[JsonSerializable(typeof(ServiceResult<bool>))]
[JsonSerializable(typeof(ServiceResult<TotalResultData>))]
[JsonSerializable(typeof(ServiceResult<TotalResultData>))]
public partial class SourceGenerationContext : JsonSerializerContext
{
}

View File

@ -7,7 +7,7 @@
<MudLayout> <MudLayout>
<MudAppBar Elevation="0" Dense="true"> <MudAppBar Elevation="0" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" <MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start"
OnClick="@(_ => DrawerToggle())"/> OnClick="@DrawerToggle"/>
<MudSpacer/> <MudSpacer/>
<MudTooltip Duration="1000" Text="Powered by MudBlazor"> <MudTooltip Duration="1000" Text="Powered by MudBlazor">
<MudIconButton Icon="@Icons.Custom.Brands.MudBlazor" Color="Color.Inherit" <MudIconButton Icon="@Icons.Custom.Brands.MudBlazor" Color="Color.Inherit"
@ -34,27 +34,4 @@
@Body @Body
</MudContainer> </MudContainer>
</MudMainContent> </MudMainContent>
</MudLayout> </MudLayout>
@code {
bool drawerOpen = true;
public bool IsDarkMode { get; set; }
public MudThemeProvider MudThemeProvider { get; set; } = null!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
IsDarkMode = await MudThemeProvider.GetSystemPreference();
StateHasChanged();
}
}
void DrawerToggle()
{
drawerOpen = !drawerOpen;
}
}

View File

@ -0,0 +1,26 @@
using MudBlazor;
namespace WebUI.Common;
public partial class MainLayout
{
bool drawerOpen = true;
public bool IsDarkMode { get; set; }
public MudThemeProvider MudThemeProvider { get; set; } = null!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
IsDarkMode = await MudThemeProvider.GetSystemPreference();
StateHasChanged();
}
}
void DrawerToggle()
{
drawerOpen = !drawerOpen;
}
}

View File

@ -1,13 +0,0 @@
using System.Text.Json.Serialization;
using WebUI.Common.Models;
namespace WebUI.Common.SerializerContexts;
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(List<Avatar>))]
[JsonSerializable(typeof(List<Navigator>))]
[JsonSerializable(typeof(List<Title>))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

3
WebUI/GlobalUsings.cs Normal file
View File

@ -0,0 +1,3 @@
// Global using directives
global using MudBlazor;

View File

@ -1,73 +1,91 @@
@page "/Cards" @page "/Cards"
<PageTitle>Cards</PageTitle> <PageTitle>Cards</PageTitle>
<h1>Cards</h1>
@if (ErrorMessage != string.Empty)
{
<MudText Color="Color.Error" Typo="Typo.h3">@ErrorMessage</MudText>
return;
}
<MudGrid Class="my-8">
<MudContainer>
@if (ErrorMessage != string.Empty)
{
<MudText Color="Color.Error" Typo="Typo.h3">@ErrorMessage</MudText>
return;
}
@if (CardDtos is null) @if (CardDtos is null)
{ {
<MudGrid> @for (var i = 0; i < 3; i++)
@for (var i = 0; i < 5; i++) {
{ <MudItem xs="12" md="6" lg="4">
<MudItem> <MudCard Outlined="true">
<MudCard> <MudCardHeader>
<MudCardHeader> <CardHeaderContent>
<CardHeaderContent> <MudSkeleton Width="30%" Height="32px;" Class="mb-5"/>
<MudSkeleton SkeletonType="SkeletonType.Rectangle" Height="32px"/> </CardHeaderContent>
</CardHeaderContent> </MudCardHeader>
</MudCardHeader> <MudCardContent>
<MudCardContent> <MudSkeleton Width="60%"/>
<MudSkeleton Width="80px" Height="32px"/> <MudSkeleton Width="100%"/>
<MudSkeleton Width="147px" Height="28px"/> </MudCardContent>
</MudCardContent> <MudCardActions>
<MudCardActions> <MudStack Row="true" Style="width:100%" Spacing="4" Justify="Justify.FlexEnd">
<MudSkeleton SkeletonType="SkeletonType.Rectangle" Width="99px" Height="25px" Class="ml-2"/> <MudSkeleton Width="128px" Height="32px"/>
</MudCardActions> <MudSkeleton Width="148px" Height="32px"/>
</MudCard> </MudStack>
</MudItem> </MudCardActions>
} </MudCard>
</MudGrid> </MudItem>
}
return; return;
} }
@if (CardDtos.Count != 0) @if (CardDtos.Count != 0)
{ {
<MudGrid> @foreach (var card in CardDtos)
@foreach (var card in CardDtos) {
{ <MudItem xs="12" md="6" lg="4">
<MudItem> <MudCard>
<MudCard> <MudCardHeader>
<MudCardHeader> <CardHeaderContent>
<CardHeaderContent> <MudText Typo="Typo.h5">@card.PlayerName</MudText>
<MudText Typo="Typo.h5">@card.PlayerName</MudText> </CardHeaderContent>
</CardHeaderContent> <CardHeaderActions>
<CardHeaderActions> <MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Default"
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Default" OnClick="() => OnEditPlayerNameClicked(card)"/>
OnClick="() => OnEditPlayerNameClicked(card)"/> </CardHeaderActions>
</CardHeaderActions> </MudCardHeader>
</MudCardHeader> <MudCardContent>
<MudCardContent> <MudText Style="font-weight: bold">Card ID</MudText>
<MudText Style="font-weight: bold">Card ID</MudText> <MudText Style="font-family:monospace">@card.CardId</MudText>
<MudText>@card.CardId</MudText> </MudCardContent>
</MudCardContent> <MudCardActions>
<MudCardActions> <MudStack Row="true" Style="width:100%" Spacing="4" Justify="Justify.FlexEnd">
<MudButton Variant="Variant.Text" Color="Color.Primary"> <MudButton Href="@($"Cards/Option/{card.CardId}")"
Check detail Variant="Variant.Text"
StartIcon="@Icons.Material.Filled.DisplaySettings"
Color="Color.Primary">
Edit Options
</MudButton> </MudButton>
</MudCardActions> <MudMenu Dense="true"
</MudCard> Color="Color.Primary"
</MudItem> Label="View Play Data"
} StartIcon="@Icons.Material.Filled.FeaturedPlayList"
</MudGrid> EndIcon="@Icons.Material.Filled.KeyboardArrowDown"
FullWidth="true"
AnchorOrigin="Origin.BottomCenter"
TransformOrigin="Origin.TopCenter">
<MudMenuItem Href="@($"Cards/TotalResult/{card.CardId}")">Total Result</MudMenuItem>
<MudMenuItem Href="@($"Cards/Results/{card.CardId}")">Song Play Results</MudMenuItem>
</MudMenu>
</MudStack>
</MudCardActions>
</MudCard>
</MudItem>
}
return; return;
} }
<MudText Align="Align.Center" Typo="Typo.h3"> <MudItem xs="12">
No Data <MudText Align="Align.Center" Typo="Typo.h3">
</MudText> No Data
</MudContainer> </MudText>
</MudItem>
</MudGrid>

View File

@ -1,8 +1,10 @@
using System.Net.Http.Json; using System.Net.Http.Json;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using MudBlazor;
using Shared.Dto.Api; using Shared.Dto.Api;
using Shared.Models; using Shared.Models;
using Throw;
using Shared.SerializerContexts;
using WebUI.Pages.Dialogs;
namespace WebUI.Pages; namespace WebUI.Pages;
@ -14,6 +16,9 @@ public partial class Cards
[Inject] [Inject]
public required IDialogService DialogService { get; set; } public required IDialogService DialogService { get; set; }
[Inject]
public required ILogger<Cards> Logger { get; set; }
private List<ClientCardDto>? CardDtos { get; set; } private List<ClientCardDto>? CardDtos { get; set; }
private string ErrorMessage { get; set; } = string.Empty; private string ErrorMessage { get; set; } = string.Empty;
@ -21,12 +26,11 @@ public partial class Cards
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
await Task.Delay(3000);
var result = await Client.GetFromJsonAsync<ServiceResult<List<ClientCardDto>>>("api/Profiles"); var result = await Client.GetFromJsonAsync<ServiceResult<List<ClientCardDto>>>("api/Profiles");
if (result is null) result.ThrowIfNull();
{
ErrorMessage = "Parse result failed"; Logger.LogInformation("Result: {Result}", result.Data);
return;
}
if (!result.Succeeded) if (!result.Succeeded)
{ {
@ -46,6 +50,7 @@ public partial class Cards
}; };
var parameters = new DialogParameters { { "Data", card } }; var parameters = new DialogParameters { { "Data", card } };
var dialog = await DialogService.ShowAsync<ChangePlayerNameDialog>("Favorite", parameters, options); var dialog = await DialogService.ShowAsync<ChangePlayerNameDialog>("Favorite", parameters, options);
// ReSharper disable once UnusedVariable
var result = await dialog.Result; var result = await dialog.Result;
} }
} }

View File

@ -50,6 +50,12 @@
async Task Submit() async Task Submit()
{ {
if (originalName.Equals(Data.PlayerName))
{
MudDialog.Close(DialogResult.Ok(true));
return;
}
Logger.LogInformation("Data is {CardId}, {Name}", Data.CardId, Data.PlayerName); Logger.LogInformation("Data is {CardId}, {Name}", Data.CardId, Data.PlayerName);
var response = await Client.PostAsJsonAsync("api/Profiles/PlayerName", Data); var response = await Client.PostAsJsonAsync("api/Profiles/PlayerName", Data);
var result = await response.Content.ReadFromJsonAsync<ServiceResult<bool>>(); var result = await response.Content.ReadFromJsonAsync<ServiceResult<bool>>();

61
WebUI/Pages/Option.razor Normal file
View File

@ -0,0 +1,61 @@
@page "/Cards/Option/{cardId:long}"
@using Domain.Enums
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
<PageTitle>Option</PageTitle>
<h1>Play Options</h1>
@if (playOptionData is null)
{
<MudStack>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
</MudStack>
return;
}
@if (errorMessage != string.Empty)
{
<MudText Color="Color.Error" Typo="Typo.h3">@errorMessage</MudText>
return;
}
<MudStack>
<MudStack Row="true">
<MudField Label="Avatar">@GetAvatarName((uint)playOptionData.OptionPart1.AvatarId)</MudField>
<MudButton Variant="Variant.Text">Change Avatar</MudButton>
</MudStack>
<MudStack Row="true">
<MudField Label="Title">@GetTitleName((uint)playOptionData.OptionPart1.TitleId)</MudField>
<MudButton Variant="Variant.Text">Change Title</MudButton>
</MudStack>
<MudStack Row="true">
<MudField Label="Navigator">@GetNavigatorName((uint)playOptionData.OptionPart2.NavigatorId)</MudField>
<MudButton Variant="Variant.Text">Change Navigator</MudButton>
</MudStack>
<MudSelect T="ShowFastSlowOption" Label="Fast/Slow option" Variant="Variant.Outlined"
@bind-Value="@playOptionData.OptionPart1.ShowFastSlowOption">
@foreach (var item in ShowFastSlowOptionExtensions.GetValues())
{
<MudSelectItem Value="item">@item.ToStringFast()</MudSelectItem>
}
</MudSelect>
<MudSelect T="ShowFeverTranceOption" Label="Fever/Trance option" Variant="Variant.Outlined"
@bind-Value="@playOptionData.OptionPart1.ShowFeverTranceOption">
@foreach (var item in ShowFeverTranceOptionExtensions.GetValues())
{
<MudSelectItem Value="item">@item.ToStringFast()</MudSelectItem>
}
</MudSelect>
<MudButton Color="Color.Info" Variant="Variant.Filled">Save Options</MudButton>
</MudStack>

View File

@ -0,0 +1,72 @@
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components;
using Shared.Models;
using Throw;
using WebUI.Services;
using SourceGenerationContext = Shared.SerializerContexts.SourceGenerationContext;
namespace WebUI.Pages;
public partial class Option
{
[Parameter]
public long CardId { get; set; }
[Inject]
public required HttpClient Client { get; set; }
[Inject]
public required IDialogService DialogService { get; set; }
[Inject]
public required IDataService DataService { get; set; }
private readonly List<BreadcrumbItem> breadcrumbs = new()
{
new BreadcrumbItem("Cards", href: "/Cards"),
};
private PlayOptionData? playOptionData;
private string errorMessage = string.Empty;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
breadcrumbs.Add(new BreadcrumbItem($"Card: {CardId}", href:null, disabled:true));
breadcrumbs.Add(new BreadcrumbItem("Option", href: $"/Cards/Option/{CardId}", disabled: false));
await Task.Delay(3000);
var result = await Client.GetFromJsonAsync<ServiceResult<PlayOptionData>>($"api/PlayOption/{CardId}");
result.ThrowIfNull();
if (!result.Succeeded)
{
errorMessage = result.Error!.Message;
return;
}
playOptionData = result.Data;
}
private string GetNavigatorName(uint navigatorId)
{
var navigator = DataService.GetNavigators().GetValueOrDefault(navigatorId);
return navigator?.NavigatorName ?? "Navigator id unknown";
}
private string GetAvatarName(uint avatarId)
{
var avatar = DataService.GetAvatars().GetValueOrDefault(avatarId);
return avatar?.AvatarName ?? "Avatar id unknown";
}
private string GetTitleName(uint titleId)
{
var title = DataService.GetTitles().GetValueOrDefault(titleId);
return title?.TitleName ?? "Title id unknown";
}
}

View File

@ -1,8 +1,8 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Text.Json.Serialization;
using Throw; using Throw;
using WebUI.Common.Models; using WebUI.Common.Models;
using SourceGenerationContext = WebUI.Common.SerializerContexts.SourceGenerationContext;
namespace WebUI.Services; namespace WebUI.Services;
@ -50,4 +50,12 @@ public class DataService : IDataService
{ {
return new ReadOnlyDictionary<uint, Title>(titles); return new ReadOnlyDictionary<uint, Title>(titles);
} }
}
[JsonSerializable(typeof(List<Avatar>))]
[JsonSerializable(typeof(List<Navigator>))]
[JsonSerializable(typeof(List<Title>))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
} }

View File

@ -6,7 +6,7 @@ card_id, pcol1, pcol2, pcol3 are primary keys
| :---: | :-----: | :--------: | :----------------------------------------------------------: | | :---: | :-----: | :--------: | :----------------------------------------------------------: |
| 0 | 0 | 0 | score_i1:**avatar** score_ui1:**fast/slow** score_ui2:**fever/trance** fcol1: fcol2:**title** fcol3:**sound** | | 0 | 0 | 0 | score_i1:**avatar** score_ui1:**fast/slow** score_ui2:**fever/trance** fcol1: fcol2:**title** fcol3:**sound** |
| 1 | 0 | 0 | score_i1:**navigator** fcol3:**unknown** | | 1 | 0 | 0 | score_i1:**navigator** fcol3:**unknown** |
| 10 | song id | 0 | score_i1:**skin** score_ui2/score_ui6: **Unknown used when the song need to be and is unlocked**, fcol1:**Favorite song** | | 10 | song id | 0 | score_i1:**skin** score_ui2s:**Whether the song is unlocked** , core_ui6: **The song need to be unlocked**, fcol1:**Favorite song** |
| 20 | song id | difficulty | score_ui1:**play count** score_ui2:**clear count** score_ui3:**no miss or higher count** score_ui4: **full_chain or higher count** score_ui5: **S+ or higher count** score_ui6: **perfect count** | | 20 | song id | difficulty | score_ui1:**play count** score_ui2:**clear count** score_ui3:**no miss or higher count** score_ui4: **full_chain or higher count** score_ui5: **S+ or higher count** score_ui6: **perfect count** |
| 21 | song id | difficulty | score_ui1/score_ui5: **highest score**, score_ui2/score_ui6:**highest_score time** score_ui3:**max_chain** score_ui4: **max chain time** fcol1:**max hit adlib count** fcol2:**time for fcol1** | | 21 | song id | difficulty | score_ui1/score_ui5: **highest score**, score_ui2/score_ui6:**highest_score time** score_ui3:**max_chain** score_ui4: **max chain time** fcol1:**max hit adlib count** fcol2:**time for fcol1** |
| 30 | 0 | 0 | score_ui2:**Unknown, looks like some sort of count** | | 30 | 0 | 0 | score_ui2:**Unknown, looks like some sort of count** |