1
0
mirror of synced 2024-11-24 06:20:12 +01:00

Finish total result and options page, add unlock all music function

This commit is contained in:
asesidaa 2023-02-22 02:55:32 +08:00
parent 4d0e351abd
commit e5eee66ff2
22 changed files with 644 additions and 70 deletions

View File

@ -2,7 +2,7 @@
namespace Application.Api; namespace Application.Api;
public record SetFavoriteMusicCommand(MusicDetailDto Data) : IRequestWrapper<bool>; public record SetFavoriteMusicCommand(MusicFavoriteDto Data) : IRequestWrapper<bool>;
public class SetFavoriteMusicCommandHandler : RequestHandlerBase<SetFavoriteMusicCommand, bool> public class SetFavoriteMusicCommandHandler : RequestHandlerBase<SetFavoriteMusicCommand, bool>
{ {

View File

@ -0,0 +1,38 @@
using Microsoft.Extensions.Logging;
namespace Application.Api;
public record UnlockAllMusicCommand(long CardId) : IRequestWrapper<bool>;
public class UnlockAllMusicCommandHandler : RequestHandlerBase<UnlockAllMusicCommand, bool>
{
private readonly ILogger<UnlockAllMusicCommandHandler> logger;
public UnlockAllMusicCommandHandler(ICardDependencyAggregate aggregate,
ILogger<UnlockAllMusicCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<bool>> Handle(UnlockAllMusicCommand request, CancellationToken cancellationToken)
{
var unlocks = await CardDbContext.CardDetails.Where(
detail => detail.CardId == request.CardId &&
detail.Pcol1 == 10 &&
detail.ScoreUi6 == 1).ToListAsync(cancellationToken: cancellationToken);
if (unlocks.Count == 0)
{
logger.LogWarning("Attempt to unlock for card {Card} that does not exist or is empty!", request.CardId);
return ServiceResult.Failed<bool>(ServiceError.CustomMessage("Unlock failed"));
}
foreach (var unlock in unlocks)
{
unlock.ScoreUi2 = 1;
}
CardDbContext.CardDetails.UpdateRange(unlocks);
await CardDbContext.SaveChangesAsync(cancellationToken);
return new ServiceResult<bool>(true);
}
}

View File

@ -16,7 +16,7 @@ public class ProfilesController : BaseController<ProfilesController>
return result; return result;
} }
[HttpGet("{cardId:long}")] [HttpGet("TotalResult/{cardId:long}")]
public async Task<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));
@ -24,9 +24,9 @@ public class ProfilesController : BaseController<ProfilesController>
} }
[HttpPost("Favorite")] [HttpPost("Favorite")]
public async Task<ServiceResult<bool>> SetFavoriteMusic(MusicDetailDto detail) public async Task<ServiceResult<bool>> SetFavoriteMusic(MusicFavoriteDto favorite)
{ {
var result = await Mediator.Send(new SetFavoriteMusicCommand(detail)); var result = await Mediator.Send(new SetFavoriteMusicCommand(favorite));
return result; return result;
} }
@ -36,4 +36,12 @@ public class ProfilesController : BaseController<ProfilesController>
var result = await Mediator.Send(new SetPlayerNameCommand(card)); var result = await Mediator.Send(new SetPlayerNameCommand(card));
return result; return result;
} }
[HttpPost("UnlockAllMusic/{cardId:long}")]
public async Task<ServiceResult<bool>> UnlockAllMusic(long cardId)
{
var result = await Mediator.Send(new UnlockAllMusicCommand(cardId));
return result;
}
} }

Binary file not shown.

Binary file not shown.

View File

@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore;
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()
@ -58,7 +57,7 @@ try
builder.Services.AddControllers(options => builder.Services.AddControllers(options =>
options.Filters.Add<ApiExceptionFilterService>()) options.Filters.Add<ApiExceptionFilterService>())
.AddJsonOptions(options => options.JsonSerializerOptions.AddContext<SourceGenerationContext>()); .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
// 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();

View File

