Add last play time support
Support setting avatar, navigator and titles on web interface (unoptimized)
This commit is contained in:
parent
95d9d0be5f
commit
4cd290aedc
@ -1,3 +1,6 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Adlibs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Keynum/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unlockable/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Touhou/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unlockable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vocaloid/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
@ -126,7 +126,8 @@ public static class Configs
|
||||
|
||||
public const string RANK_STATUS_XPATH = $"{ROOT_XPATH}/ranking_status";
|
||||
|
||||
public const int CONFIG_PCOL1 = 0;
|
||||
public const int FIRST_CONFIG_PCOL1 = 0;
|
||||
public const int SECOND_CONFIG_PCOL1 = 1;
|
||||
public const int CONFIG_PCOL2 = 0;
|
||||
public const int CONFIG_PCOL3 = 0;
|
||||
|
||||
|
@ -65,26 +65,38 @@ public class ApiController : WebApiController
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public bool SetPlayOption([JsonData] PlayOption data)
|
||||
{
|
||||
var existing = cardSqLiteConnection.Table<CardDetail>()
|
||||
var firstConfig = cardSqLiteConnection.Table<CardDetail>()
|
||||
.Where(detail => detail.CardId == data.CardId
|
||||
&& detail.Pcol1 == Configs.CONFIG_PCOL1
|
||||
&& detail.Pcol1 == Configs.FIRST_CONFIG_PCOL1
|
||||
&& detail.Pcol2 == Configs.CONFIG_PCOL2
|
||||
&& detail.Pcol3 == Configs.CONFIG_PCOL3);
|
||||
|
||||
if (!existing.Any())
|
||||
var secondConfig = cardSqLiteConnection.Table<CardDetail>()
|
||||
.Where(detail => detail.CardId == data.CardId
|
||||
&& detail.Pcol1 == Configs.SECOND_CONFIG_PCOL1
|
||||
&& detail.Pcol2 == Configs.CONFIG_PCOL2
|
||||
&& detail.Pcol3 == Configs.CONFIG_PCOL3);
|
||||
|
||||
if (!firstConfig.Any() || !secondConfig.Any())
|
||||
{
|
||||
$"Trying to update non existing card's config! Card id {data.CardId}".Warn();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var cardDetail = existing.First();
|
||||
cardDetail.ScoreUi1 = (long)data.FastSlowIndicator;
|
||||
cardDetail.ScoreUi2 = (long)data.FeverTrance;
|
||||
var firstDetail = firstConfig.First();
|
||||
firstDetail.ScoreUi1 = (long)data.FastSlowIndicator;
|
||||
firstDetail.ScoreUi2 = (long)data.FeverTrance;
|
||||
firstDetail.ScoreI1 = data.AvatarId;
|
||||
firstDetail.Fcol2 = (int)data.TitleId;
|
||||
|
||||
var result = cardSqLiteConnection.Update(cardDetail);
|
||||
var secondDetail = secondConfig.First();
|
||||
secondDetail.ScoreI1 = data.NavigatorId;
|
||||
|
||||
return result == 1;
|
||||
var firstResult = cardSqLiteConnection.Update(firstDetail);
|
||||
var secondResult = cardSqLiteConnection.Update(secondDetail);
|
||||
|
||||
return firstResult == 1 && secondResult == 1;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, "/UserDetail/{cardId}")]
|
||||
@ -125,16 +137,26 @@ public class ApiController : WebApiController
|
||||
|
||||
private void ProcessCardDetail(UserDetail userDetail, IDictionary<int, SongPlayData> songPlayDataDict)
|
||||
{
|
||||
var option = cardSqLiteConnection.Table<CardDetail>()
|
||||
var firstOption = cardSqLiteConnection.Table<CardDetail>()
|
||||
.FirstOrDefault(detail => detail.CardId == userDetail.CardId
|
||||
&& detail.Pcol1 == Configs.CONFIG_PCOL1
|
||||
&& detail.Pcol1 == Configs.FIRST_CONFIG_PCOL1
|
||||
&& detail.Pcol2 == Configs.CONFIG_PCOL2
|
||||
&& detail.Pcol3 == Configs.CONFIG_PCOL3
|
||||
, new CardDetail
|
||||
{
|
||||
CardId = userDetail.CardId
|
||||
});
|
||||
SetOptions(option, userDetail);
|
||||
var secondOption = cardSqLiteConnection.Table<CardDetail>()
|
||||
.FirstOrDefault(detail => detail.CardId == userDetail.CardId
|
||||
&& detail.Pcol1 == Configs.SECOND_CONFIG_PCOL1
|
||||
&& detail.Pcol2 == Configs.CONFIG_PCOL2
|
||||
&& detail.Pcol3 == Configs.CONFIG_PCOL3
|
||||
, new CardDetail
|
||||
{
|
||||
CardId = userDetail.CardId
|
||||
});
|
||||
|
||||
SetOptions(firstOption, secondOption, userDetail);
|
||||
|
||||
var songCounts = cardSqLiteConnection.Table<CardDetail>()
|
||||
.Where(detail => detail.CardId == userDetail.CardId && detail.Pcol1 == Configs.COUNT_PCOL1);
|
||||
@ -162,10 +184,10 @@ public class ApiController : WebApiController
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetOptions(CardDetail cardDetail, UserDetail userDetail)
|
||||
private static void SetOptions(CardDetail firstOptionCardDetail, CardDetail secondOptionCardDetail, UserDetail userDetail)
|
||||
{
|
||||
var fastSlow = (int)cardDetail.ScoreUi1;
|
||||
var feverTrance = (int)cardDetail.ScoreUi2;
|
||||
var fastSlow = (int)firstOptionCardDetail.ScoreUi1;
|
||||
var feverTrance = (int)firstOptionCardDetail.ScoreUi2;
|
||||
|
||||
if (!Enum.IsDefined(typeof(PlayOptions.FastSlowIndicator), fastSlow))
|
||||
{
|
||||
@ -179,9 +201,12 @@ public class ApiController : WebApiController
|
||||
|
||||
userDetail.PlayOption = new PlayOption
|
||||
{
|
||||
CardId = cardDetail.CardId,
|
||||
CardId = firstOptionCardDetail.CardId,
|
||||
FastSlowIndicator = (PlayOptions.FastSlowIndicator)fastSlow,
|
||||
FeverTrance = (PlayOptions.FeverTranceShow)feverTrance
|
||||
FeverTrance = (PlayOptions.FeverTranceShow)feverTrance,
|
||||
AvatarId = firstOptionCardDetail.ScoreI1,
|
||||
TitleId = firstOptionCardDetail.Fcol2,
|
||||
NavigatorId = secondOptionCardDetail.ScoreI1
|
||||
};
|
||||
}
|
||||
private void SetDetails(CardDetail cardDetail, IDictionary<int, SongPlayData> songPlayDataDict,
|
||||
@ -241,6 +266,7 @@ public class ApiController : WebApiController
|
||||
}
|
||||
|
||||
songPlayDetailData.PlayCount = (int)cardDetail.ScoreUi1;
|
||||
songPlayDetailData.LastPlayTime = cardDetail.LastPlayTime;
|
||||
songPlayDetailData.ClearState = ClearState.Failed;
|
||||
userDetail.PlayedStageCount++;
|
||||
|
||||
|
@ -40,14 +40,14 @@ public class CardServiceController : WebApiController
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(Command), cmdType))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(cmdType), cmdType, "Cmd type is unknown!");
|
||||
throw new ArgumentOutOfRangeException(nameof(cmdType), cmdType, $"Cmd type is unknown!\n Data is {xmlData}");
|
||||
}
|
||||
|
||||
var command = (Command)cmdType;
|
||||
|
||||
return command switch
|
||||
{
|
||||
Command.CardRequest => ProcessCardRequest(mac, cardId, xmlData, type),
|
||||
Command.CardReadRequest or Command.CardWriteRequest => ProcessCardRequest(mac, cardId, xmlData, type),
|
||||
Command.ReissueRequest => ProcessReissueRequest(),
|
||||
Command.RegisterRequest => ProcessRegisterRequest(cardId, xmlData),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(command), command, "Command unknown, should never happen!")
|
||||
@ -539,8 +539,31 @@ public class CardServiceController : WebApiController
|
||||
var result = cardSqLiteConnection.Table<CardDetail>()
|
||||
.Where(detail => detail.CardId == cardId);
|
||||
|
||||
// Unlock all unlockable songs in card details table when write for the first time
|
||||
// Unlock all unlockable songs in card details table when write card detail for the first time
|
||||
if (!result.Any())
|
||||
{
|
||||
UnlockSongs(cardId);
|
||||
}
|
||||
|
||||
var reader = new ChoXmlReader<CardDetail>(new StringReader(xmlData)).WithXPath(Configs.DATA_XPATH);
|
||||
var cardDetail = reader.Read();
|
||||
|
||||
if (cardDetail is null)
|
||||
{
|
||||
throw new HttpRequestException("Write object is null");
|
||||
}
|
||||
|
||||
cardDetail.SetCardId(cardId);
|
||||
cardDetail.LastPlayTime = DateTime.Now;
|
||||
var rowsAffected = cardSqLiteConnection.InsertOrReplace(cardDetail);
|
||||
if (rowsAffected == 0)
|
||||
{
|
||||
throw new ApplicationException("Update database failed!");
|
||||
}
|
||||
|
||||
"Updated card detail".Info();
|
||||
}
|
||||
private void UnlockSongs(long cardId)
|
||||
{
|
||||
var unlockableSongIds = Configs.SETTINGS.UnlockableSongIds;
|
||||
|
||||
@ -555,16 +578,14 @@ public class CardServiceController : WebApiController
|
||||
Pcol2 = id,
|
||||
Pcol3 = 0,
|
||||
ScoreUi2 = 1,
|
||||
ScoreUi6 = 1
|
||||
ScoreUi6 = 1,
|
||||
LastPlayTime = DateTime.Now
|
||||
})
|
||||
.ToList();
|
||||
|
||||
cardSqLiteConnection.InsertOrIgnoreAll(detailList);
|
||||
}
|
||||
|
||||
Write<CardDetail>(cardId, xmlData);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private enum CardRequestType
|
||||
@ -612,7 +633,8 @@ public class CardServiceController : WebApiController
|
||||
|
||||
private enum Command
|
||||
{
|
||||
CardRequest = 256,
|
||||
CardReadRequest = 256,
|
||||
CardWriteRequest = 768,
|
||||
RegisterRequest = 512,
|
||||
ReissueRequest = 1536
|
||||
}
|
||||
|
@ -91,6 +91,10 @@ public class CardDetail : Record, ICardIdModel
|
||||
[XmlElement("fcol3")]
|
||||
public int Fcol3 { get; set; }
|
||||
|
||||
[Column("last_play_time")]
|
||||
[XmlIgnore]
|
||||
public DateTime LastPlayTime { get; set; } = DateTime.MinValue;
|
||||
|
||||
public void SetCardId(long cardId)
|
||||
{
|
||||
CardId = cardId;
|
||||
|
@ -36,5 +36,9 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Utils" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
@ -1,21 +1,23 @@
|
||||
@page "/user/{CardId:long}"
|
||||
@using SharedProject.models
|
||||
@using SharedProject.enums
|
||||
@using SharedProject.common
|
||||
@inject HttpClient Client
|
||||
@inject IDialogService DialogService
|
||||
@inject ILogger<User> Logger
|
||||
|
||||
<PageTitle>User</PageTitle>
|
||||
<MudContainer>
|
||||
@if (userDetail == null)
|
||||
{
|
||||
@if (pageLoading)
|
||||
{
|
||||
<MudSkeleton Width="1184px" Height="57px"/>
|
||||
<MudSkeleton Width="1184px" Height="57px"/>
|
||||
<MudSkeleton Width="1184px" Height="57px"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (userDetail is null)
|
||||
{
|
||||
<MudText Typo="Typo.h3">No Data</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudExpansionPanels>
|
||||
<MudExpansionPanel Text="Total Result">
|
||||
<MudList>
|
||||
@ -33,21 +35,77 @@ else
|
||||
</MudList>
|
||||
</MudExpansionPanel>
|
||||
<MudExpansionPanel Text="PlayOptions">
|
||||
<MudSelect @bind-Value="@fastSlowIndicator" Label="FAST/SLOW show setting">
|
||||
<MudSelect @bind-Value="@playOption.FastSlowIndicator"
|
||||
Label="FAST/SLOW show setting">
|
||||
@foreach (var item in Enum.GetValues<PlayOptions.FastSlowIndicator>())
|
||||
{
|
||||
<MudSelectItem Value="@item">@item.GetHelpText()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudSelect @bind-Value="@feverTranceShow" Label="FEVER/TRANCE show setting">
|
||||
<MudSelect @bind-Value="@playOption.FeverTrance"
|
||||
Label="FEVER/TRANCE show setting">
|
||||
@foreach (var item in Enum.GetValues<PlayOptions.FeverTranceShow>())
|
||||
{
|
||||
<MudSelectItem Value="@item">@item.GetHelpText()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudButton Disabled="@isSavingOptions" OnClick="SaveOptions" Variant="Variant.Filled" Color="Color.Info">
|
||||
<MudAutocomplete T="long" Label="Avatar setting"
|
||||
@bind-Value="@playOption.AvatarId"
|
||||
CoerceText="true" SearchFunc="@SearchAvatar"
|
||||
ToStringFunc="@AvatarIdToString"
|
||||
Dense="true"
|
||||
MaxItems="@avatarMaxItems">
|
||||
<MoreItemsTemplate>
|
||||
@*<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
|
||||
OnClick="() => { avatarMaxItems += 10; AvatarAutoComplete.Clear();}">
|
||||
Load more options
|
||||
</MudButton>*@
|
||||
<MudText Align="Align.Center">
|
||||
Only first 50 items are displayed
|
||||
</MudText>
|
||||
</MoreItemsTemplate>
|
||||
</MudAutocomplete>
|
||||
|
||||
<MudAutocomplete T="long" Label="Navigator setting"
|
||||
@bind-Value="@playOption.NavigatorId"
|
||||
CoerceText="true" SearchFunc="@SearchNavigator"
|
||||
ToStringFunc="@NavigatorIdToString"
|
||||
Dense="true"
|
||||
MaxItems="@avatarMaxItems">
|
||||
<MoreItemsTemplate>
|
||||
@*<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
|
||||
OnClick="() => { avatarMaxItems += 10; AvatarAutoComplete.Clear();}">
|
||||
Load more options
|
||||
</MudButton>*@
|
||||
<MudText Align="Align.Center">
|
||||
Only first 50 items are displayed
|
||||
</MudText>
|
||||
</MoreItemsTemplate>
|
||||
</MudAutocomplete>
|
||||
|
||||
<MudAutocomplete T="long" Label="Title setting"
|
||||
@bind-Value="@playOption.TitleId"
|
||||
CoerceText="true" SearchFunc="@SearchTitle"
|
||||
ToStringFunc="@TitleIdToString"
|
||||
Dense="true"
|
||||
MaxItems="@avatarMaxItems">
|
||||
<MoreItemsTemplate>
|
||||
@*<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
|
||||
OnClick="() => { avatarMaxItems += 10; AvatarAutoComplete.Clear();}">
|
||||
Load more options
|
||||
</MudButton>*@
|
||||
<MudText Align="Align.Center">
|
||||
Only first 50 items are displayed
|
||||
</MudText>
|
||||
</MoreItemsTemplate>
|
||||
</MudAutocomplete>
|
||||
|
||||
<MudButton Disabled="@isSavingOptions"
|
||||
OnClick="SaveOptions"
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Info">
|
||||
@if (isSavingOptions)
|
||||
{
|
||||
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
|
||||
@ -61,7 +119,10 @@ else
|
||||
</MudButton>
|
||||
</MudExpansionPanel>
|
||||
<MudExpansionPanel Text="SongPlayData">
|
||||
<MudDataGrid T="SongPlayData" Items="@songPlayDataList" Sortable="true" Filterable="true">
|
||||
<MudDataGrid T="SongPlayData"
|
||||
Items="@songPlayDataList"
|
||||
Sortable="true"
|
||||
Filterable="true">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">Played Songs</MudText>
|
||||
</ToolBarContent>
|
||||
@ -77,14 +138,19 @@ else
|
||||
<Column T="SongPlayData" Field="IsFavorite" Sortable="false" Title="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"/>
|
||||
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>
|
||||
<Column T="SongPlayData" Field="Title" Title="Song Title"/>
|
||||
<Column T="SongPlayData" Field="Artist" Title="Artist"/>
|
||||
<Column T="SongPlayData" Field="TotalPlayCount" Title="Total Play Count" />
|
||||
<Column T="SongPlayData" Field="TotalPlayCount" Title="Total Play Count"/>
|
||||
<Column T="SongPlayData" Field="LastPlayTime" Title="Last Play Time"/>
|
||||
</Columns>
|
||||
<ChildRowContent>
|
||||
@if (context.ShowDetails)
|
||||
@ -98,7 +164,10 @@ else
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent Class="pa-0">
|
||||
<MudTable Items="@context.SongPlaySubDataList" Context="SongPlayDetail" Elevation="0" Filter="data => data.ClearState != ClearState.NotPlayed">
|
||||
<MudTable Items="@context.SongPlaySubDataList"
|
||||
Context="SongPlayDetail"
|
||||
Elevation="0"
|
||||
Filter="data => data.ClearState != ClearState.NotPlayed">
|
||||
<HeaderContent>
|
||||
<MudTh>Difficulty</MudTh>
|
||||
<MudTh>Clear State</MudTh>
|
||||
@ -106,6 +175,7 @@ else
|
||||
<MudTh>Rating</MudTh>
|
||||
<MudTh>Score</MudTh>
|
||||
<MudTh>Max Chain</MudTh>
|
||||
<MudTh>Last Play Time</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Difficulty">@SongPlayDetail.Difficulty</MudTd>
|
||||
@ -116,6 +186,7 @@ else
|
||||
<MudTd DataLabel="Rating">@CalculateRating(SongPlayDetail.Score)</MudTd>
|
||||
<MudTd DataLabel="Score">@SongPlayDetail.Score</MudTd>
|
||||
<MudTd DataLabel="Max Chain">@SongPlayDetail.MaxChain</MudTd>
|
||||
<MudTd DataLabel="Last Play Time">@SongPlayDetail.LastPlayTime</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
</MudCardContent>
|
||||
@ -130,80 +201,6 @@ else
|
||||
</MudDataGrid>
|
||||
</MudExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
}
|
||||
}
|
||||
}
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public long CardId { get; set; }
|
||||
|
||||
private PlayOptions.FeverTranceShow feverTranceShow;
|
||||
|
||||
private PlayOptions.FastSlowIndicator fastSlowIndicator;
|
||||
|
||||
private UserDetail? userDetail;
|
||||
|
||||
private List<SongPlayData> songPlayDataList = new();
|
||||
|
||||
private bool isSavingOptions;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
userDetail = await Client.GetFromJsonAsync<UserDetail>($"api/UserDetail/{CardId}") ?? new();
|
||||
songPlayDataList = userDetail.SongPlayDataList ?? new List<SongPlayData>();
|
||||
feverTranceShow = userDetail.PlayOption.FeverTrance;
|
||||
fastSlowIndicator = userDetail.PlayOption.FastSlowIndicator;
|
||||
}
|
||||
|
||||
private void OnShowDetailsClick(SongPlayData data)
|
||||
{
|
||||
data.ShowDetails = !data.ShowDetails;
|
||||
}
|
||||
|
||||
private async Task SaveOptions()
|
||||
{
|
||||
isSavingOptions = true;
|
||||
var postData = new PlayOption
|
||||
{
|
||||
CardId = CardId,
|
||||
FastSlowIndicator = fastSlowIndicator,
|
||||
FeverTrance = feverTranceShow
|
||||
};
|
||||
var result = await Client.PostAsJsonAsync("api/UserDetail/SetPlayOption", postData);
|
||||
isSavingOptions = false;
|
||||
}
|
||||
|
||||
private static string CalculateRating(int score)
|
||||
{
|
||||
var grade = SharedConstants.GRADES.Where(g => g.Score <= score).Select(g => g.Grade).Last();
|
||||
return grade;
|
||||
}
|
||||
|
||||
private async Task OnFavoriteToggled(SongPlayData data)
|
||||
{
|
||||
var options = new DialogOptions
|
||||
{
|
||||
CloseOnEscapeKey = false,
|
||||
DisableBackdropClick = true,
|
||||
FullWidth = true
|
||||
};
|
||||
var parameters = new DialogParameters();
|
||||
parameters.Add("Data", data);
|
||||
parameters.Add("CardId", CardId);
|
||||
var dialog = DialogService.Show<FavoriteDialog>("Favorite", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (result.Cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)result.Data)
|
||||
{
|
||||
Logger.LogInformation("Changed!");
|
||||
data.IsFavorite = !data.IsFavorite;
|
||||
}
|
||||
}
|
||||
}
|
163
MudAdmin/Pages/User.razor.cs
Normal file
163
MudAdmin/Pages/User.razor.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
using SharedProject.common;
|
||||
using SharedProject.models;
|
||||
|
||||
namespace MudAdmin.Pages;
|
||||
|
||||
public partial class User
|
||||
{
|
||||
|
||||
[Inject]
|
||||
public HttpClient Client { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
public IDialogService DialogService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
public ILogger<User> Logger { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public long CardId { get; set; }
|
||||
|
||||
private PlayOption playOption = new();
|
||||
|
||||
private UserDetail? userDetail;
|
||||
|
||||
private List<SongPlayData> songPlayDataList = new();
|
||||
|
||||
private Dictionary<long, Navigator> navigatorDictionary = new();
|
||||
|
||||
private Dictionary<long, Title> titleDictionary = new();
|
||||
|
||||
private Dictionary<long, Avatar> avatarDictionary = new();
|
||||
|
||||
private bool isSavingOptions;
|
||||
|
||||
private int avatarMaxItems = 50;
|
||||
|
||||
private bool pageLoading = true;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
userDetail = await Client.GetFromJsonAsync<UserDetail>($"api/UserDetail/{CardId}");
|
||||
if (userDetail is null)
|
||||
{
|
||||
pageLoading = false;
|
||||
return;
|
||||
}
|
||||
songPlayDataList = userDetail.SongPlayDataList ?? new List<SongPlayData>();
|
||||
playOption = userDetail.PlayOption;
|
||||
|
||||
var navigators = await Client.GetFromJsonAsync<Navigators>("data/navigator.json");
|
||||
if (navigators?.NavigatorList != null)
|
||||
{
|
||||
this.navigatorDictionary = navigators.NavigatorList.ToDictionary(navigator => (long)navigator.Id);
|
||||
}
|
||||
var avatars = await Client.GetFromJsonAsync<Avatar[]>("data/avatar.json");
|
||||
if (avatars != null)
|
||||
{
|
||||
this.avatarDictionary = avatars.ToDictionary(avatar => (long)avatar.Id);
|
||||
}
|
||||
var titles = await Client.GetFromJsonAsync<Title[]>("data/title.json");
|
||||
if (titles != null)
|
||||
{
|
||||
this.titleDictionary = titles.ToDictionary(title => (long)title.Id);
|
||||
}
|
||||
pageLoading = false;
|
||||
}
|
||||
|
||||
private void OnShowDetailsClick(SongPlayData data)
|
||||
{
|
||||
data.ShowDetails = !data.ShowDetails;
|
||||
}
|
||||
|
||||
private async Task SaveOptions()
|
||||
{
|
||||
isSavingOptions = true;
|
||||
var postData = new PlayOption
|
||||
{
|
||||
CardId = CardId,
|
||||
FastSlowIndicator = playOption.FastSlowIndicator,
|
||||
FeverTrance = playOption.FeverTrance,
|
||||
AvatarId = playOption.AvatarId,
|
||||
NavigatorId = playOption.NavigatorId,
|
||||
TitleId = playOption.TitleId
|
||||
};
|
||||
var result = await Client.PostAsJsonAsync("api/UserDetail/SetPlayOption", postData);
|
||||
isSavingOptions = false;
|
||||
}
|
||||
|
||||
private static string CalculateRating(int score)
|
||||
{
|
||||
var grade = SharedConstants.GRADES.Where(g => g.Score <= score).Select(g => g.Grade).Last();
|
||||
return grade;
|
||||
}
|
||||
|
||||
private async Task OnFavoriteToggled(SongPlayData data)
|
||||
{
|
||||
var options = new DialogOptions
|
||||
{
|
||||
CloseOnEscapeKey = false,
|
||||
DisableBackdropClick = true,
|
||||
FullWidth = true
|
||||
};
|
||||
var parameters = new DialogParameters();
|
||||
parameters.Add("Data", data);
|
||||
parameters.Add("CardId", CardId);
|
||||
var dialog = DialogService.Show<FavoriteDialog>("Favorite", parameters, options);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (result.Cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)result.Data)
|
||||
{
|
||||
Logger.LogInformation("Changed!");
|
||||
data.IsFavorite = !data.IsFavorite;
|
||||
}
|
||||
}
|
||||
|
||||
private Task<IEnumerable<long>> SearchAvatar(string value)
|
||||
{
|
||||
var result = string.IsNullOrEmpty(value) ?
|
||||
avatarDictionary.Keys :
|
||||
avatarDictionary.Where(pair => pair.Value.ToString().Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(pair => pair.Key);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private Task<IEnumerable<long>> SearchTitle(string value)
|
||||
{
|
||||
var result = string.IsNullOrEmpty(value) ?
|
||||
titleDictionary.Keys :
|
||||
titleDictionary.Where(pair => pair.Value.ToString().Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(pair => pair.Key);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private Task<IEnumerable<long>> SearchNavigator(string value)
|
||||
{
|
||||
var result = string.IsNullOrEmpty(value) ?
|
||||
navigatorDictionary.Keys :
|
||||
navigatorDictionary.Where(pair => pair.Value.ToString().Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(pair => pair.Key);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private string AvatarIdToString(long id)
|
||||
{
|
||||
return avatarDictionary.ContainsKey(id) ? avatarDictionary[id].ToString() : $"No Data for {id}!";
|
||||
}
|
||||
|
||||
private string NavigatorIdToString(long id)
|
||||
{
|
||||
return navigatorDictionary.ContainsKey(id) ? navigatorDictionary[id].ToString() : $"No Data for {id}!";
|
||||
}
|
||||
|
||||
private string TitleIdToString(long id)
|
||||
{
|
||||
return titleDictionary.ContainsKey(id) ? titleDictionary[id].ToString() : $"No Data for {id}!";
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@
|
||||
}
|
||||
</MudGrid>
|
||||
}
|
||||
else if (!(users.Count == 0))
|
||||
else if (users.Count != 0)
|
||||
{
|
||||
<MudGrid>
|
||||
@foreach (var user in users)
|
||||
|
@ -1,105 +0,0 @@
|
||||
using SharedProject.models;
|
||||
using GenFu;
|
||||
using SharedProject.enums;
|
||||
|
||||
namespace MudAdmin.Utils;
|
||||
|
||||
public class MockDataRepo
|
||||
{
|
||||
private static readonly MockDataRepo INSTANCE = new MockDataRepo();
|
||||
|
||||
public List<User> Users { get; }
|
||||
|
||||
public List<UserDetail> UserDetails { get; private set; } = null!;
|
||||
|
||||
public List<SongPlayData> SongPlayDataList { get; private set; } = null!;
|
||||
|
||||
private MockDataRepo()
|
||||
{
|
||||
ConfigureGenFu();
|
||||
Users = GenFu.GenFu.ListOf<User>(10);
|
||||
GenerateUserDetails();
|
||||
GenerateSongPlayData();
|
||||
}
|
||||
|
||||
private void GenerateSongPlayData()
|
||||
{
|
||||
SongPlayDataList = GenFu.GenFu.ListOf<SongPlayData>();
|
||||
|
||||
foreach (var songPlayData in SongPlayDataList)
|
||||
{
|
||||
var subDataList = new List<SongPlayDetailData>();
|
||||
var random = new Random();
|
||||
|
||||
foreach (var difficulty in Enum.GetValues<Difficulty>())
|
||||
{
|
||||
if (random.Next() <= int.MaxValue / 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var subData = GenFu.GenFu.New<SongPlayDetailData>();
|
||||
subData.Difficulty = difficulty;
|
||||
|
||||
if (subData.ClearState == ClearState.Perfect)
|
||||
{
|
||||
subData.ClearState = ClearState.FullChain;
|
||||
}
|
||||
subDataList.Add(subData);
|
||||
}
|
||||
|
||||
songPlayData.SongPlaySubDataList = subDataList.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void GenerateUserDetails()
|
||||
{
|
||||
UserDetails = new List<UserDetail>();
|
||||
|
||||
foreach (var user in Users)
|
||||
{
|
||||
var detail = GenFu.GenFu.New<UserDetail>();
|
||||
detail.CardId = user.CardId;
|
||||
detail.PlayerName = user.PlayerName;
|
||||
detail.PlayOption = new PlayOption
|
||||
{
|
||||
CardId = user.CardId,
|
||||
FeverTrance = PlayOptions.FeverTranceShow.Show,
|
||||
FastSlowIndicator = PlayOptions.FastSlowIndicator.NotUsed
|
||||
};
|
||||
detail.AverageScore = 900000;
|
||||
detail.TotalScore = 10000000;
|
||||
detail.TotalSongCount = 123;
|
||||
detail.TotalStageCount = 390;
|
||||
UserDetails.Add(detail);
|
||||
}
|
||||
}
|
||||
|
||||
public static MockDataRepo GetMockDataRepo()
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private void ConfigureGenFu()
|
||||
{
|
||||
GenFu.GenFu.Configure<User>()
|
||||
.Fill(user => user.CardId, () => new Random().NextInt64(7000000000000000, 8000000000000000));
|
||||
|
||||
GenFu.GenFu.Configure<UserDetail>()
|
||||
.Fill(detail => detail.PlayedSongCount).WithinRange(100, 123)
|
||||
.Fill(detail => detail.ClearedStageCount).WithinRange(300, 390)
|
||||
.Fill(detail => detail.NoMissStageCount).WithinRange(200, 300)
|
||||
.Fill(detail => detail.FullChainStageCount).WithinRange(100, 200)
|
||||
.Fill(detail => detail.PerfectStageCount).WithinRange(0, 100)
|
||||
.Fill(detail => detail.SAboveStageCount).WithinRange(200, 300)
|
||||
.Fill(detail => detail.SPlusAboveStageCount).WithinRange(100, 200)
|
||||
.Fill(detail => detail.SPlusPlusAboveStageCount).WithinRange(0, 100);
|
||||
|
||||
GenFu.GenFu.Configure<SongPlayDetailData>()
|
||||
.Fill(data => data.Score).WithinRange(0, 1000001);
|
||||
GenFu.GenFu.Configure<SongPlayData>()
|
||||
.Fill(data => data.ShowDetails, false);
|
||||
|
||||
}
|
||||
}
|
2850
MudAdmin/wwwroot/data/avatar.json
Normal file
2850
MudAdmin/wwwroot/data/avatar.json
Normal file
File diff suppressed because it is too large
Load Diff
2483
MudAdmin/wwwroot/data/navigator.json
Normal file
2483
MudAdmin/wwwroot/data/navigator.json
Normal file
File diff suppressed because it is too large
Load Diff
49772
MudAdmin/wwwroot/data/title.json
Normal file
49772
MudAdmin/wwwroot/data/title.json
Normal file
File diff suppressed because it is too large
Load Diff
8
SharedProject/enums/NavigatorDefaultAvailability.cs
Normal file
8
SharedProject/enums/NavigatorDefaultAvailability.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace SharedProject.enums;
|
||||
|
||||
public enum NavigatorDefaultAvailability
|
||||
{
|
||||
NotAvailable = 0,
|
||||
Available = 1,
|
||||
AvailableWithVoice = 2,
|
||||
}
|
11
SharedProject/enums/NavigatorGenre.cs
Normal file
11
SharedProject/enums/NavigatorGenre.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace SharedProject.enums;
|
||||
|
||||
public enum NavigatorGenre
|
||||
{
|
||||
Default = 1,
|
||||
Original = 2,
|
||||
Game = 3,
|
||||
Touhou = 4,
|
||||
Vocaloid = 5,
|
||||
Collab = 6,
|
||||
}
|
36
SharedProject/enums/TitleUnlockType.cs
Normal file
36
SharedProject/enums/TitleUnlockType.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace SharedProject.enums;
|
||||
|
||||
public enum TitleUnlockType
|
||||
{
|
||||
Invalid = 0,
|
||||
Default = 1,
|
||||
Clear = 2,
|
||||
NoMiss = 3,
|
||||
FullChain = 4,
|
||||
SRankSimpleStages = 5,
|
||||
SRankNormalStages = 6,
|
||||
SRankHardStages = 7,
|
||||
SRankExtraStages = 8,
|
||||
SRankAllDifficulties = 9,
|
||||
SPlusRankAllDifficulties = 10,
|
||||
SPlusPlusRankAllDifficulties = 11,
|
||||
Event = 12,
|
||||
Prefecture = 13,
|
||||
ChainMilestone = 14,
|
||||
Adlibs = 15,
|
||||
ConsecutiveNoMiss = 16,
|
||||
ClearsUsingItems = 17,
|
||||
Avatars = 18,
|
||||
MultiplayerStarsTotal = 19,
|
||||
SongSet20 = 20,
|
||||
SongSet21 = 21,
|
||||
SongSet22 = 22,
|
||||
SongSet23 = 23,
|
||||
SongSet24 = 24,
|
||||
SongSet25 = 25,
|
||||
SongSet26 = 26,
|
||||
ProfileLevel = 27,
|
||||
Perfect = 28,
|
||||
OnlineMatching = 29,
|
||||
Trophies = 30,
|
||||
}
|
15
SharedProject/models/Avatar.cs
Normal file
15
SharedProject/models/Avatar.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class Avatar
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
public string? IdString { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Variant { get; set; }
|
||||
public string? AcquireMethod { get; set; }
|
||||
|
||||
public override string ToString() {
|
||||
return $"{Id}: {FullName}, {AcquireMethod}";
|
||||
}
|
||||
}
|
12
SharedProject/models/NameEntry.cs
Normal file
12
SharedProject/models/NameEntry.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class NameEntry
|
||||
{
|
||||
public string? NameWithVariant { get; set; }
|
||||
public string? NameWithoutVariant{ get; set; }
|
||||
public string? Variant{ get; set; }
|
||||
public string? IllustrationCredit{ get; set; }
|
||||
public override string ToString() {
|
||||
return $"{NameWithVariant}";
|
||||
}
|
||||
}
|
25
SharedProject/models/Navigator.cs
Normal file
25
SharedProject/models/Navigator.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using SharedProject.enums;
|
||||
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class Navigator
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
|
||||
public string? IdString { get; set; }
|
||||
|
||||
public string? FileName { get; set; }
|
||||
|
||||
public NameEntry? NameEntry0 { get; set; }
|
||||
public NameEntry? NameEntry1 { get; set; }
|
||||
|
||||
public NavigatorGenre Genre { get; set; }
|
||||
|
||||
public NavigatorDefaultAvailability DefaultAvailability { get; set; }
|
||||
|
||||
public string? ToolTipJp { get; set; }
|
||||
public string? ToolTipEn { get; set; }
|
||||
public override string ToString() {
|
||||
return $"{Id}: {NameEntry1}, {ToolTipEn}";
|
||||
}
|
||||
}
|
8
SharedProject/models/Navigators.cs
Normal file
8
SharedProject/models/Navigators.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class Navigators
|
||||
{
|
||||
public int Count { get; set; }
|
||||
|
||||
public List<Navigator>? NavigatorList { get; set; }
|
||||
}
|
@ -13,4 +13,13 @@ public class PlayOption
|
||||
|
||||
[JsonPropertyName(nameof(FeverTrance))]
|
||||
public PlayOptions.FeverTranceShow FeverTrance { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(AvatarId))]
|
||||
public long AvatarId { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(NavigatorId))]
|
||||
public long NavigatorId { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(TitleId))]
|
||||
public long TitleId { get; set; }
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using SharedProject.common;
|
||||
using SharedProject.enums;
|
||||
|
||||
namespace SharedProject.models;
|
||||
|
||||
@ -25,4 +26,15 @@ public class SongPlayData
|
||||
return SongPlaySubDataList.Sum(data => data.PlayCount);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public DateTime LastPlayTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var songPlayDetailData = SongPlaySubDataList.Where(data => data.ClearState != ClearState.NotPlayed)
|
||||
.MinBy(data => data.LastPlayTime);
|
||||
return songPlayDetailData?.LastPlayTime ?? DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,4 +13,6 @@ public class SongPlayDetailData
|
||||
public Difficulty Difficulty { get; set; }
|
||||
|
||||
public ClearState ClearState { get; set; }
|
||||
|
||||
public DateTime LastPlayTime { get; set; }
|
||||
}
|
17
SharedProject/models/Title.cs
Normal file
17
SharedProject/models/Title.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using SharedProject.enums;
|
||||
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class Title
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
public string? IdString { get; set; }
|
||||
public string? NameJp { get; set; }
|
||||
public string? NameEng { get; set; }
|
||||
public string? UnlockRequirementJp { get; set; }
|
||||
public string? UnlockRequirementEng { get; set; }
|
||||
public TitleUnlockType Type { get; set; }
|
||||
public override string ToString() {
|
||||
return $"{Id}: {NameEng}, {UnlockRequirementEng}";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user