Update web interface
This commit is contained in:
parent
4871c0bf6f
commit
d3265dbc02
41
GC-local-server-rewrite/backports/JsonDataAttribute.cs
Normal file
41
GC-local-server-rewrite/backports/JsonDataAttribute.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EmbedIO.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Specifies that a parameter of a controller method will receive
|
||||
/// an object obtained by deserializing the request body as JSON.</para>
|
||||
/// <para>The received object will be <see langword="null"/>
|
||||
/// only if the deserialized object is <c>null</c>.</para>
|
||||
/// <para>If the request body is not valid JSON,
|
||||
/// or if it cannot be deserialized to the type of the parameter,
|
||||
/// a <c>400 Bad Request</c> response will be sent to the client.</para>
|
||||
/// <para>This class cannot be inherited.</para>
|
||||
/// </summary>
|
||||
/// <seealso cref="Attribute" />
|
||||
/// <seealso cref="IRequestDataAttribute{TController}" />
|
||||
[AttributeUsage(AttributeTargets.Parameter)]
|
||||
public class JsonDataAttribute : Attribute, IRequestDataAttribute<WebApiController>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<object?> GetRequestDataAsync(WebApiController controller, Type type, string parameterName)
|
||||
{
|
||||
string body;
|
||||
|
||||
using (var reader = controller.HttpContext.OpenRequestText())
|
||||
{
|
||||
body = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Swan.Formatters.Json.Deserialize(body, type);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
throw HttpException.BadRequest($"Expected request body to be deserializable to {type.FullName}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -122,6 +122,16 @@ public static class Configs
|
||||
|
||||
public const string RANK_STATUS_XPATH = $"{ROOT_XPATH}/ranking_status";
|
||||
|
||||
public const int CONFIG_PCOL1 = 0;
|
||||
public const int CONFIG_PCOL2 = 0;
|
||||
public const int CONFIG_PCOL3 = 0;
|
||||
|
||||
public const int FAVORITE_PCOL1 = 10;
|
||||
|
||||
public const int COUNT_PCOL1 = 20;
|
||||
|
||||
public const int SCORE_PCOL1 = 21;
|
||||
|
||||
public static readonly List<string> DOMAINS = new()
|
||||
{
|
||||
"localhost",
|
||||
|
@ -1,5 +1,4 @@
|
||||
using ChoETL;
|
||||
using EmbedIO;
|
||||
using EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
using GCLocalServerRewrite.common;
|
||||
@ -39,6 +38,54 @@ public class ApiController : WebApiController
|
||||
return result;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Post, "/UserDetail/SetMusicFavorite")]
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public bool SetFavorite([JsonData] MusicFavoriteData data)
|
||||
{
|
||||
var existing = cardSqLiteConnection.Table<CardDetail>()
|
||||
.Where(detail => detail.CardId == data.CardId
|
||||
&& detail.Pcol1 == Configs.FAVORITE_PCOL1
|
||||
&& detail.Pcol2 == data.MusicId);
|
||||
|
||||
if (!existing.Any())
|
||||
{
|
||||
$"Trying to update non existing song's favorite! Card id {data.CardId}, music id {data.MusicId}".Warn();
|
||||
return false;
|
||||
}
|
||||
|
||||
var cardDetail = existing.First();
|
||||
cardDetail.Fcol1 = data.IsFavorite ? 1 : 0;
|
||||
var result = cardSqLiteConnection.Update(cardDetail);
|
||||
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Post, "/UserDetail/SetPlayOption")]
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public bool SetPlayOption([JsonData] PlayOption data)
|
||||
{
|
||||
var existing = cardSqLiteConnection.Table<CardDetail>()
|
||||
.Where(detail => detail.CardId == data.CardId
|
||||
&& detail.Pcol1 == Configs.CONFIG_PCOL1
|
||||
&& detail.Pcol2 == Configs.CONFIG_PCOL2
|
||||
&& detail.Pcol3 == Configs.CONFIG_PCOL3);
|
||||
|
||||
if (!existing.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 result = cardSqLiteConnection.Update(cardDetail);
|
||||
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, "/UserDetail/{cardId}")]
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public UserDetail? GetUserDetail(long cardId)
|
||||
@ -47,6 +94,7 @@ public class ApiController : WebApiController
|
||||
|
||||
if (!cardResult.Any())
|
||||
{
|
||||
$"Getting detail for non exisisting card! Card id is {cardId}".Warn();
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -229,6 +277,7 @@ public class ApiController : WebApiController
|
||||
{
|
||||
Artist = musicData.Artist ?? string.Empty,
|
||||
Title = musicData.Title ?? string.Empty,
|
||||
MusicId = musicId,
|
||||
SongPlaySubDataList = new SongPlayDetailData[4]
|
||||
};
|
||||
|
||||
|
57
MudAdmin/Pages/FavoriteDialog.razor
Normal file
57
MudAdmin/Pages/FavoriteDialog.razor
Normal file
@ -0,0 +1,57 @@
|
||||
@using SharedProject.models
|
||||
@inject HttpClient Client
|
||||
@inject ILogger<FavoriteDialog> Logger
|
||||
|
||||
<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]
|
||||
MudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public SongPlayData Data { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public long CardId { get; set; }
|
||||
|
||||
async void Submit()
|
||||
{
|
||||
var postData = new MusicFavoriteData
|
||||
{
|
||||
CardId = CardId,
|
||||
IsFavorite = !Data.IsFavorite,
|
||||
MusicId = Data.MusicId
|
||||
};
|
||||
Logger.LogInformation("Data is {cardId}, {musicId}", CardId, Data.MusicId);
|
||||
var response = await Client.PostAsJsonAsync("api/UserDetail/SetMusicFavorite", postData);
|
||||
var result = await response.Content.ReadFromJsonAsync<bool>();
|
||||
Logger.LogInformation("Favorite result is {result}", result);
|
||||
MudDialog.Close(DialogResult.Ok(result));
|
||||
}
|
||||
|
||||
void Cancel() => MudDialog.Cancel();
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
@page "/fetchdata"
|
||||
@inject HttpClient Http
|
||||
|
||||
<PageTitle>Weather forecast</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3" GutterBottom="true">Weather forecast</MudText>
|
||||
<MudText Class="mb-8">This component demonstrates fetching data from the server.</MudText>
|
||||
@if (forecasts == null)
|
||||
{
|
||||
<MudProgressCircular Color="Color.Default" Indeterminate="true"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="forecasts" Hover="true" SortLabel="Sort By" Elevation="0">
|
||||
<HeaderContent>
|
||||
<MudTh>
|
||||
<MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<WeatherForecast, object>(x=>x.Date)">Date</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<WeatherForecast, object>(x=>x.TemperatureC)">Temp. (C)</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<WeatherForecast, object>(x=>x.TemperatureF)">Temp. (F)</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<WeatherForecast, object>(x=>x.Summary!)">Summary</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Date">@context.Date</MudTd>
|
||||
<MudTd DataLabel="Temp. (C)">@context.TemperatureC</MudTd>
|
||||
<MudTd DataLabel="Temp. (F)">@context.TemperatureF</MudTd>
|
||||
<MudTd DataLabel="Summary">@context.Summary</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager PageSizeOptions="new int[]{50, 100}"/>
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
|
||||
}
|
||||
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public string? Summary { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,17 @@
|
||||
@page "/user/{CardId:long}"
|
||||
@using SharedProject.models
|
||||
@using MudAdmin.Utils
|
||||
@using SharedProject.enums
|
||||
@inject HttpClient Client
|
||||
@inject IDialogService DialogService
|
||||
@inject ILogger<User> Logger
|
||||
|
||||
<PageTitle>User</PageTitle>
|
||||
|
||||
<MudContainer>
|
||||
@if (userDetail == null)
|
||||
{
|
||||
<MudProgressCircular Color="Color.Default" Indeterminate="true"/>
|
||||
<MudSkeleton Width="1184px" Height="57px"/>
|
||||
<MudSkeleton Width="1184px" Height="57px"/>
|
||||
<MudSkeleton Width="1184px" Height="57px"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -57,12 +60,12 @@ else
|
||||
</MudButton>
|
||||
</MudExpansionPanel>
|
||||
<MudExpansionPanel Text="SongPlayData">
|
||||
<MudDataGrid T="SongPlayData" Items="@songPlayDataList" Sortable="true">
|
||||
<MudDataGrid T="SongPlayData" Items="@songPlayDataList" Sortable="true" Filterable="true">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">Played Songs</MudText>
|
||||
</ToolBarContent>
|
||||
<Columns>
|
||||
<Column T="SongPlayData">
|
||||
<Column T="SongPlayData" Sortable="false" Filterable="false">
|
||||
<CellTemplate>
|
||||
<MudButton Variant="Variant.Outlined" Size="Size.Small"
|
||||
OnClick="@(() => OnShowDetailsClick(context.Item))">
|
||||
@ -70,22 +73,17 @@ else
|
||||
</MudButton>
|
||||
</CellTemplate>
|
||||
</Column>
|
||||
<Column T="SongPlayData">
|
||||
<Column T="SongPlayData" Field="IsFavorite" Sortable="false" Title="Favorite">
|
||||
<CellTemplate>
|
||||
<MudToggleIconButton ToggleChanged="@(() => OnFavoriteToggled(context.Item))"
|
||||
<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" Title="Total Play Count">
|
||||
<CellTemplate>
|
||||
<MudText>
|
||||
@CalculateTotalPlayCount(context.Item)
|
||||
</MudText>
|
||||
</CellTemplate>
|
||||
</Column>
|
||||
<Column T="SongPlayData" Field="TotalPlayCount" Title="Total Play Count" />
|
||||
</Columns>
|
||||
<ChildRowContent>
|
||||
@if (context.ShowDetails)
|
||||
@ -99,7 +97,7 @@ else
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent Class="pa-0">
|
||||
<MudTable Items="@context.SongPlaySubDataList" Context="SongPlayDetail" Elevation="0">
|
||||
<MudTable Items="@context.SongPlaySubDataList" Context="SongPlayDetail" Elevation="0" Filter="data => data.ClearState != ClearState.NotPlayed">
|
||||
<HeaderContent>
|
||||
<MudTh>Difficulty</MudTh>
|
||||
<MudTh>Clear State</MudTh>
|
||||
@ -132,6 +130,7 @@ else
|
||||
</MudExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
}
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
|
||||
@ -177,7 +176,13 @@ else
|
||||
private async Task SaveOptions()
|
||||
{
|
||||
isSavingOptions = true;
|
||||
await Task.Delay(2000);
|
||||
var postData = new PlayOption
|
||||
{
|
||||
CardId = CardId,
|
||||
FastSlowIndicator = fastSlowIndicator,
|
||||
FeverTrance = feverTranceShow
|
||||
};
|
||||
var result = await Client.PostAsJsonAsync("api/UserDetail/SetPlayOption", postData);
|
||||
isSavingOptions = false;
|
||||
}
|
||||
|
||||
@ -195,14 +200,29 @@ else
|
||||
return grade;
|
||||
}
|
||||
|
||||
private void OnFavoriteToggled(SongPlayData data)
|
||||
private async Task OnFavoriteToggled(SongPlayData data)
|
||||
{
|
||||
data.IsFavorite = !data.IsFavorite;
|
||||
}
|
||||
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;
|
||||
|
||||
private static int CalculateTotalPlayCount(SongPlayData data)
|
||||
{
|
||||
return data.SongPlaySubDataList
|
||||
.Sum(detailData => detailData.PlayCount);
|
||||
if (result.Cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)result.Data)
|
||||
{
|
||||
Logger.LogInformation("Changed!");
|
||||
data.IsFavorite = !data.IsFavorite;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,30 +5,63 @@
|
||||
|
||||
<PageTitle>Users</PageTitle>
|
||||
|
||||
<MudGrid>
|
||||
@foreach (var user in users)
|
||||
<MudContainer>
|
||||
@if (users is null)
|
||||
{
|
||||
<MudItem>
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">@user.PlayerName</MudText>
|
||||
</CardHeaderContent>
|
||||
</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)">Test</MudButton>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
<MudGrid>
|
||||
@for (var i = 0; i < 5; i++)
|
||||
{
|
||||
<MudItem>
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudSkeleton SkeletonType="SkeletonType.Rectangle" Height="32px"/>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudSkeleton Width="80px" Height="32px"/>
|
||||
<MudSkeleton Width="147px" Height="28px"/>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudSkeleton SkeletonType="SkeletonType.Rectangle" Width="99px" Height="25px" Class="ml-2"/>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
}
|
||||
</MudGrid>
|
||||
else if (!(users.Count == 0))
|
||||
{
|
||||
<MudGrid>
|
||||
@foreach (var user in users)
|
||||
{
|
||||
<MudItem>
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h5">@user.PlayerName</MudText>
|
||||
</CardHeaderContent>
|
||||
</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>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.h3">No Data</MudText>
|
||||
}
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
private List<models.User> users = new();
|
||||
private List<models.User>? users;
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@ -41,4 +74,5 @@
|
||||
{
|
||||
NavigationManager.NavigateTo($"user/{user.CardId}");
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using MudAdmin;
|
||||
|
@ -1,6 +1,5 @@
|
||||
<MudNavMenu>
|
||||
<MudNavLink Href="" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Home</MudNavLink>
|
||||
<MudNavLink Href="counter" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Add">Counter</MudNavLink>
|
||||
<MudNavLink Href="fetchdata" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Fetch data</MudNavLink>
|
||||
<MudNavLink Href="users" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Users</MudNavLink>
|
||||
</MudNavMenu>
|
@ -2,6 +2,7 @@
|
||||
|
||||
public enum ClearState
|
||||
{
|
||||
NotPlayed = 0,
|
||||
Failed,
|
||||
Clear,
|
||||
NoMiss,
|
||||
|
15
SharedProject/models/MusicFavoriteData.cs
Normal file
15
SharedProject/models/MusicFavoriteData.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class MusicFavoriteData
|
||||
{
|
||||
[JsonPropertyName(nameof(CardId))]
|
||||
public long CardId { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(MusicId))]
|
||||
public int MusicId { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(IsFavorite))]
|
||||
public bool IsFavorite { get; set; }
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
using SharedProject.enums;
|
||||
using System.Text.Json.Serialization;
|
||||
using SharedProject.enums;
|
||||
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class PlayOption
|
||||
{
|
||||
[JsonPropertyName(nameof(CardId))]
|
||||
public long CardId { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(FastSlowIndicator))]
|
||||
public PlayOptions.FastSlowIndicator FastSlowIndicator { get; set; }
|
||||
|
||||
[JsonPropertyName(nameof(FeverTrance))]
|
||||
public PlayOptions.FeverTranceShow FeverTrance { get; set; }
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace SharedProject.models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharedProject.models;
|
||||
|
||||
public class SongPlayData
|
||||
{
|
||||
@ -6,9 +8,20 @@ public class SongPlayData
|
||||
|
||||
public string Artist { get; set; } = string.Empty;
|
||||
|
||||
public int MusicId { get; set; }
|
||||
|
||||
public SongPlayDetailData[] SongPlaySubDataList { get; set; } = new SongPlayDetailData[4];
|
||||
|
||||
public bool IsFavorite { get; set; }
|
||||
|
||||
public bool ShowDetails { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public int TotalPlayCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return SongPlaySubDataList.Sum(data => data.PlayCount);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user