@ -1,6 +1,6 @@
namespace Shared.Dto.Api; namespace Shared.Dto.Api;
public class MusicDetailDto public class MusicFavoriteDto
{ {
public long CardId { get; set; } public long CardId { get; set; }

View File

@ -1,4 +1,5 @@
using Domain.Enums; using System.Text.Json.Serialization;
using Domain.Enums;
namespace Shared.Models; namespace Shared.Models;
@ -12,9 +13,7 @@ namespace Shared.Models;
[Serializable] [Serializable]
public class ServiceError public class ServiceError
{ {
/// <summary> [JsonConstructor]
/// CTOR
/// </summary>
public ServiceError(string message, int code) public ServiceError(string message, int code)
{ {
Message = message; Message = message;
@ -63,7 +62,8 @@ public class ServiceError
public static ServiceError DatabaseSaveFailed => new ServiceError("Database save failed", 800); public static ServiceError DatabaseSaveFailed => new ServiceError("Database save failed", 800);
public static ServiceError NotReissue => new ServiceError("Not reissue, registering a new card", (int)CardReturnCode.NotReissue); public static ServiceError NotReissue =>
new ServiceError("Not reissue, registering a new card", (int)CardReturnCode.NotReissue);
public static ServiceError UserNotFound => new("Card with this id does not exist", 996); public static ServiceError UserNotFound => new("Card with this id does not exist", 996);

View File

@ -1,4 +1,6 @@
namespace Shared.Models; using System.Text.Json.Serialization;
namespace Shared.Models;
/// <summary> /// <summary>
/// A standard response for service calls. /// A standard response for service calls.

View File

@ -1,16 +0,0 @@
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

@ -31,7 +31,7 @@ public enum UnlockType
Prefecture = 13, Prefecture = 13,
ChainMilestone = 14, ChainMilestone = 14,
Adlibs = 15, Adlibs = 15,
ConsequtiveNoMiss = 16, ConsecutiveNoMiss = 16,
ClearsUsingItems = 17, ClearsUsingItems = 17,
Avatars = 18, Avatars = 18,
MultiplayerStarsTotal = 19, MultiplayerStarsTotal = 19,

View File

@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Components;
using Shared.Dto.Api; using Shared.Dto.Api;
using Shared.Models; using Shared.Models;
using Throw; using Throw;
using Shared.SerializerContexts;
using WebUI.Pages.Dialogs; using WebUI.Pages.Dialogs;
namespace WebUI.Pages; namespace WebUI.Pages;
@ -26,11 +25,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");
result.ThrowIfNull(); result.ThrowIfNull();
Logger.LogInformation("Result: {Result}", result.Data); Logger.LogInformation("Result: {Result}", result.Succeeded);
if (!result.Succeeded) if (!result.Succeeded)
{ {
@ -52,5 +51,9 @@ public partial class Cards
var dialog = await DialogService.ShowAsync<ChangePlayerNameDialog>("Favorite", parameters, options); var dialog = await DialogService.ShowAsync<ChangePlayerNameDialog>("Favorite", parameters, options);
// ReSharper disable once UnusedVariable // ReSharper disable once UnusedVariable
var result = await dialog.Result; var result = await dialog.Result;
if (!result.Canceled)
{
StateHasChanged();
}
} }
} }

View File

@ -0,0 +1,99 @@
@using WebUI.Services
@using Shared.Models
@using WebUI.Common.Models
@inject IDataService DataService
<MudDialog>
<DialogContent>
<MudTable Items="@avatars" Filter="@Filter" @bind-SelectedItem="@selectedAvatar" Hover="true"
FixedHeader="true" Height="70vh">
<ColGroup>
<col style="width: 50px;"/>
<col/>
</ColGroup>
<ToolBarContent>
<MudTextField @bind-Value="searchString" Placeholder="Search" Adornment="Adornment.Start" Immediate="false"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0">
</MudTextField>
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Avatar, object>(x => x.AvatarId))">
Avatar Id
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Avatar, object>(x => x.AvatarName))">
Avatar Name
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
@{
# pragma warning disable CS8602
}
<MudTd DataLabel="Id" Class="cursor-pointer">@context.AvatarId</MudTd>
<MudTd DataLabel="Title" Class="cursor-pointer">@context.AvatarName</MudTd>
@{
# pragma warning restore CS8602
}
</RowTemplate>
<PagerContent>
<MudTablePager PageSizeOptions="new[] { 10, 25, 50, 100 }"/>
</PagerContent>
</MudTable>
<MudText Class="mt-4 d-block" Typo="Typo.caption">
<b>Selected Title:</b> @selectedAvatar?.AvatarName
</MudText>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Ok</MudButton>
</DialogActions>
</MudDialog>
@code{
[CascadingParameter]
public required MudDialogInstance MudDialog { get; set; }
[Parameter]
public required PlayOptionData Data { get; set; }
private Avatar? selectedAvatar;
private IReadOnlyList<Avatar> avatars = new List<Avatar>();
private string searchString = string.Empty;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
selectedAvatar = DataService.GetAvatarById((uint)Data.OptionPart1.AvatarId);
avatars = DataService.GetAvatarsSortedById();
}
private bool Filter(Avatar? avatar)
{
if (avatar is null)
{
return false;
}
return string.IsNullOrEmpty(searchString) ||
avatar.AvatarName.Contains(searchString, StringComparison.OrdinalIgnoreCase);
}
private void Submit()
{
if (selectedAvatar is not null)
{
Data.OptionPart1.AvatarId = (int)selectedAvatar.AvatarId;
}
MudDialog.Close(DialogResult.Ok(true));
}
private void Cancel() => MudDialog.Cancel();
}

View File

@ -0,0 +1,123 @@
@using WebUI.Services
@using Shared.Models
@using WebUI.Common.Models
@inject IDataService DataService
<MudDialog>
<DialogContent>
<MudTable Items="@navigators" Filter="@Filter" @bind-SelectedItem="@selectedNavigator" Hover="true"
FixedHeader="true" Height="70vh">
<ColGroup>
<col style="width: 50px;"/>
<col/>
</ColGroup>
<ToolBarContent>
<MudTextField @bind-Value="searchString" Placeholder="Search" Adornment="Adornment.Start" Immediate="false"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0">
</MudTextField>
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Navigator, object>(x => x.Id))">
Navigator Id
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Navigator, object>(x => x.NavigatorName))">
Navigator Name
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Navigator, object>(x => x.Genre))">
Navigator Genre
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Navigator, object>(x => x.IllustrationCredit))">
Illustration Credit
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Navigator, object>(x => x.ToolTipJp))">
Tooltip (Japanese)
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Navigator, object>(x => x.ToolTipEn))">
Tooltip (English)
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
@{
# pragma warning disable CS8602
}
<MudTd DataLabel="Id" Class="cursor-pointer">@context.Id</MudTd>
<MudTd DataLabel="Navigator" Class="cursor-pointer">@context.NavigatorName</MudTd>
<MudTd DataLabel="Genre" Class="cursor-pointer">@context.Genre</MudTd>
<MudTd DataLabel="IllustrationCredit" Class="cursor-pointer">@context.IllustrationCredit</MudTd>
<MudTd DataLabel="ToolTipJp" Class="cursor-pointer">@context.ToolTipJp</MudTd>
<MudTd DataLabel="ToolTipEn" Class="cursor-pointer">@context.ToolTipEn</MudTd>
@{
# pragma warning restore CS8602
}
</RowTemplate>
<PagerContent>
<MudTablePager PageSizeOptions="new[] { 10, 25, 50, 100 }"/>
</PagerContent>
</MudTable>
<MudText Class="mt-4 d-block" Typo="Typo.caption">
<b>Selected Navigator:</b> @selectedNavigator?.NavigatorName
</MudText>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Ok</MudButton>
</DialogActions>
</MudDialog>
@code{
[CascadingParameter]
public required MudDialogInstance MudDialog { get; set; }
[Parameter]
public required PlayOptionData Data { get; set; }
private Navigator? selectedNavigator;
private IReadOnlyList<Navigator> navigators = new List<Navigator>();
private string searchString = string.Empty;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
selectedNavigator = DataService.GetNavigatorById((uint)Data.OptionPart2.NavigatorId);
navigators = DataService.GetNavigatorsSortedById();
}
private bool Filter(Navigator? navigator)
{
if (navigator is null)
{
return false;
}
var aggregate = $"{navigator.NavigatorName}{navigator.IllustrationCredit}{navigator.ToolTipEn}{navigator.ToolTipJp}";
return string.IsNullOrEmpty(searchString) ||
aggregate.Contains(searchString, StringComparison.OrdinalIgnoreCase);
}
private void Submit()
{
if (selectedNavigator is not null)
{
Data.OptionPart2.NavigatorId = (int)selectedNavigator.Id;
}
MudDialog.Close(DialogResult.Ok(true));
}
private void Cancel() => MudDialog.Cancel();
}

