Add Play Records page and apis, finish migration (except for online matching)
This commit is contained in:
parent
e5eee66ff2
commit
eb4380b6d5
125
Application/Api/GetSongPlayRecordsQuery.cs
Normal file
125
Application/Api/GetSongPlayRecordsQuery.cs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Domain.Entities;
|
||||||
|
using Domain.Enums;
|
||||||
|
|
||||||
|
namespace Application.Api;
|
||||||
|
|
||||||
|
public record GetSongPlayRecordsQuery(long cardId) : IRequestWrapper<List<SongPlayRecord>>;
|
||||||
|
|
||||||
|
public class GetSongPlayRecordsQueryHandler : RequestHandlerBase<GetSongPlayRecordsQuery, List<SongPlayRecord>>
|
||||||
|
{
|
||||||
|
public GetSongPlayRecordsQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records")]
|
||||||
|
public override async Task<ServiceResult<List<SongPlayRecord>>> Handle(GetSongPlayRecordsQuery request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var exists = await CardDbContext.CardDetails.AnyAsync(detail => detail.CardId == request.cardId);
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
return ServiceResult.Failed<List<SongPlayRecord>>(ServiceError.CustomMessage("No play record"));
|
||||||
|
}
|
||||||
|
var results = new List<SongPlayRecord>();
|
||||||
|
|
||||||
|
var musics = await MusicDbContext.MusicUnlocks.ToDictionaryAsync(unlock => unlock.MusicId, cancellationToken);
|
||||||
|
|
||||||
|
var playCounts = await CardDbContext.CardDetails
|
||||||
|
.Where(detail => detail.CardId == request.cardId &&
|
||||||
|
detail.Pcol1 == 20)
|
||||||
|
.Select(detail => new
|
||||||
|
{
|
||||||
|
MusicId = detail.Pcol2,
|
||||||
|
Difficulty = (Difficulty)detail.Pcol3,
|
||||||
|
Detail = detail
|
||||||
|
})
|
||||||
|
.ToDictionaryAsync(arg => new { arg.MusicId, arg.Difficulty }, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
var stageDetails = await CardDbContext.CardDetails
|
||||||
|
.Where(detail => detail.CardId == request.cardId &&
|
||||||
|
detail.Pcol1 == 21)
|
||||||
|
.Select(detail => new
|
||||||
|
{
|
||||||
|
MusicId = detail.Pcol2,
|
||||||
|
Difficulty = (Difficulty)detail.Pcol3,
|
||||||
|
Score = detail.ScoreUi1,
|
||||||
|
MaxChain = detail.ScoreUi3,
|
||||||
|
})
|
||||||
|
.ToDictionaryAsync(arg => new { arg.MusicId, arg.Difficulty }, cancellationToken);
|
||||||
|
|
||||||
|
var favorites = await CardDbContext.CardDetails
|
||||||
|
.Where(detail => detail.CardId == request.cardId &&
|
||||||
|
detail.Pcol1 == 10)
|
||||||
|
.Select(detail => new { MusicId = detail.Pcol2, IsFavorite = detail.Fcol1 == 1 })
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
foreach (var song in favorites)
|
||||||
|
{
|
||||||
|
var musicId = song.MusicId;
|
||||||
|
var music = musics.GetValueOrDefault(musicId);
|
||||||
|
var songPlayRecord = new SongPlayRecord
|
||||||
|
{
|
||||||
|
MusicId = (int)musicId,
|
||||||
|
Title = music?.Title ?? string.Empty,
|
||||||
|
Artist = music?.Artist ?? string.Empty,
|
||||||
|
IsFavorite = song.IsFavorite
|
||||||
|
};
|
||||||
|
foreach (var difficulty in DifficultyExtensions.GetValues())
|
||||||
|
{
|
||||||
|
var key = new { MusicId = musicId, Difficulty = difficulty };
|
||||||
|
if (!playCounts.ContainsKey(key) || !stageDetails.ContainsKey(key)) continue;
|
||||||
|
var playCountDetail = playCounts[key].Detail;
|
||||||
|
var playCount = playCountDetail.ScoreUi1;
|
||||||
|
var stageDetail = stageDetails[key];
|
||||||
|
var score = stageDetail.Score;
|
||||||
|
var maxChain = stageDetail.MaxChain;
|
||||||
|
var clearState = GetClearState(playCountDetail);
|
||||||
|
var stagePlayRecord = new StagePlayRecord
|
||||||
|
{
|
||||||
|
Difficulty = difficulty,
|
||||||
|
ClearState = clearState,
|
||||||
|
PlayCount = (int)playCount,
|
||||||
|
Score = (int)score,
|
||||||
|
MaxChain = (int)maxChain,
|
||||||
|
LastPlayTime = playCountDetail?.LastPlayTime ?? DateTime.MinValue
|
||||||
|
};
|
||||||
|
songPlayRecord.StagePlayRecords.Add(stagePlayRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
songPlayRecord.TotalPlayCount = songPlayRecord.StagePlayRecords.Sum(record => record.PlayCount);
|
||||||
|
if (songPlayRecord.StagePlayRecords.Count > 0)
|
||||||
|
{
|
||||||
|
results.Add(songPlayRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ServiceResult<List<SongPlayRecord>>(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClearState GetClearState(CardDetail detail)
|
||||||
|
{
|
||||||
|
var result = ClearState.Failed;
|
||||||
|
if (detail.ScoreUi2 > 0)
|
||||||
|
{
|
||||||
|
result = ClearState.Clear;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail.ScoreUi3 > 0)
|
||||||
|
{
|
||||||
|
result = ClearState.NoMiss;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail.ScoreUi4 > 0)
|
||||||
|
{
|
||||||
|
result = ClearState.FullChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail.ScoreUi6 > 0)
|
||||||
|
{
|
||||||
|
result = ClearState.Perfect;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta06" PrivateAssets="all" ExcludeAssets="runtime"/>
|
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta06" PrivateAssets="all" ExcludeAssets="runtime" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -34,5 +34,5 @@ public partial class CardDetail
|
|||||||
|
|
||||||
public long Fcol3 { get; set; }
|
public long Fcol3 { get; set; }
|
||||||
|
|
||||||
public DateTime LastPlayTime { get; set; }
|
public DateTime? LastPlayTime { get; set; }
|
||||||
}
|
}
|
||||||
|
14
Domain/Enums/ClearState.cs
Normal file
14
Domain/Enums/ClearState.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using NetEscapades.EnumGenerators;
|
||||||
|
|
||||||
|
namespace Domain.Enums;
|
||||||
|
|
||||||
|
[EnumExtensions]
|
||||||
|
public enum ClearState
|
||||||
|
{
|
||||||
|
NotPlayed = 0,
|
||||||
|
Failed,
|
||||||
|
Clear,
|
||||||
|
NoMiss,
|
||||||
|
FullChain,
|
||||||
|
Perfect
|
||||||
|
}
|
12
Domain/Enums/Difficulty.cs
Normal file
12
Domain/Enums/Difficulty.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using NetEscapades.EnumGenerators;
|
||||||
|
|
||||||
|
namespace Domain.Enums;
|
||||||
|
|
||||||
|
[EnumExtensions]
|
||||||
|
public enum Difficulty
|
||||||
|
{
|
||||||
|
Simple = 0,
|
||||||
|
Normal = 1,
|
||||||
|
Hard = 2,
|
||||||
|
Extra = 3
|
||||||
|
}
|
@ -48,7 +48,7 @@ namespace Infrastructure.Migrations
|
|||||||
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
lastplaytime = table.Column<long>(name: "last_play_time", type: "INTEGER", nullable: false)
|
lastplaytime = table.Column<long>(name: "last_play_time", type: "INTEGER", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
|
@ -76,7 +76,7 @@ public partial class CardDbContext : DbContext, ICardDbContext
|
|||||||
entity.Property(e => e.Fcol3).HasColumnName("fcol3");
|
entity.Property(e => e.Fcol3).HasColumnName("fcol3");
|
||||||
entity.Property(e => e.LastPlayTenpoId).HasColumnName("last_play_tenpo_id").IsRequired(false);
|
entity.Property(e => e.LastPlayTenpoId).HasColumnName("last_play_tenpo_id").IsRequired(false);
|
||||||
entity.Property(e => e.LastPlayTime).HasColumnName("last_play_time")
|
entity.Property(e => e.LastPlayTime).HasColumnName("last_play_time")
|
||||||
.HasConversion<DateTimeToTicksConverter>();
|
.HasConversion<DateTimeToTicksConverter>().IsRequired(false);
|
||||||
entity.Property(e => e.ScoreBi1).HasColumnName("score_bi1");
|
entity.Property(e => e.ScoreBi1).HasColumnName("score_bi1");
|
||||||
entity.Property(e => e.ScoreI1).HasColumnName("score_i1");
|
entity.Property(e => e.ScoreI1).HasColumnName("score_i1");
|
||||||
entity.Property(e => e.ScoreUi1).HasColumnName("score_ui1");
|
entity.Property(e => e.ScoreUi1).HasColumnName("score_ui1");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"CardDbName": "card.db3",
|
"CardDbName": "card.full.db3",
|
||||||
"MusicDbName": "music471omni.db3"
|
"MusicDbName": "music471omni.db3"
|
||||||
}
|
}
|
@ -23,7 +23,15 @@ public class ProfilesController : BaseController<ProfilesController>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("Favorite")]
|
[HttpGet("SongPlayRecords/{cardId:long}")]
|
||||||
|
public async Task<ServiceResult<List<SongPlayRecord>>> GetSongPlayRecords(long cardId)
|
||||||
|
{
|
||||||
|
var result = await Mediator.Send(new GetSongPlayRecordsQuery(cardId));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("SetFavorite")]
|
||||||
public async Task<ServiceResult<bool>> SetFavoriteMusic(MusicFavoriteDto favorite)
|
public async Task<ServiceResult<bool>> SetFavoriteMusic(MusicFavoriteDto favorite)
|
||||||
{
|
{
|
||||||
var result = await Mediator.Send(new SetFavoriteMusicCommand(favorite));
|
var result = await Mediator.Send(new SetFavoriteMusicCommand(favorite));
|
||||||
|
Binary file not shown.
Binary file not shown.
18
Shared/Models/SongPlayRecord.cs
Normal file
18
Shared/Models/SongPlayRecord.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Shared.Models;
|
||||||
|
|
||||||
|
public class SongPlayRecord
|
||||||
|
{
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Artist { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int MusicId { get; set; }
|
||||||
|
|
||||||
|
public bool IsFavorite { get; set; }
|
||||||
|
|
||||||
|
public int TotalPlayCount { get; set; }
|
||||||
|
|
||||||
|
public List<StagePlayRecord> StagePlayRecords { get; set; } = new();
|
||||||
|
}
|
18
Shared/Models/StagePlayRecord.cs
Normal file
18
Shared/Models/StagePlayRecord.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using Domain.Enums;
|
||||||
|
|
||||||
|
namespace Shared.Models;
|
||||||
|
|
||||||
|
public class StagePlayRecord
|
||||||
|
{
|
||||||
|
public int Score { get; set; }
|
||||||
|
|
||||||
|
public int PlayCount { get; set; }
|
||||||
|
|
||||||
|
public int MaxChain { get; set; }
|
||||||
|
|
||||||
|
public Difficulty Difficulty { get; set; }
|
||||||
|
|
||||||
|
public ClearState ClearState { get; set; }
|
||||||
|
|
||||||
|
public DateTime LastPlayTime { get; set; }
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
// Global using directives
|
// Global using directives
|
||||||
|
|
||||||
global using MudBlazor;
|
global using System.Net.Http.Json;
|
||||||
|
global using Microsoft.AspNetCore.Components;
|
||||||
|
global using MudBlazor;
|
||||||
|
global using Shared.Models;
|
@ -73,7 +73,7 @@
|
|||||||
AnchorOrigin="Origin.BottomCenter"
|
AnchorOrigin="Origin.BottomCenter"
|
||||||
TransformOrigin="Origin.TopCenter">
|
TransformOrigin="Origin.TopCenter">
|
||||||
<MudMenuItem Href="@($"Cards/TotalResult/{card.CardId}")">Total Result</MudMenuItem>
|
<MudMenuItem Href="@($"Cards/TotalResult/{card.CardId}")">Total Result</MudMenuItem>
|
||||||
<MudMenuItem Href="@($"Cards/Results/{card.CardId}")">Song Play Results</MudMenuItem>
|
<MudMenuItem Href="@($"Cards/PlayRecords/{card.CardId}")">Song Play Results</MudMenuItem>
|
||||||
</MudMenu>
|
</MudMenu>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudCardActions>
|
</MudCardActions>
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using System.Net.Http.Json;
|
using Shared.Dto.Api;
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Shared.Dto.Api;
|
|
||||||
using Shared.Models;
|
|
||||||
using Throw;
|
using Throw;
|
||||||
using WebUI.Pages.Dialogs;
|
using WebUI.Pages.Dialogs;
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
@using WebUI.Services
|
@inject IDataService DataService
|
||||||
@using Shared.Models
|
|
||||||
@using WebUI.Common.Models
|
|
||||||
@inject IDataService DataService
|
|
||||||
|
|
||||||
<MudDialog>
|
<MudDialog>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
@using WebUI.Services
|
@inject IDataService DataService
|
||||||
@using Shared.Models
|
|
||||||
@using WebUI.Common.Models
|
|
||||||
@inject IDataService DataService
|
|
||||||
|
|
||||||
<MudDialog>
|
<MudDialog>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
@using System.Text.RegularExpressions
|
@using System.Text.RegularExpressions
|
||||||
@using Shared.Dto.Api
|
@using Shared.Dto.Api
|
||||||
@using Shared.Models
|
|
||||||
@using Throw
|
@using Throw
|
||||||
@inject HttpClient Client
|
@inject HttpClient Client
|
||||||
@inject ILogger<ChangePlayerNameDialog> Logger
|
@inject ILogger<ChangePlayerNameDialog> Logger
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
@using WebUI.Services
|
@inject IDataService DataService
|
||||||
@using Shared.Models
|
|
||||||
@using WebUI.Common.Models
|
|
||||||
@inject IDataService DataService
|
|
||||||
|
|
||||||
<MudDialog>
|
<MudDialog>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
61
WebUI/Pages/Dialogs/FavoriteDialog.razor
Normal file
61
WebUI/Pages/Dialogs/FavoriteDialog.razor
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
@using Shared.Dto.Api
|
||||||
|
@using Throw
|
||||||
|
@inject HttpClient Client
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<TitleContent>
|
||||||
|
@if (!Data.IsFavorite)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.BookmarkAdd" Color="Color.Secondary"/>
|
||||||
|
Add to favorite?
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.BookmarkRemove" Color="Color.Secondary"/>
|
||||||
|
Remove from favorite?
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
</TitleContent>
|
||||||
|
<DialogContent>
|
||||||
|
<MudTextField Value="@Data.Title" Label="Song Title" ReadOnly="true"/>
|
||||||
|
<MudTextField Value="@Data.Artist" Label="Artist Name" ReadOnly="true"/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" OnClick="Submit">Confirm</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code{
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
public required MudDialogInstance MudDialog { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public required SongPlayRecord Data { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
var favoriteData = new MusicFavoriteDto
|
||||||
|
{
|
||||||
|
CardId = CardId,
|
||||||
|
IsFavorite = !Data.IsFavorite,
|
||||||
|
MusicId = Data.MusicId
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await Client.PostAsJsonAsync("api/Profiles/SetFavorite", favoriteData);
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<ServiceResult<bool>>();
|
||||||
|
result.ThrowIfNull();
|
||||||
|
|
||||||
|
MudDialog.Close(DialogResult.Ok(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,4 @@
|
|||||||
using System.Net.Http.Json;
|
using Throw;
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Shared.Models;
|
|
||||||
using Throw;
|
|
||||||
using WebUI.Pages.Dialogs;
|
using WebUI.Pages.Dialogs;
|
||||||
using WebUI.Services;
|
using WebUI.Services;
|
||||||
|
|
||||||
|
84
WebUI/Pages/PlayRecords.razor
Normal file
84
WebUI/Pages/PlayRecords.razor
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
@page "/Cards/PlayRecords/{cardId:long}"
|
||||||
|
|
||||||
|
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
|
||||||
|
|
||||||
|
<PageTitle>Song Play Records</PageTitle>
|
||||||
|
<h1>Song Play Records</h1>
|
||||||
|
@if (errorMessage is not null)
|
||||||
|
{
|
||||||
|
<MudText Color="Color.Error" Typo="Typo.h3">@errorMessage</MudText>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (songPlayRecords is null)
|
||||||
|
{
|
||||||
|
<MudStack>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
<MudSkeleton Width="100%"/>
|
||||||
|
</MudStack>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudDataGrid T="SongPlayRecord"
|
||||||
|
Items="songPlayRecords"
|
||||||
|
SortMode="SortMode.Single">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudText Typo="Typo.h6">Played Songs</MudText>
|
||||||
|
</ToolBarContent>
|
||||||
|
<Columns>
|
||||||
|
<HierarchyColumn T="SongPlayRecord"></HierarchyColumn>
|
||||||
|
<Column T="SongPlayRecord" Field="@nameof(SongPlayRecord.Title)" Title="Song Title"/>
|
||||||
|
<Column T="SongPlayRecord" Field="@nameof(SongPlayRecord.Artist)" Title="Artist"/>
|
||||||
|
<Column T="SongPlayRecord" Field="@nameof(SongPlayRecord.TotalPlayCount)" Title="Total Play Count"/>
|
||||||
|
<Column T="SongPlayRecord" Field="@nameof(SongPlayRecord.IsFavorite)" Title="Is Favorite">
|
||||||
|
<CellTemplate>
|
||||||
|
<MudToggleIconButton Toggled="@context.Item.IsFavorite"
|
||||||
|
ToggledChanged="@(() => OnFavoriteToggled(context.Item))"
|
||||||
|
Icon="@Icons.Material.Filled.FavoriteBorder"
|
||||||
|
Color="@Color.Secondary"
|
||||||
|
Title="Add to favorite"
|
||||||
|
ToggledIcon="@Icons.Material.Filled.Favorite"
|
||||||
|
ToggledColor="@Color.Secondary"
|
||||||
|
ToggledTitle="Remove from favorite"/>
|
||||||
|
</CellTemplate>
|
||||||
|
</Column>
|
||||||
|
</Columns>
|
||||||
|
<ChildRowContent>
|
||||||
|
<MudCard>
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.body1">Song Play Details</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudTable Items="@context.Item.StagePlayRecords">
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>Difficulty</MudTh>
|
||||||
|
<MudTh>Play Count</MudTh>
|
||||||
|
<MudTh>Clear State</MudTh>
|
||||||
|
<MudTh>Score</MudTh>
|
||||||
|
<MudTh>Rating</MudTh>
|
||||||
|
<MudTh>Max Chain</MudTh>
|
||||||
|
<MudTh>Last Play Time</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate Context="stage">
|
||||||
|
<MudTd DataLabel="Difficulty">@stage.Difficulty</MudTd>
|
||||||
|
<MudTd DataLabel="PlayCount">@stage.PlayCount</MudTd>
|
||||||
|
<MudTd DataLabel="ClearState">@stage.ClearState</MudTd>
|
||||||
|
<MudTd DataLabel="Score">@stage.Score</MudTd>
|
||||||
|
<MudTd DataLabel="Rating">@GetRating(stage.Score)</MudTd>
|
||||||
|
<MudTd DataLabel="MaxChain">@stage.MaxChain</MudTd>
|
||||||
|
<MudTd DataLabel="LastPlayTime">@stage.LastPlayTime</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
</ChildRowContent>
|
||||||
|
<PagerContent>
|
||||||
|
<MudDataGridPager T="SongPlayRecord"/>
|
||||||
|
</PagerContent>
|
||||||
|
</MudDataGrid>
|
85
WebUI/Pages/PlayRecords.razor.cs
Normal file
85
WebUI/Pages/PlayRecords.razor.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
using Throw;
|
||||||
|
using WebUI.Pages.Dialogs;
|
||||||
|
|
||||||
|
namespace WebUI.Pages;
|
||||||
|
|
||||||
|
public partial class PlayRecords
|
||||||
|
{
|
||||||
|
private readonly List<BreadcrumbItem> breadcrumbs = new()
|
||||||
|
{
|
||||||
|
new BreadcrumbItem("Cards", href: "/Cards")
|
||||||
|
};
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public required HttpClient Client { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public required IDialogService DialogService { get; set; }
|
||||||
|
|
||||||
|
private string? errorMessage;
|
||||||
|
|
||||||
|
private List<SongPlayRecord>? songPlayRecords;
|
||||||
|
|
||||||
|
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/PlayRecords/{CardId}", disabled: false));
|
||||||
|
|
||||||
|
var result = await Client.GetFromJsonAsync<ServiceResult<List<SongPlayRecord>>>($"api/Profiles/SongPlayRecords/{CardId}");
|
||||||
|
result.ThrowIfNull();
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
errorMessage = result.Error!.Message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
songPlayRecords = result.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRating(int score) => score switch
|
||||||
|
{
|
||||||
|
> 990000 => "S++",
|
||||||
|
> 950000 => "S+",
|
||||||
|
> 900000 => "S",
|
||||||
|
> 800000 => "A",
|
||||||
|
> 700000 => "B",
|
||||||
|
> 500000 => "C",
|
||||||
|
> 300000 => "D",
|
||||||
|
_ => "E"
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task OnFavoriteToggled(SongPlayRecord data)
|
||||||
|
{
|
||||||
|
var options = new DialogOptions
|
||||||
|
{
|
||||||
|
CloseOnEscapeKey = false,
|
||||||
|
DisableBackdropClick = true,
|
||||||
|
FullWidth = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ "Data", data },
|
||||||
|
{"CardId", CardId}
|
||||||
|
};
|
||||||
|
var dialog = await DialogService.ShowAsync<FavoriteDialog>("Favorite", parameters, options);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
if (result.Canceled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Data is ServiceResult<bool> serviceResult && serviceResult.Data)
|
||||||
|
{
|
||||||
|
data.IsFavorite = !data.IsFavorite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -35,6 +35,7 @@
|
|||||||
<MudList>
|
<MudList>
|
||||||
<MudListSubheader>Player Name: @totalResultData.PlayerName</MudListSubheader>
|
<MudListSubheader>Player Name: @totalResultData.PlayerName</MudListSubheader>
|
||||||
<MudListItem>Total Score: @totalResultData.PlayerData.TotalScore</MudListItem>
|
<MudListItem>Total Score: @totalResultData.PlayerData.TotalScore</MudListItem>
|
||||||
|
<MudListItem>Rank: @totalResultData.PlayerData.Rank</MudListItem>
|
||||||
<MudListItem>Average Score: @totalResultData.PlayerData.AverageScore</MudListItem>
|
<MudListItem>Average Score: @totalResultData.PlayerData.AverageScore</MudListItem>
|
||||||
<MudListItem>Played Song Count: @totalResultData.PlayerData.PlayedSongCount / @totalResultData.PlayerData.TotalSongCount</MudListItem>
|
<MudListItem>Played Song Count: @totalResultData.PlayerData.PlayedSongCount / @totalResultData.PlayerData.TotalSongCount</MudListItem>
|
||||||
<MudListItem>Cleared Stage Count: @totalResultData.StageCountData.Cleared / @totalResultData.StageCountData.Total</MudListItem>
|
<MudListItem>Cleared Stage Count: @totalResultData.StageCountData.Cleared / @totalResultData.StageCountData.Total</MudListItem>
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using System.Net.Http.Json;
|
using Throw;
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Shared.Models;
|
|
||||||
using Throw;
|
|
||||||
|
|
||||||
namespace WebUI.Pages;
|
namespace WebUI.Pages;
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using System.Collections.ObjectModel;
|
using Throw;
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using Throw;
|
|
||||||
using WebUI.Common.Models;
|
using WebUI.Common.Models;
|
||||||
|
|
||||||
namespace WebUI.Services;
|
namespace WebUI.Services;
|
||||||
@ -29,17 +26,17 @@ public class DataService : IDataService
|
|||||||
|
|
||||||
public async Task InitializeAsync()
|
public async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
var avatarList = await client.GetFromJsonAsync("data/Avatars.json", SourceGenerationContext.Default.ListAvatar);
|
var avatarList = await client.GetFromJsonAsync<List<Avatar>>("data/Avatars.json");
|
||||||
avatarList.ThrowIfNull();
|
avatarList.ThrowIfNull();
|
||||||
avatars = avatarList.ToDictionary(avatar => avatar.AvatarId);
|
avatars = avatarList.ToDictionary(avatar => avatar.AvatarId);
|
||||||
sortedAvatarList = avatarList.OrderBy(avatar => avatar.AvatarId).ToList();
|
sortedAvatarList = avatarList.OrderBy(avatar => avatar.AvatarId).ToList();
|
||||||
|
|
||||||
var navigatorList = await client.GetFromJsonAsync("data/Navigators.json", SourceGenerationContext.Default.ListNavigator);
|
var navigatorList = await client.GetFromJsonAsync<List<Navigator>>("data/Navigators.json");
|
||||||
navigatorList.ThrowIfNull();
|
navigatorList.ThrowIfNull();
|
||||||
navigators = navigatorList.ToDictionary(navigator => navigator.Id);
|
navigators = navigatorList.ToDictionary(navigator => navigator.Id);
|
||||||
sortedNavigatorList = navigatorList.OrderBy(navigator => navigator.Id).ToList();
|
sortedNavigatorList = navigatorList.OrderBy(navigator => navigator.Id).ToList();
|
||||||
|
|
||||||
var titleList = await client.GetFromJsonAsync("data/Titles.json", SourceGenerationContext.Default.ListTitle);
|
var titleList = await client.GetFromJsonAsync<List<Title>>("data/Titles.json");
|
||||||
titleList.ThrowIfNull();
|
titleList.ThrowIfNull();
|
||||||
titles = titleList.ToDictionary(title => title.Id);
|
titles = titleList.ToDictionary(title => title.Id);
|
||||||
sortedTitleList = titleList.OrderBy(title => title.Id).ToList();
|
sortedTitleList = titleList.OrderBy(title => title.Id).ToList();
|
||||||
@ -74,12 +71,4 @@ public class DataService : IDataService
|
|||||||
{
|
{
|
||||||
return navigators.GetValueOrDefault(id);
|
return navigators.GetValueOrDefault(id);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[JsonSerializable(typeof(List<Avatar>))]
|
|
||||||
[JsonSerializable(typeof(List<Navigator>))]
|
|
||||||
[JsonSerializable(typeof(List<Title>))]
|
|
||||||
internal partial class SourceGenerationContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
@using System.Net.Http
|
@using System.Net.Http
|
||||||
@using System.Net.Http.Json
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@ -8,4 +7,6 @@
|
|||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using MudBlazor
|
@using MudBlazor
|
||||||
@using WebUI
|
@using WebUI
|
||||||
@using WebUI.Common
|
@using WebUI.Common
|
||||||
|
@using WebUI.Services
|
||||||
|
@using WebUI.Common.Models
|
Loading…
x
Reference in New Issue
Block a user