Change avatar/navigator/title select method

Add the function to change player name
This commit is contained in:
jiych1
2022-07-07 21:46:17 +08:00
parent 4cd290aedc
commit 14261c8e29
19 changed files with 615 additions and 246 deletions
@@ -39,6 +39,20 @@ public class ApiController : WebApiController
return result;
}
[Route(HttpVerbs.Post, "/Users/SetPlayerName")]
public bool SetPlayerName([JsonData] User data)
{
var existing = cardSqLiteConnection.Table<Card>().Where(card => card.CardId == data.CardId);
if (!existing.Any())
{
$"Trying to update non existing user's name! Card id {data.CardId}".Warn();
return false;
}
var user = existing.First();
user.PlayerName = data.PlayerName;
return cardSqLiteConnection.Update(user) == 1;
}
[Route(HttpVerbs.Post, "/UserDetail/SetMusicFavorite")]
// ReSharper disable once UnusedMember.Global
public bool SetFavorite([JsonData] MusicFavoriteData data)
+13
View File
@@ -19,6 +19,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.4" PrivateAssets="all" />
<PackageReference Include="MudBlazor" Version="6.0.11-dev.4" />
<PackageReference Include="protobuf-net" Version="3.1.17" />
</ItemGroup>
<ItemGroup>
@@ -30,6 +31,18 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\data\avatar.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\data\navigator.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\data\title.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\news.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
@@ -0,0 +1,73 @@
@using SharedProject.common
@using System.Text.RegularExpressions
@inject HttpClient Client
@inject ILogger<ChangePlayerNameDialog> Logger
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">
Change Player Name
</MudText>
</TitleContent>
<DialogContent>
<MudForm @bind-IsValid="IsValid">
<MudTextField Value="@Data.CardId" Label="CardId" ReadOnly="true"/>
<MudTextField @bind-Value="Data.PlayerName"
Immediate="true"
Counter="SharedConstants.MAX_PLAYER_NAME_LENGTH"
MaxLength="SharedConstants.MAX_PLAYER_NAME_LENGTH"
Validation="ValidatePlayerName"/>
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit" Disabled="@(!IsValid)">Confirm</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public SharedProject.models.User Data { get; set; } = null!;
private string originalName = string.Empty;
private bool IsValid { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
originalName = new string(Data.PlayerName);
}
async Task Submit()
{
Logger.LogInformation("Data is {cardId}, {name}", Data.CardId, Data.PlayerName);
var response = await Client.PostAsJsonAsync("api/Users/SetPlayerName", Data);
var result = await response.Content.ReadFromJsonAsync<bool>();
Logger.LogInformation("SetPlayerName result is {result}", result);
MudDialog.Close(DialogResult.Ok(result));
}
void Cancel()
{
Data.PlayerName = originalName;
MudDialog.Cancel();
}
private static string? ValidatePlayerName(string playerName)
{
const string pattern = @"^[a-zA-Z0-9!?,./\-+:<>_\\@*#&=() ]{1,8}$";
return playerName.Length switch
{
0 => "Player name cannot be empty!",
> 8 => "Player name cannot be longer than 8 characters!",
_ => !Regex.IsMatch(playerName, pattern) ? "Player name contains invalid character!" : null
};
}
}
+1 -1
View File
@@ -38,7 +38,7 @@
[Parameter]
public long CardId { get; set; }
async void Submit()
async Task Submit()
{
var postData = new MusicFavoriteData
{
+291 -192
View File
@@ -4,203 +4,302 @@
<PageTitle>User</PageTitle>
<MudContainer>
@if (pageLoading)
@if (pageLoading)
{
<MudSkeleton Width="1184px" Height="57px"/>
<MudSkeleton Width="1184px" Height="57px"/>
<MudSkeleton Width="1184px" Height="57px"/>
}
else
{
if (userDetail is null)
{
<MudSkeleton Width="1184px" Height="57px"/>
<MudSkeleton Width="1184px" Height="57px"/>
<MudSkeleton Width="1184px" Height="57px"/>
<MudText Typo="Typo.h3">No Data</MudText>
}
else
{
if (userDetail is null)
{
<MudText Typo="Typo.h3">No Data</MudText>
}
else
{
<MudExpansionPanels>
<MudExpansionPanel Text="Total Result">
<MudList>
<MudListSubheader>Player Name: @userDetail.PlayerName</MudListSubheader>
<MudListItem>Total Score: @userDetail.TotalScore</MudListItem>
<MudListItem>Average Score: @userDetail.AverageScore</MudListItem>
<MudListItem>Played Song Count: @userDetail.PlayedSongCount / @userDetail.TotalSongCount</MudListItem>
<MudListItem>Cleared Stage Count: @userDetail.ClearedStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>No Miss Stage Count: @userDetail.NoMissStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>Full Chain Stage Count: @userDetail.FullChainStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>Perfect Stage Count: @userDetail.PerfectStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S and Above Stage Count: @userDetail.SAboveStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S+ and Above Stage Count: @userDetail.SPlusAboveStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S++ and Above Stage Count: @userDetail.SPlusPlusAboveStageCount / @userDetail.TotalStageCount</MudListItem>
</MudList>
</MudExpansionPanel>
<MudExpansionPanel Text="PlayOptions">
<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>
<MudExpansionPanels>
<MudExpansionPanel Text="Total Result">
<MudList>
<MudListSubheader>Player Name: @userDetail.PlayerName</MudListSubheader>
<MudListItem>Total Score: @userDetail.TotalScore</MudListItem>
<MudListItem>Average Score: @userDetail.AverageScore</MudListItem>
<MudListItem>Played Song Count: @userDetail.PlayedSongCount / @userDetail.TotalSongCount</MudListItem>
<MudListItem>Cleared Stage Count: @userDetail.ClearedStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>No Miss Stage Count: @userDetail.NoMissStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>Full Chain Stage Count: @userDetail.FullChainStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>Perfect Stage Count: @userDetail.PerfectStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S and Above Stage Count: @userDetail.SAboveStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S+ and Above Stage Count: @userDetail.SPlusAboveStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S++ and Above Stage Count: @userDetail.SPlusPlusAboveStageCount / @userDetail.TotalStageCount</MudListItem>
</MudList>
</MudExpansionPanel>
<MudExpansionPanel Text="PlayOptions">
<MudStack>
<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="@playOption.FeverTrance"
Label="FEVER/TRANCE show setting">
@foreach (var item in Enum.GetValues<PlayOptions.FeverTranceShow>())
{
<MudSelectItem Value="@item">@item.GetHelpText()</MudSelectItem>
}
</MudSelect>
<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"/>
<MudText Class="ms-2">Saving...</MudText>
}
else
{
<MudIcon Icon="@Icons.Filled.Save"></MudIcon>
<MudText>Save</MudText>
}
<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>
<MudStack Row="true">
<MudField Label="Navigator setting">
@NavigatorIdToString(playOption.NavigatorId)
</MudField>
<MudButton Variant="Variant.Text"
OnClick="OnChangeNavigatorButtonClick">
Change Navigator
</MudButton>
</MudExpansionPanel>
<MudExpansionPanel Text="SongPlayData">
<MudDataGrid T="SongPlayData"
Items="@songPlayDataList"
Sortable="true"
Filterable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Played Songs</MudText>
</ToolBarContent>
<Columns>
<Column T="SongPlayData" Sortable="false" Filterable="false">
<CellTemplate>
<MudButton Variant="Variant.Outlined" Size="Size.Small"
OnClick="@(() => OnShowDetailsClick(context.Item))">
@(context.Item.ShowDetails ? "Hide" : "Show") Song Play Details
</MudButton>
</CellTemplate>
</Column>
<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"/>
</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="LastPlayTime" Title="Last Play Time"/>
</Columns>
<ChildRowContent>
@if (context.ShowDetails)
{
<MudTr>
<td colspan="5">
<MudCard Elevation="0">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.body1">Song Play Details</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent Class="pa-0">
<MudTable Items="@context.SongPlaySubDataList"
Context="SongPlayDetail"
Elevation="0"
Filter="data => data.ClearState != ClearState.NotPlayed">
<HeaderContent>
<MudTh>Difficulty</MudTh>
<MudTh>Clear State</MudTh>
<MudTh>Play Count</MudTh>
<MudTh>Rating</MudTh>
<MudTh>Score</MudTh>
<MudTh>Max Chain</MudTh>
<MudTh>Last Play Time</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Difficulty">@SongPlayDetail.Difficulty</MudTd>
<MudTd DataLabel="Clear State">
@(SongPlayDetail.Score != 1000000 ? SongPlayDetail.ClearState : ClearState.Perfect)
</MudTd>
<MudTd DataLabel="Play Count">@SongPlayDetail.PlayCount</MudTd>
<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>
</MudCard>
</td>
</MudTr>
}
</ChildRowContent>
<PagerContent>
<MudDataGridPager T="SongPlayData"/>
</PagerContent>
</MudDataGrid>
</MudExpansionPanel>
</MudExpansionPanels>
}
</MudStack>
<MudStack Row="true">
<MudField Label="Avatar setting">
@AvatarIdToString(playOption.AvatarId)
</MudField>
<MudButton Variant="Variant.Text"
OnClick="OnChangeAvatarButtonClick">
Change Avatar
</MudButton>
</MudStack>
<MudStack Row="true">
<MudField Label="Title setting">
@TitleIdToString(playOption.TitleId)
</MudField>
<MudButton Variant="Variant.Text"
OnClick="OnChangeTitleButtonClick">
Change Title
</MudButton>
</MudStack>
<MudOverlay @bind-Visible="IsNavigatorOverlayVisible"
DarkBackground="true">
<MudPaper>
<MudContainer MaxWidth="MaxWidth.Large">
<MudStack>
<MudDataGrid T="Navigator"
MultiSelection="false" @bind-SelectedItem="SelectedNavigator"
FixedHeader="true" FixedFooter="true" Height="70vh"
Items="navigatorDictionary.Values" ReadOnly="true" Hover="true"
Sortable="true" Filterable="true" Hideable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Navigators</MudText>
<MudSpacer/>
</ToolBarContent>
<Columns>
<Column T="Navigator" Field="@nameof(Navigator.Id)"/>
<Column T="Navigator" Field="@nameof(Navigator.IdString)"/>
<Column T="Navigator" Field="@nameof(Navigator.NameEntry0)" Title="Navigator Name"
SortBy="NameEntrySortByFunc"/>
<Column T="Navigator" Field="@nameof(Navigator.ToolTipJp)" Title="Navigator Description (Japanese)"/>
<Column T="Navigator" Field="@nameof(Navigator.ToolTipEn)" Title="Navigator Description (English)"/>
<Column T="Navigator" Field="@nameof(Navigator.Genre)" Title="Navigator Type"/>
</Columns>
<PagerContent>
<MudDataGridPager T="Navigator"/>
</PagerContent>
</MudDataGrid>
<MudText Align="Align.Left">
Currently selected navigator:<br>
@SelectedNavigator
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
OnClick="OnNavigatorOverlayClosed">
Close
</MudButton>
</MudStack>
</MudContainer>
</MudPaper>
</MudOverlay>
<MudOverlay @bind-Visible="IsAvatarOverlayVisible"
DarkBackground="true">
<MudPaper>
<MudContainer MaxWidth="MaxWidth.Large">
<MudStack>
<MudDataGrid T="Avatar"
MultiSelection="false" @bind-SelectedItem="SelectedAvatar"
FixedHeader="true" FixedFooter="true" Height="70vh"
Items="avatarDictionary.Values" ReadOnly="true" Hover="true"
Sortable="true" Filterable="true" Hideable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Avatars</MudText>
<MudSpacer/>
</ToolBarContent>
<Columns>
<Column T="Avatar" Field="@nameof(Avatar.Id)"/>
<Column T="Avatar" Field="@nameof(Avatar.IdString)"/>
<Column T="Avatar" Field="@nameof(Avatar.FullName)" Title="Avatar Name"/>
<Column T="Avatar" Field="@nameof(Avatar.AcquireMethodJp)" Title="Acquire Method (Japanese)"/>
<Column T="Avatar" Field="@nameof(Avatar.AcquireMethodEn)" Title="Acquire Method (English)"/>
</Columns>
<PagerContent>
<MudDataGridPager T="Avatar"/>
</PagerContent>
</MudDataGrid>
<MudText Align="Align.Left">
Currently selected avatar:<br>
@SelectedAvatar
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
OnClick="OnAvatarOverlayClosed">
Close
</MudButton>
</MudStack>
</MudContainer>
</MudPaper>
</MudOverlay>
<MudOverlay @bind-Visible="IsTitleOverlayVisible"
DarkBackground="true">
<MudPaper>
<MudContainer MaxWidth="MaxWidth.Large">
<MudStack>
<MudDataGrid T="Title"
MultiSelection="false" @bind-SelectedItem="SelectedTitle"
FixedHeader="true" FixedFooter="true" Height="70vh"
Items="titleDictionary.Values" ReadOnly="true" Hover="true"
Sortable="true" Filterable="true" Hideable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Titles</MudText>
<MudSpacer/>
</ToolBarContent>
<Columns>
<Column T="Title" Field="@nameof(Title.Id)"/>
<Column T="Title" Field="@nameof(Title.IdString)"/>
<Column T="Title" Field="@nameof(Title.NameJp)" Title="Title Name"/>
<Column T="Title" Field="@nameof(Title.UnlockRequirementJp)" Title="Unlock Requirement (Japanese)"/>
<Column T="Title" Field="@nameof(Title.UnlockRequirementEng)" Title="Unlock Requirement (English)"/>
<Column T="Title" Field="@nameof(Title.Type)" Title="Title Type"/>
</Columns>
<PagerContent>
<MudDataGridPager T="Title"/>
</PagerContent>
</MudDataGrid>
<MudText Align="Align.Left">
Currently selected title:<br>
@SelectedTitle
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
OnClick="OnTitleOverlayClosed">
Close
</MudButton>
</MudStack>
</MudContainer>
</MudPaper>
</MudOverlay>
<MudButton Disabled="@isSavingOptions"
OnClick="SaveOptions"
Variant="Variant.Filled"
Color="Color.Info">
@if (isSavingOptions)
{
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
<MudText Class="ms-2">Saving...</MudText>
}
else
{
<MudIcon Icon="@Icons.Filled.Save"></MudIcon>
<MudText>Save</MudText>
}
</MudButton>
</MudStack>
</MudExpansionPanel>
<MudExpansionPanel Text="SongPlayData">
<MudDataGrid T="SongPlayData"
Items="@songPlayDataList"
Sortable="true"
Filterable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Played Songs</MudText>
</ToolBarContent>
<Columns>
<Column T="SongPlayData" Sortable="false" Filterable="false">
<CellTemplate>
<MudButton Variant="Variant.Outlined" Size="Size.Small"
OnClick="@(() => OnShowDetailsClick(context.Item))">
@(context.Item.ShowDetails ? "Hide" : "Show") Song Play Details
</MudButton>
</CellTemplate>
</Column>
<Column T="SongPlayData" Field="@nameof(SongPlayData.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"/>
</CellTemplate>
</Column>
<Column T="SongPlayData" Field="@nameof(SongPlayData.Title)" Title="Song Title"/>
<Column T="SongPlayData" Field="@nameof(SongPlayData.Artist)" Title="Artist"/>
<Column T="SongPlayData" Field="@nameof(SongPlayData.TotalPlayCount)" Title="Total Play Count"/>
<Column T="SongPlayData" Field="@nameof(SongPlayData.LastPlayTime)" Title="Last Play Time"/>
</Columns>
<ChildRowContent>
@if (context.ShowDetails)
{
<MudTr>
<td colspan="5">
<MudCard Elevation="0">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.body1">Song Play Details</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent Class="pa-0">
<MudTable Items="@context.SongPlaySubDataList"
Context="SongPlayDetail"
Elevation="0"
Filter="data => data.ClearState != ClearState.NotPlayed">
<HeaderContent>
<MudTh>Difficulty</MudTh>
<MudTh>Clear State</MudTh>
<MudTh>Play Count</MudTh>
<MudTh>Rating</MudTh>
<MudTh>Score</MudTh>
<MudTh>Max Chain</MudTh>
<MudTh>Last Play Time</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Difficulty">@SongPlayDetail.Difficulty</MudTd>
<MudTd DataLabel="Clear State">
@(SongPlayDetail.Score != 1000000 ? SongPlayDetail.ClearState : ClearState.Perfect)
</MudTd>
<MudTd DataLabel="Play Count">@SongPlayDetail.PlayCount</MudTd>
<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>
</MudCard>
</td>
</MudTr>
}
</ChildRowContent>
<PagerContent>
<MudDataGridPager T="SongPlayData"/>
</PagerContent>
</MudDataGrid>
</MudExpansionPanel>
</MudExpansionPanels>
}
}
</MudContainer>
+100 -35
View File
@@ -1,6 +1,7 @@
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using ProtoBuf;
using SharedProject.common;
using SharedProject.models;
@@ -8,7 +9,6 @@ namespace MudAdmin.Pages;
public partial class User
{
[Inject]
public HttpClient Client { get; set; } = null!;
@@ -17,28 +17,38 @@ public partial class User
[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 Dictionary<long, Navigator> navigatorDictionary = new();
private bool pageLoading = true;
private PlayOption playOption = new();
private List<SongPlayData> songPlayDataList = new();
private Dictionary<long, Title> titleDictionary = new();
private UserDetail? userDetail;
[Parameter]
public long CardId { get; set; }
private bool IsTitleOverlayVisible { get; set; }
private bool IsNavigatorOverlayVisible { get; set; }
private bool IsAvatarOverlayVisible { get; set; }
private Title? SelectedTitle { get; set; }
private Avatar? SelectedAvatar { get; set; }
private Navigator? SelectedNavigator { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
@@ -51,24 +61,38 @@ public partial class User
songPlayDataList = userDetail.SongPlayDataList ?? new List<SongPlayData>();
playOption = userDetail.PlayOption;
var navigators = await Client.GetFromJsonAsync<Navigators>("data/navigator.json");
var navigatorStream = await Client.GetStreamAsync(SharedConstants.NAVIGATOR_DAT_URI);
var navigators = Serializer.Deserialize<Navigators>(navigatorStream);
if (navigators?.NavigatorList != null)
{
this.navigatorDictionary = navigators.NavigatorList.ToDictionary(navigator => (long)navigator.Id);
navigatorDictionary = navigators.NavigatorList.ToDictionary(navigator => (long)navigator.Id);
}
var avatars = await Client.GetFromJsonAsync<Avatar[]>("data/avatar.json");
var avatarStream = await Client.GetStreamAsync(SharedConstants.AVATAR_DAT_URI);
var avatars = Serializer.Deserialize<List<Avatar>>(avatarStream);
if (avatars != null)
{
this.avatarDictionary = avatars.ToDictionary(avatar => (long)avatar.Id);
avatarDictionary = avatars.ToDictionary(avatar => (long)avatar.Id);
}
var titles = await Client.GetFromJsonAsync<Title[]>("data/title.json");
var titleStream = await Client.GetStreamAsync(SharedConstants.TITLE_DAT_URI);
var titles = Serializer.Deserialize<List<Title>>(titleStream);
if (titles != null)
{
this.titleDictionary = titles.ToDictionary(title => (long)title.Id);
titleDictionary = titles.ToDictionary(title => (long)title.Id);
}
SetSelected();
pageLoading = false;
}
private void SetSelected()
{
SelectedTitle = titleDictionary.GetValueOrDefault(playOption.TitleId);
SelectedAvatar = avatarDictionary.GetValueOrDefault(playOption.AvatarId);
SelectedNavigator = navigatorDictionary.GetValueOrDefault(playOption.NavigatorId);
}
private void OnShowDetailsClick(SongPlayData data)
{
data.ShowDetails = !data.ShowDetails;
@@ -92,7 +116,7 @@ public partial class User
private static string CalculateRating(int score)
{
var grade = SharedConstants.GRADES.Where(g => g.Score <= score).Select(g => g.Grade).Last();
var grade = SharedConstants.Grades.Where(g => g.Score <= score).Select(g => g.Grade).Last();
return grade;
}
@@ -124,25 +148,19 @@ public partial class User
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);
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);
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);
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);
}
@@ -150,14 +168,61 @@ public partial class User
{
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}!";
}
private void OnChangeTitleButtonClick()
{
IsTitleOverlayVisible = true;
}
private void OnChangeNavigatorButtonClick()
{
IsNavigatorOverlayVisible = true;
}
private void OnChangeAvatarButtonClick()
{
IsAvatarOverlayVisible = true;
}
private void OnTitleOverlayClosed()
{
IsTitleOverlayVisible = false;
if (SelectedTitle != null)
{
playOption.TitleId = SelectedTitle.Id;
}
}
private void OnNavigatorOverlayClosed()
{
IsNavigatorOverlayVisible = false;
if (SelectedNavigator != null)
{
playOption.NavigatorId = SelectedNavigator.Id;
}
}
private void OnAvatarOverlayClosed()
{
IsAvatarOverlayVisible = false;
if (SelectedAvatar != null)
{
playOption.AvatarId = SelectedAvatar.Id;
}
}
private static object NameEntrySortByFunc(Navigator navigator)
{
return navigator.NameEntry1?.ToString().ToLowerInvariant() ?? string.Empty;
}
}
+22 -2
View File
@@ -2,6 +2,8 @@
@using models = SharedProject.models
@inject NavigationManager NavigationManager
@inject HttpClient Client
@inject IDialogService DialogService
@inject ILogger<Users> Logger
<PageTitle>Users</PageTitle>
@@ -41,13 +43,17 @@
<CardHeaderContent>
<MudText Typo="Typo.h5">@user.PlayerName</MudText>
</CardHeaderContent>
<CardHeaderActions>
<MudIconButton Icon="@Icons.Filled.Edit" Color="Color.Default"
OnClick="() => OnEditPlayerNameClicked(user)"/>
</CardHeaderActions>
</MudCardHeader>
<MudCardContent>
<MudText Typo="Typo.h6">Card ID</MudText>
<MudText>@user.CardId</MudText>
</MudCardContent>
<MudCardActions>
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="() => OnClick(user)">Check detail</MudButton>
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="() => OnCheckDetailClick(user)">Check detail</MudButton>
</MudCardActions>
</MudCard>
</MudItem>
@@ -70,9 +76,23 @@
users = await Client.GetFromJsonAsync<List<models.User>>("api/Users") ?? new List<models.User>();
}
private void OnClick(models.User user)
private void OnCheckDetailClick(models.User user)
{
NavigationManager.NavigateTo($"user/{user.CardId}");
}
private async Task OnEditPlayerNameClicked(models.User user)
{
var options = new DialogOptions
{
CloseOnEscapeKey = false,
DisableBackdropClick = true,
FullWidth = true
};
var parameters = new DialogParameters();
parameters.Add("Data", user);
var dialog = DialogService.Show<ChangePlayerNameDialog>("Favorite", parameters, options);
var result = await dialog.Result;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<MudSnackbarProvider/>
<MudLayout>
<MudAppBar Elevation="0">
<MudAppBar Elevation="0" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start"
OnClick="@((e) => DrawerToggle())"/>
<MudSpacer/>
+4
View File
@@ -7,4 +7,8 @@
<RootNamespace>SharedProject</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="3.1.17" />
</ItemGroup>
</Project>
+9 -1
View File
@@ -20,7 +20,15 @@ public static class SharedConstants
public const int S_PLUS_PLUS_SCORE_THRESHOLD = 990000;
public static readonly ScoreGradeMap[] GRADES =
public const int MAX_PLAYER_NAME_LENGTH = 8;
public const string NAVIGATOR_DAT_URI = "data/navigator.dat";
public const string AVATAR_DAT_URI = "data/avatar.dat";
public const string TITLE_DAT_URI = "data/title.dat";
public static readonly ScoreGradeMap[] Grades =
{
new(E_SCORE_THRESHOLD, "E"),
new(D_SCORE_THRESHOLD, "D"),
@@ -1,5 +1,8 @@
namespace SharedProject.enums;
using ProtoBuf;
namespace SharedProject.enums;
[ProtoContract]
public enum NavigatorDefaultAvailability
{
NotAvailable = 0,
+4 -1
View File
@@ -1,5 +1,8 @@
namespace SharedProject.enums;
using ProtoBuf;
namespace SharedProject.enums;
[ProtoContract]
public enum NavigatorGenre
{
Default = 1,
+4 -1
View File
@@ -1,5 +1,8 @@
namespace SharedProject.enums;
using ProtoBuf;
namespace SharedProject.enums;
[ProtoContract]
public enum TitleUnlockType
{
Invalid = 0,
+20 -3
View File
@@ -1,15 +1,32 @@
namespace SharedProject.models;
using ProtoBuf;
namespace SharedProject.models;
[ProtoContract]
public class Avatar
{
[ProtoMember(1)]
public uint Id { get; set; }
[ProtoMember(2)]
public string? IdString { get; set; }
[ProtoMember(3)]
public string? FullName { get; set; }
[ProtoMember(4)]
public string? Name { get; set; }
[ProtoMember(5)]
public string? Variant { get; set; }
public string? AcquireMethod { get; set; }
[ProtoMember(6)]
public string? AcquireMethodJp { get; set; }
[ProtoMember(7)]
public string? AcquireMethodEn { get; set; }
public override string ToString() {
return $"{Id}: {FullName}, {AcquireMethod}";
return $"{Id}: {FullName}, {AcquireMethodEn}";
}
}
+11 -3
View File
@@ -1,12 +1,20 @@
namespace SharedProject.models;
using ProtoBuf;
namespace SharedProject.models;
[ProtoContract]
public class NameEntry
{
[ProtoMember(1)]
public string? NameWithVariant { get; set; }
[ProtoMember(2)]
public string? NameWithoutVariant{ get; set; }
[ProtoMember(3)]
public string? Variant{ get; set; }
[ProtoMember(4)]
public string? IllustrationCredit{ get; set; }
public override string ToString() {
return $"{NameWithVariant}";
public override string ToString()
{
return $"{NameWithVariant}, {IllustrationCredit}";
}
}
+17 -2
View File
@@ -1,25 +1,40 @@
using SharedProject.enums;
using ProtoBuf;
using SharedProject.enums;
namespace SharedProject.models;
[ProtoContract]
public class Navigator
{
[ProtoMember(1)]
public uint Id { get; set; }
[ProtoMember(2)]
public string? IdString { get; set; }
[ProtoMember(3)]
public string? FileName { get; set; }
[ProtoMember(4)]
public NameEntry? NameEntry0 { get; set; }
[ProtoMember(5)]
public NameEntry? NameEntry1 { get; set; }
[ProtoMember(6)]
public NavigatorGenre Genre { get; set; }
[ProtoMember(7)]
public NavigatorDefaultAvailability DefaultAvailability { get; set; }
[ProtoMember(8)]
public string? ToolTipJp { get; set; }
[ProtoMember(9)]
public string? ToolTipEn { get; set; }
public override string ToString() {
return $"{Id}: {NameEntry1}, {ToolTipEn}";
return $"{Id}: {NameEntry1?.NameWithVariant}, {NameEntry1?.IllustrationCredit}";
}
}
+6 -1
View File
@@ -1,8 +1,13 @@
namespace SharedProject.models;
using ProtoBuf;
namespace SharedProject.models;
[ProtoContract]
public class Navigators
{
[ProtoMember(1)]
public int Count { get; set; }
[ProtoMember(2)]
public List<Navigator>? NavigatorList { get; set; }
}
+16 -1
View File
@@ -1,15 +1,30 @@
using SharedProject.enums;
using ProtoBuf;
using SharedProject.enums;
namespace SharedProject.models;
[ProtoContract]
public class Title
{
[ProtoMember(1)]
public uint Id { get; set; }
[ProtoMember(2)]
public string? IdString { get; set; }
[ProtoMember(3)]
public string? NameJp { get; set; }
[ProtoMember(4)]
public string? NameEng { get; set; }
[ProtoMember(5)]
public string? UnlockRequirementJp { get; set; }
[ProtoMember(6)]
public string? UnlockRequirementEng { get; set; }
[ProtoMember(7)]
public TitleUnlockType Type { get; set; }
public override string ToString() {
return $"{Id}: {NameEng}, {UnlockRequirementEng}";
+5 -1
View File
@@ -1,10 +1,14 @@
namespace SharedProject.models;
using System.Text.Json.Serialization;
namespace SharedProject.models;
public class User
{
// ReSharper disable once UnusedAutoPropertyAccessor.Global
[JsonPropertyName(nameof(CardId))]
public long CardId { get; set; }
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
[JsonPropertyName(nameof(PlayerName))]
public string PlayerName { get; set; } = string.Empty;
}