View File

@ -0,0 +1,116 @@
@using WebUI.Services
@using Shared.Models
@using WebUI.Common.Models
@inject IDataService DataService
<MudDialog>
<DialogContent>
<MudTable Items="@titles" Filter="@Filter" @bind-SelectedItem="@selectedTitle" Hover="true"
FixedHeader="true" Height="70vh">
<ColGroup>
<col style="width: 50px;" />
<col />
</ColGroup>
<ToolBarContent>
<MudTextField @bind-Value="searchString" Placeholder="Search" Adornment="Adornment.Start" Immediate="false"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0">
</MudTextField>
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Title, object>(x => x.Id))">
Title Id
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Title, object>(x => x.TitleName))">
Title Name
</MudTableSortLabel>
</MudTh>
<MudTh>
Unlock Condition Type
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Title, object>(x => x.UnlockRequirementJp))">
Unlock Requirement (Japanese)
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="@(new Func<Title, object>(x => x.UnlockRequirementEn))">
Unlock Requirement (English)
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
@{
# pragma warning disable CS8602
}
<MudTd DataLabel="Id" Class="cursor-pointer">@context.Id</MudTd>
<MudTd DataLabel="Title" Class="cursor-pointer">@context.TitleName</MudTd>
<MudTd DataLabel="UnlockType" Class="cursor-pointer">@context.UnlockType</MudTd>
<MudTd DataLabel="UnlockRequirementJp" Class="cursor-pointer">@context.UnlockRequirementJp</MudTd>
<MudTd DataLabel="UnlockRequirementEn" Class="cursor-pointer">@context.UnlockRequirementEn</MudTd>
@{
# pragma warning restore CS8602
}
</RowTemplate>
<PagerContent>
<MudTablePager PageSizeOptions="new []{10, 25, 50, 100}"/>
</PagerContent>
</MudTable>
<MudText Class="mt-4 d-block" Typo="Typo.caption">
<b>Selected Title:</b> @selectedTitle?.TitleName
</MudText>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Ok</MudButton>
</DialogActions>
</MudDialog>
@code{
[CascadingParameter]
public required MudDialogInstance MudDialog { get; set; }
[Parameter]
public required PlayOptionData Data { get; set; }
private Title? selectedTitle;
private IReadOnlyList<Title> titles = new List<Title>();
private string searchString = string.Empty;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
selectedTitle = DataService.GetTitleById((uint)Data.OptionPart1.TitleId);
titles = DataService.GetTitlesSortedById();
}
private bool Filter(Title? title)
{
if (title is null)
{
return false;
}
var aggregate = $"{title.TitleName}{title.UnlockRequirementEn}{title.UnlockRequirementJp}";
return string.IsNullOrEmpty(searchString) ||
aggregate.Contains(searchString, StringComparison.OrdinalIgnoreCase);
}
private void Submit()
{
if (selectedTitle is not null)
{
Data.OptionPart1.TitleId = (int)selectedTitle.Id;
}
MudDialog.Close(DialogResult.Ok(true));
}
private void Cancel() => MudDialog.Cancel();
}

