1
0
mirror of synced 2024-11-23 22:10:59 +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;
public record SetFavoriteMusicCommand(MusicDetailDto Data) : IRequestWrapper<bool>;
public record SetFavoriteMusicCommand(MusicFavoriteDto Data) : IRequestWrapper<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;
}
[HttpGet("{cardId:long}")]
[HttpGet("TotalResult/{cardId:long}")]
public async Task<ServiceResult<TotalResultData>> GetCardTotalResultById(long cardId)
{
var result = await Mediator.Send(new GetTotalResultQuery(cardId));
@ -24,9 +24,9 @@ public class ProfilesController : BaseController<ProfilesController>
}
[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;
}
@ -36,4 +36,12 @@ public class ProfilesController : BaseController<ProfilesController>
var result = await Mediator.Send(new SetPlayerNameCommand(card));
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.Extensions.Logging;
using Throw;
using Shared.SerializerContexts;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
@ -58,7 +57,7 @@ try
builder.Services.AddControllers(options =>
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
builder.Services.AddEndpointsApiExplorer();

View File

@ -1,6 +1,6 @@
namespace Shared.Dto.Api;
public class MusicDetailDto
public class MusicFavoriteDto
{
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;
@ -12,9 +13,7 @@ namespace Shared.Models;
[Serializable]
public class ServiceError
{
/// <summary>
/// CTOR
/// </summary>
[JsonConstructor]
public ServiceError(string message, int code)
{
Message = message;
@ -62,9 +61,10 @@ public class ServiceError
}
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 UserFailedToCreate => new("Failed to create User.", 995);

View File

@ -1,4 +1,6 @@
namespace Shared.Models;
using System.Text.Json.Serialization;
namespace Shared.Models;
/// <summary>
/// A standard response for service calls.
@ -14,7 +16,7 @@ public class ServiceResult<T> : ServiceResult
{
Data = data;
}
public ServiceResult(T? data, ServiceError error) : base(error)
{
Data = data;
@ -22,7 +24,7 @@ public class ServiceResult<T> : ServiceResult
public ServiceResult(ServiceError error) : base(error)
{
}
}

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,
ChainMilestone = 14,
Adlibs = 15,
ConsequtiveNoMiss = 16,
ConsecutiveNoMiss = 16,
ClearsUsingItems = 17,
Avatars = 18,
MultiplayerStarsTotal = 19,

View File

@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Components;
using Shared.Dto.Api;
using Shared.Models;
using Throw;
using Shared.SerializerContexts;
using WebUI.Pages.Dialogs;
namespace WebUI.Pages;
@ -26,11 +25,11 @@ public partial class Cards
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Task.Delay(3000);
var result = await Client.GetFromJsonAsync<ServiceResult<List<ClientCardDto>>>("api/Profiles");
result.ThrowIfNull();
Logger.LogInformation("Result: {Result}", result.Data);
Logger.LogInformation("Result: {Result}", result.Succeeded);
if (!result.Succeeded)
{
@ -52,5 +51,9 @@ public partial class Cards
var dialog = await DialogService.ShowAsync<ChangePlayerNameDialog>("Favorite", parameters, options);
// ReSharper disable once UnusedVariable
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,38 +6,40 @@
<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)
@if (errorMessage is not null)
{
<MudText Color="Color.Error" Typo="Typo.h3">@errorMessage</MudText>
return;
}
@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;
}
<MudStack>
<MudStack Row="true">
<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 Row="true">
<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 Row="true">
<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>
@ -57,5 +59,20 @@
}
</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>

View File

@ -2,8 +2,8 @@
using Microsoft.AspNetCore.Components;
using Shared.Models;
using Throw;
using WebUI.Pages.Dialogs;
using WebUI.Services;
using SourceGenerationContext = Shared.SerializerContexts.SourceGenerationContext;
namespace WebUI.Pages;
@ -20,6 +20,8 @@ public partial class Option
[Inject]
public required IDataService DataService { get; set; }
private bool isSaving;
private readonly List<BreadcrumbItem> breadcrumbs = new()
{
@ -28,7 +30,15 @@ public partial class Option
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()
{
@ -36,7 +46,6 @@ public partial class Option
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();
@ -51,22 +60,78 @@ public partial class Option
private string GetNavigatorName(uint navigatorId)
{
var navigator = DataService.GetNavigators().GetValueOrDefault(navigatorId);
var navigator = DataService.GetNavigatorById(navigatorId);
return navigator?.NavigatorName ?? "Navigator id unknown";
}
private string GetAvatarName(uint avatarId)
{
var avatar = DataService.GetAvatars().GetValueOrDefault(avatarId);
var avatar = DataService.GetAvatarById(avatarId);
return avatar?.AvatarName ?? "Avatar id unknown";
}
private string GetTitleName(uint titleId)
{
var title = DataService.GetTitles().GetValueOrDefault(titleId);
var title = DataService.GetTitleById(titleId);
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 List<Avatar> sortedAvatarList = new();
private List<Navigator> sortedNavigatorList = new();
private List<Title> sortedTitleList = new();
private readonly 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);
avatarList.ThrowIfNull();
avatars = avatarList.ToDictionary(avatar => avatar.AvatarId);
sortedAvatarList = avatarList.OrderBy(avatar => avatar.AvatarId).ToList();
var navigatorList = await client.GetFromJsonAsync("data/Navigators.json", SourceGenerationContext.Default.ListNavigator);
navigatorList.ThrowIfNull();
navigators = navigatorList.ToDictionary(navigator => navigator.Id);
sortedNavigatorList = navigatorList.OrderBy(navigator => navigator.Id).ToList();
var titleList = await client.GetFromJsonAsync("data/Titles.json", SourceGenerationContext.Default.ListTitle);
titleList.ThrowIfNull();
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 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** |
| 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** |
| 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** |