1
0
mirror of synced 2024-11-24 06:20:12 +01:00

Update web interface

This commit is contained in:
Yuchen Ji 2022-06-21 01:49:47 +08:00
parent 4871c0bf6f
commit d3265dbc02
13 changed files with 293 additions and 110 deletions

View 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}.");
}
}
}
}

View File

@ -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",

View File

@ -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]
};

View 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();
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -5,31 +5,64 @@
<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}");
}
}

View File

@ -1,3 +1,4 @@
using System.Text.Json;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudAdmin;

View File

@ -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>

View File

@ -2,6 +2,7 @@
public enum ClearState
{
NotPlayed = 0,
Failed,
Clear,
NoMiss,

View 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; }
}

View File

@ -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; }
}

View File

@ -1,14 +1,27 @@
namespace SharedProject.models;
using System.Text.Json.Serialization;
namespace SharedProject.models;
public class SongPlayData
{
public string Title { get; set; } = string.Empty;
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);
}
}
}