View File

@ -6,6 +6,13 @@
<PageTitle>Option</PageTitle> <PageTitle>Option</PageTitle>
<h1>Play Options</h1> <h1>Play Options</h1>
@if (errorMessage is not null)
{
<MudText Color="Color.Error" Typo="Typo.h3">@errorMessage</MudText>
return;
}
@if (playOptionData is null) @if (playOptionData is null)
{ {
<MudStack> <MudStack>
@ -19,25 +26,20 @@
return; return;
} }
@if (errorMessage != string.Empty)
{
<MudText Color="Color.Error" Typo="Typo.h3">@errorMessage</MudText>
return;
}
<MudStack> <MudStack>
<MudStack Row="true"> <MudStack Row="true">
<MudField Label="Avatar">@GetAvatarName((uint)playOptionData.OptionPart1.AvatarId)</MudField> <MudField Label="Avatar">@GetAvatarName((uint)playOptionData.OptionPart1.AvatarId)</MudField>
<MudButton Variant="Variant.Text">Change Avatar</MudButton> <MudButton Variant="Variant.Text" OnClick="OpenChangeAvatarDialog">Change Avatar</MudButton>
</MudStack> </MudStack>
<MudStack Row="true"> <MudStack Row="true">
<MudField Label="Title">@GetTitleName((uint)playOptionData.OptionPart1.TitleId)</MudField> <MudField Label="Title">@GetTitleName((uint)playOptionData.OptionPart1.TitleId)</MudField>
<MudButton Variant="Variant.Text">Change Title</MudButton> <MudButton Variant="Variant.Text" OnClick="OpenChangeTitleDialog">Change Title</MudButton>
</MudStack> </MudStack>
<MudStack Row="true"> <MudStack Row="true">
<MudField Label="Navigator">@GetNavigatorName((uint)playOptionData.OptionPart2.NavigatorId)</MudField> <MudField Label="Navigator">@GetNavigatorName((uint)playOptionData.OptionPart2.NavigatorId)</MudField>
<MudButton Variant="Variant.Text">Change Navigator</MudButton> <MudButton Variant="Variant.Text" OnClick="OpenChangeNavigatorDialog">Change Navigator</MudButton>
</MudStack> </MudStack>
@ -57,5 +59,20 @@
} }
</MudSelect> </MudSelect>
<MudButton Color="Color.Info" Variant="Variant.Filled">Save Options</MudButton> <MudButton Color="Color.Info" Variant="Variant.Filled" OnClick="SaveOptions">
@if (isSaving)
{
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
<MudText Class="ms-2">Saving...</MudText>
}
else
{
<MudIcon Icon="@Icons.Material.Filled.Save"></MudIcon>
<MudText>Save</MudText>
}
</MudButton>
<MudButton Color="Color.Default" Variant="Variant.Filled" OnClick="UnlockMusics">
<MudIcon Icon="@Icons.Material.Filled.LockOpen"></MudIcon>
<MudText>Unlock All Musics</MudText>
</MudButton>
</MudStack> </MudStack>

View File

@ -2,8 +2,8 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Shared.Models; using Shared.Models;
using Throw; using Throw;
using WebUI.Pages.Dialogs;
using WebUI.Services; using WebUI.Services;
using SourceGenerationContext = Shared.SerializerContexts.SourceGenerationContext;
namespace WebUI.Pages; namespace WebUI.Pages;
@ -21,6 +21,8 @@ public partial class Option
[Inject] [Inject]
public required IDataService DataService { get; set; } public required IDataService DataService { get; set; }
private bool isSaving;
private readonly List<BreadcrumbItem> breadcrumbs = new() private readonly List<BreadcrumbItem> breadcrumbs = new()
{ {
new BreadcrumbItem("Cards", href: "/Cards"), new BreadcrumbItem("Cards", href: "/Cards"),
@ -28,7 +30,15 @@ public partial class Option
private PlayOptionData? playOptionData; private PlayOptionData? playOptionData;
private string errorMessage = string.Empty; private string? errorMessage;
private static readonly DialogOptions OPTIONS = new()
{
CloseOnEscapeKey = false,
DisableBackdropClick = true,
FullWidth = true,
MaxWidth = MaxWidth.ExtraExtraLarge
};
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -36,7 +46,6 @@ public partial class Option
breadcrumbs.Add(new BreadcrumbItem($"Card: {CardId}", href:null, disabled:true)); breadcrumbs.Add(new BreadcrumbItem($"Card: {CardId}", href:null, disabled:true));
breadcrumbs.Add(new BreadcrumbItem("Option", href: $"/Cards/Option/{CardId}", disabled: false)); 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}"); var result = await Client.GetFromJsonAsync<ServiceResult<PlayOptionData>>($"api/PlayOption/{CardId}");
result.ThrowIfNull(); result.ThrowIfNull();
@ -51,22 +60,78 @@ public partial class Option
private string GetNavigatorName(uint navigatorId) private string GetNavigatorName(uint navigatorId)
{ {
var navigator = DataService.GetNavigators().GetValueOrDefault(navigatorId); var navigator = DataService.GetNavigatorById(navigatorId);
return navigator?.NavigatorName ?? "Navigator id unknown"; return navigator?.NavigatorName ?? "Navigator id unknown";
} }
private string GetAvatarName(uint avatarId) private string GetAvatarName(uint avatarId)
{ {
var avatar = DataService.GetAvatars().GetValueOrDefault(avatarId); var avatar = DataService.GetAvatarById(avatarId);
return avatar?.AvatarName ?? "Avatar id unknown"; return avatar?.AvatarName ?? "Avatar id unknown";
} }
private string GetTitleName(uint titleId) private string GetTitleName(uint titleId)
{ {
var title = DataService.GetTitles().GetValueOrDefault(titleId); var title = DataService.GetTitleById(titleId);
return title?.TitleName ?? "Title id unknown"; return title?.TitleName ?? "Title id unknown";
} }
private async Task OpenChangeTitleDialog()
{
var parameters = new DialogParameters
{
["Data"] = playOptionData
};
var dialog = await DialogService.ShowAsync<ChangeTitleDialog>("Change Title", parameters, OPTIONS);
var result = await dialog.Result;
if (!result.Canceled)
{
StateHasChanged();
}
}
private async Task OpenChangeNavigatorDialog()
{
var parameters = new DialogParameters
{
["Data"] = playOptionData
};
var dialog = await DialogService.ShowAsync<ChangeNavigatorDialog>("Change Navigator", parameters, OPTIONS);
var result = await dialog.Result;
if (!result.Canceled)
{
StateHasChanged();
}
}
private async Task OpenChangeAvatarDialog()
{
var parameters = new DialogParameters
{
["Data"] = playOptionData
};
var dialog = await DialogService.ShowAsync<ChangeAvatarDialog>("Change Navigator", parameters, OPTIONS);
var result = await dialog.Result;
if (!result.Canceled)
{
StateHasChanged();
}
}
private async Task SaveOptions()
{
isSaving = true;
var result = await Client.PostAsJsonAsync("api/PlayOption", playOptionData);
isSaving = false;
}
private async Task UnlockMusics()
{
await Client.PostAsync($"api/Profiles/UnlockAllMusic/{CardId}", null);
}
} }

View File

@ -0,0 +1,47 @@
@page "/Cards/TotalResult/{cardId:long}"
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
<PageTitle>Total Result</PageTitle>
<h1>Total Result</h1>
@if (errorMessage is not null)
{
<MudText Color="Color.Error" Typo="Typo.h3">@errorMessage</MudText>
return;
}
@if (totalResultData is null)
{
<MudStack>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
<MudSkeleton Width="100%"/>
</MudStack>
return;
}
@if (totalResultData.PlayerData.PlayedSongCount == 0)
{
<MudText Typo="Typo.h3">
No Play Record
</MudText>
return;
}
<MudList>
<MudListSubheader>Player Name: @totalResultData.PlayerName</MudListSubheader>
<MudListItem>Total Score: @totalResultData.PlayerData.TotalScore</MudListItem>
<MudListItem>Average Score: @totalResultData.PlayerData.AverageScore</MudListItem>
<MudListItem>Played Song Count: @totalResultData.PlayerData.PlayedSongCount / @totalResultData.PlayerData.TotalSongCount</MudListItem>
<MudListItem>Cleared Stage Count: @totalResultData.StageCountData.Cleared / @totalResultData.StageCountData.Total</MudListItem>
<MudListItem>No Miss Stage Count: @totalResultData.StageCountData.NoMiss / @totalResultData.StageCountData.Total</MudListItem>
<MudListItem>Full Chain Stage Count: @totalResultData.StageCountData.FullChain / @totalResultData.StageCountData.Total</MudListItem>
<MudListItem>Perfect Stage Count: @totalResultData.StageCountData.Perfect / @totalResultData.StageCountData.Total</MudListItem>
<MudListItem>S and Above Stage Count: @totalResultData.StageCountData.S / @totalResultData.StageCountData.Total</MudListItem>
<MudListItem>S+ and Above Stage Count: @totalResultData.StageCountData.Ss / @totalResultData.StageCountData.Total</MudListItem>
<MudListItem>S++ and Above Stage Count: @totalResultData.StageCountData.Sss / @totalResultData.StageCountData.Total</MudListItem>
</MudList>

View File

@ -0,0 +1,43 @@
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components;
using Shared.Models;
using Throw;
namespace WebUI.Pages;
public partial class TotalResult
{
private readonly List<BreadcrumbItem> breadcrumbs = new()
{
new BreadcrumbItem("Cards", href: "/Cards")
};
[Parameter]
public long CardId { get; set; }
[Inject]
public required HttpClient Client { get; set; }
private string? errorMessage;
private TotalResultData? totalResultData;
protected async override Task OnInitializedAsync()
{
await base.OnInitializedAsync();
breadcrumbs.Add(new BreadcrumbItem($"Card: {CardId}", href:null, disabled:true));
breadcrumbs.Add(new BreadcrumbItem("TotalResult", href: $"/Cards/TotalResult/{CardId}", disabled: false));
var result = await Client.GetFromJsonAsync<ServiceResult<TotalResultData>>($"api/Profiles/TotalResult/{CardId}");
result.ThrowIfNull();
if (!result.Succeeded)
{
errorMessage = result.Error!.Message;
return;
}
totalResultData = result.Data;
}
}

View File

@ -14,6 +14,12 @@ public class DataService : IDataService
private Dictionary<uint, Title> titles = new(); private Dictionary<uint, Title> titles = new();
private List<Avatar> sortedAvatarList = new();
private List<Navigator> sortedNavigatorList = new();
private List<Title> sortedTitleList = new();
private readonly HttpClient client; private readonly HttpClient client;
public DataService(HttpClient client) public DataService(HttpClient client)
@ -26,29 +32,47 @@ public class DataService : IDataService
var avatarList = await client.GetFromJsonAsync("data/Avatars.json", SourceGenerationContext.Default.ListAvatar); var avatarList = await client.GetFromJsonAsync("data/Avatars.json", SourceGenerationContext.Default.ListAvatar);
avatarList.ThrowIfNull(); avatarList.ThrowIfNull();
avatars = avatarList.ToDictionary(avatar => avatar.AvatarId); avatars = avatarList.ToDictionary(avatar => avatar.AvatarId);
sortedAvatarList = avatarList.OrderBy(avatar => avatar.AvatarId).ToList();
var navigatorList = await client.GetFromJsonAsync("data/Navigators.json", SourceGenerationContext.Default.ListNavigator); var navigatorList = await client.GetFromJsonAsync("data/Navigators.json", SourceGenerationContext.Default.ListNavigator);
navigatorList.ThrowIfNull(); navigatorList.ThrowIfNull();
navigators = navigatorList.ToDictionary(navigator => navigator.Id); navigators = navigatorList.ToDictionary(navigator => navigator.Id);
sortedNavigatorList = navigatorList.OrderBy(navigator => navigator.Id).ToList();
var titleList = await client.GetFromJsonAsync("data/Titles.json", SourceGenerationContext.Default.ListTitle); var titleList = await client.GetFromJsonAsync("data/Titles.json", SourceGenerationContext.Default.ListTitle);
titleList.ThrowIfNull(); titleList.ThrowIfNull();
titles = titleList.ToDictionary(title => title.Id); titles = titleList.ToDictionary(title => title.Id);
sortedTitleList = titleList.OrderBy(title => title.Id).ToList();
} }
public IReadOnlyDictionary<uint, Avatar> GetAvatars() public IReadOnlyList<Avatar> GetAvatarsSortedById()
{ {
return new ReadOnlyDictionary<uint, Avatar>(avatars); return sortedAvatarList;
} }
public IReadOnlyDictionary<uint, Navigator> GetNavigators() public IReadOnlyList<Navigator> GetNavigatorsSortedById()
{ {
return new ReadOnlyDictionary<uint, Navigator>(navigators); return sortedNavigatorList;
} }
public IReadOnlyDictionary<uint, Title> GetTitles() public IReadOnlyList<Title> GetTitlesSortedById()
{ {
return new ReadOnlyDictionary<uint, Title>(titles); return sortedTitleList;
}
public Avatar? GetAvatarById(uint id)
{
return avatars.GetValueOrDefault(id);
}
public Title? GetTitleById(uint id)
{
return titles.GetValueOrDefault(id);
}
public Navigator? GetNavigatorById(uint id)
{
return navigators.GetValueOrDefault(id);
} }
} }

View File

@ -7,9 +7,15 @@ public interface IDataService
{ {
public Task InitializeAsync(); public Task InitializeAsync();
public IReadOnlyDictionary<uint, Avatar> GetAvatars(); public IReadOnlyList<Avatar> GetAvatarsSortedById();
public IReadOnlyDictionary<uint, Navigator> GetNavigators(); public IReadOnlyList<Navigator> GetNavigatorsSortedById();
public IReadOnlyDictionary<uint, Title> GetTitles(); public IReadOnlyList<Title> GetTitlesSortedById();
public Avatar? GetAvatarById(uint id);
public Title? GetTitleById(uint id);
public Navigator? GetNavigatorById(uint id);
} }

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_ui2s:**Whether the song is unlocked** , core_ui6: **The song need to be unlocked**, fcol1:**Favorite song** | | 10 | song id | 0 | score_i1:**skin**, score_ui2:**Whether the song is unlocked**, score_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** |