Leaderboard API and client-side adjustments
This commit is contained in:
parent
dc8916fea8
commit
ed8640c23f
@ -3,6 +3,9 @@
|
|||||||
public class SongLeaderboardResponse
|
public class SongLeaderboardResponse
|
||||||
{
|
{
|
||||||
public List<SongLeaderboard> LeaderboardData { get; set; } = new();
|
public List<SongLeaderboard> LeaderboardData { get; set; } = new();
|
||||||
|
public SongLeaderboard? UserScore { get; set; } = null;
|
||||||
public int CurrentPage { get; set; }
|
public int CurrentPage { get; set; }
|
||||||
public int TotalPages { get; set; }
|
public int TotalPages { get; set; } = 0;
|
||||||
|
|
||||||
|
public int TotalScores { get; set; } = 0;
|
||||||
}
|
}
|
@ -51,13 +51,15 @@ public class SongLeaderboardController(ISongLeaderboardService songLeaderboardSe
|
|||||||
return BadRequest(new { Message = "Invalid difficulty. Please provide a number between 1-5." });
|
return BadRequest(new { Message = "Invalid difficulty. Please provide a number between 1-5." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var (leaderboard, totalPages) = await songLeaderboardService.GetSongLeaderboard(songId, (Difficulty)difficulty, (int)baid, page, limit);
|
var (leaderboard, userScore, totalPages, totalScores) = await songLeaderboardService.GetSongLeaderboard(songId, (Difficulty)difficulty, (int)baid, page, limit);
|
||||||
|
|
||||||
return Ok(new SongLeaderboardResponse
|
return Ok(new SongLeaderboardResponse
|
||||||
{
|
{
|
||||||
LeaderboardData = leaderboard,
|
LeaderboardData = leaderboard,
|
||||||
|
UserScore = userScore,
|
||||||
CurrentPage = page,
|
CurrentPage = page,
|
||||||
TotalPages = totalPages
|
TotalPages = totalPages,
|
||||||
|
TotalScores = totalScores
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,5 +3,5 @@
|
|||||||
using SharedProject.Models;
|
using SharedProject.Models;
|
||||||
public interface ISongLeaderboardService
|
public interface ISongLeaderboardService
|
||||||
{
|
{
|
||||||
Task<(List<SongLeaderboard>, int)> GetSongLeaderboard(uint songId, Difficulty difficulty, int baid = 0, int page = 1, int limit = 10);
|
Task<(List<SongLeaderboard>, SongLeaderboard?, int, int)> GetSongLeaderboard(uint songId, Difficulty difficulty, int baid = 0, int page = 1, int limit = 10);
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ public class SongLeaderboardService : ISongLeaderboardService
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(List<SongLeaderboard>, int)> GetSongLeaderboard(uint songId, Difficulty difficulty, int baid = 0,
|
public async Task<(List<SongLeaderboard>, SongLeaderboard?, int, int)> GetSongLeaderboard(uint songId, Difficulty difficulty, int baid = 0,
|
||||||
int page = 1, int limit = 10)
|
int page = 1, int limit = 10)
|
||||||
{
|
{
|
||||||
// Get the total count of scores for the song
|
// Get the total count of scores for the song
|
||||||
@ -34,6 +34,7 @@ public class SongLeaderboardService : ISongLeaderboardService
|
|||||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty)
|
.Where(x => x.SongId == songId && x.Difficulty == difficulty)
|
||||||
.OrderByDescending(x => x.BestScore)
|
.OrderByDescending(x => x.BestScore)
|
||||||
.ThenByDescending(x => x.BestRate)
|
.ThenByDescending(x => x.BestRate)
|
||||||
|
.ThenByDescending(x => x.BestCrown)
|
||||||
.Skip((page - 1) * limit) // Subtract 1 because page numbers now start at 1
|
.Skip((page - 1) * limit) // Subtract 1 because page numbers now start at 1
|
||||||
.Take(limit)
|
.Take(limit)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@ -48,10 +49,6 @@ public class SongLeaderboardService : ISongLeaderboardService
|
|||||||
.Where(x => x.Baid == score.Baid)
|
.Where(x => x.Baid == score.Baid)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
var playDatum = await context.SongPlayData
|
|
||||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.Baid == score.Baid)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
// calculate Rank based on page/limit
|
// calculate Rank based on page/limit
|
||||||
var rank = await context.SongBestData
|
var rank = await context.SongBestData
|
||||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.BestScore > score.BestScore)
|
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.BestScore > score.BestScore)
|
||||||
@ -68,46 +65,38 @@ public class SongLeaderboardService : ISongLeaderboardService
|
|||||||
BestScoreRank = score.BestScoreRank
|
BestScoreRank = score.BestScoreRank
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baid == 0)
|
|
||||||
{
|
|
||||||
return (leaderboard, totalPages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current user score if it exists
|
// Get UserScore if baid is provided
|
||||||
var currentUserScore = await context.SongBestData
|
SongLeaderboard? userBestScore = null;
|
||||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.Baid == baid)
|
if (baid != 0)
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
if (currentUserScore != null)
|
|
||||||
{
|
{
|
||||||
// check if they are in the limit
|
var score = await context.SongBestData
|
||||||
var userRank = leaderboard.FindIndex(x => x.Baid == baid);
|
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.Baid == baid)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (userRank >= limit)
|
if (score != null)
|
||||||
{
|
{
|
||||||
// get the user data for the current user
|
|
||||||
var user = await context.UserData
|
var user = await context.UserData
|
||||||
.Where(x => x.Baid == baid)
|
.Where(x => x.Baid == baid)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
var rank = await context.SongBestData
|
var rank = await context.SongBestData
|
||||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.BestScore > currentUserScore.BestScore)
|
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.BestScore > score.BestScore)
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
|
|
||||||
leaderboard.Add(new SongLeaderboard
|
userBestScore = new SongLeaderboard
|
||||||
{
|
{
|
||||||
Rank = rank + 1,
|
Rank = rank + 1,
|
||||||
Baid = currentUserScore.Baid,
|
Baid = score.Baid,
|
||||||
UserName = user?.MyDonName,
|
UserName = user?.MyDonName,
|
||||||
BestScore = currentUserScore.BestScore,
|
BestScore = score.BestScore,
|
||||||
BestRate = currentUserScore.BestRate,
|
BestRate = score.BestRate,
|
||||||
BestCrown = currentUserScore.BestCrown,
|
BestCrown = score.BestCrown,
|
||||||
BestScoreRank = currentUserScore.BestScoreRank
|
BestScoreRank = score.BestScoreRank
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (leaderboard, totalPages);
|
return (leaderboard, userBestScore, totalPages, totalScores);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
@inject HttpClient Client;
|
@inject HttpClient Client;
|
||||||
@inject Blazored.LocalStorage.ILocalStorageService LocalStorage;
|
@inject Blazored.LocalStorage.ILocalStorageService LocalStorage;
|
||||||
|
@using Microsoft.Extensions.Options;
|
||||||
|
@using TaikoWebUI.Settings;
|
||||||
@using TaikoWebUI.Utilities;
|
@using TaikoWebUI.Utilities;
|
||||||
|
|
||||||
<MudCard Outlined="true" Elevation="0">
|
<MudCard Outlined="true" Elevation="0">
|
||||||
@ -44,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudTable RowClassFunc="@GetActiveRowClass" Loading="isLoading" Items="@response.LeaderboardData" Class="leaderboard-table" Elevation="0" Outlined="false" Dense="true" Striped="true">
|
<MudTable RowClassFunc="@GetActiveRowClass" Loading="isLoading" Items="@LeaderboardScores" Class="leaderboard-table" Elevation="0" Outlined="false" Dense="true" Striped="true">
|
||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>@Localizer["Rank"]</MudTh>
|
<MudTh>@Localizer["Rank"]</MudTh>
|
||||||
<MudTh>@Localizer["Player"]</MudTh>
|
<MudTh>@Localizer["Player"]</MudTh>
|
||||||
@ -53,37 +54,46 @@
|
|||||||
<MudTh>@Localizer["Rank"]</MudTh>
|
<MudTh>@Localizer["Rank"]</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Rank</MudTd>
|
@if ( @context.Rank > 0 && @context.BestScore > 0)
|
||||||
<MudTd>
|
|
||||||
<MudStack Row="true" AlignItems="AlignItems.Baseline">
|
|
||||||
<MudText Typo="Typo.body1">@context.UserName</MudText>
|
|
||||||
<MudText Typo="Typo.caption" Style="color: #9E9E9E">(@Localizer["ID"]: @context.Baid)</MudText>
|
|
||||||
</MudStack>
|
|
||||||
</MudTd>
|
|
||||||
<MudTd>@context.BestScore</MudTd>
|
|
||||||
<MudTd>
|
|
||||||
<img src="@($"/images/crown_{context.BestCrown}.png")" alt="@(ScoreUtils.GetCrownText(context.BestCrown))" title="@(ScoreUtils.GetCrownText(context.BestCrown))" style="@Constants.ICON_STYLE"/>
|
|
||||||
</MudTd>
|
|
||||||
<MudTd>
|
|
||||||
@if (context.BestScoreRank is not ScoreRank.None)
|
|
||||||
{
|
|
||||||
<img src="@($"/images/rank_{context.BestScoreRank}.png")" alt="@(ScoreUtils.GetRankText(context.BestScoreRank))" title="@(ScoreUtils.GetRankText(context.BestScoreRank))" style="@Constants.ICON_STYLE"/>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.body1">—</MudText>
|
|
||||||
}
|
|
||||||
</MudTd>
|
|
||||||
</RowTemplate>
|
|
||||||
<PagerContent>
|
|
||||||
@if (TotalPages > 1)
|
|
||||||
{
|
{
|
||||||
<div class="d-flex flex-column align-center">
|
<MudTd>@context.Rank</MudTd>
|
||||||
<MudPagination Class="pa-4" Rectangular="true" DisableElevation="true" Count="@TotalPages" Selected="currentPage" SelectedChanged="(page) => OnPageChange(page)" BoundaryCount="1" MiddleCount="3" />
|
<MudTd>
|
||||||
</div>
|
<MudStack Row="true" AlignItems="AlignItems.Baseline">
|
||||||
|
<MudText Typo="Typo.body1">@context.UserName</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Style="color: #9E9E9E">(@Localizer["ID"]: @context.Baid)</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>@context.BestScore</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<img src="@($"/images/crown_{context.BestCrown}.png")" alt="@(ScoreUtils.GetCrownText(context.BestCrown))" title="@(ScoreUtils.GetCrownText(context.BestCrown))" style="@Constants.ICON_STYLE"/>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@if (context.BestScoreRank is not ScoreRank.None)
|
||||||
|
{
|
||||||
|
<img src="@($"/images/rank_{context.BestScoreRank}.png")" alt="@(ScoreUtils.GetRankText(context.BestScoreRank))" title="@(ScoreUtils.GetRankText(context.BestScoreRank))" style="@Constants.ICON_STYLE"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1">—</MudText>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
}
|
}
|
||||||
</PagerContent>
|
else
|
||||||
|
{
|
||||||
|
<MudTd colspan="6">
|
||||||
|
<MudText Align="Align.Center" Class="my-1">
|
||||||
|
...
|
||||||
|
</MudText>
|
||||||
|
</MudTd>
|
||||||
|
}
|
||||||
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
@if (TotalPages > 1 && isPaginationEnabled)
|
||||||
|
{
|
||||||
|
<div class="d-flex flex-column align-center">
|
||||||
|
<MudPagination Class="pa-4" Rectangular="true" DisableElevation="true" Count="@TotalPages" Selected="currentPage" SelectedChanged="(page) => OnPageChange(page)" BoundaryCount="1" MiddleCount="3" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
@ -1,9 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using TaikoWebUI.Settings;
|
||||||
|
|
||||||
namespace TaikoWebUI.Components.Song;
|
namespace TaikoWebUI.Components.Song;
|
||||||
|
|
||||||
public partial class SongLeaderboardCard
|
public partial class SongLeaderboardCard
|
||||||
{
|
{
|
||||||
private SongLeaderboardResponse? response = null;
|
[Inject]
|
||||||
|
IOptions<WebUiSettings> UiSettings { get; set; } = default!;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int SongId { get; set; }
|
public int SongId { get; set; }
|
||||||
@ -13,21 +18,68 @@ public partial class SongLeaderboardCard
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Difficulty Difficulty { get; set; } = Difficulty.None;
|
public Difficulty Difficulty { get; set; } = Difficulty.None;
|
||||||
|
|
||||||
private string SelectedDifficulty { get; set; } = "None";
|
|
||||||
private int TotalPages { get; set; } = 0;
|
|
||||||
|
|
||||||
|
private SongLeaderboardResponse? response = null;
|
||||||
|
private List<SongLeaderboard> LeaderboardScores { get; set; } = new();
|
||||||
|
private int TotalRows { get; set; } = 0;
|
||||||
|
private string SelectedDifficulty { get; set; } = "None";
|
||||||
|
private bool isPaginationEnabled = true;
|
||||||
|
private int TotalPages { get; set; } = 0;
|
||||||
private bool isLoading = true;
|
private bool isLoading = true;
|
||||||
private int currentPage = 1;
|
private int currentPage = 1;
|
||||||
private int pageSize = 10;
|
private int pageSize = 10;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
|
||||||
|
if (UiSettings.Value.SongLeaderboardSettings.DisablePagination)
|
||||||
|
{
|
||||||
|
isPaginationEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UiSettings.Value.SongLeaderboardSettings.PageSize > 200 |
|
||||||
|
UiSettings.Value.SongLeaderboardSettings.PageSize <= 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid LeaderboardSettings.PageSize value in appsettings.json. The value must be between 1 and 200. Defaulting to 10.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UiSettings.Value.SongLeaderboardSettings.PageSize > 0 & UiSettings.Value.SongLeaderboardSettings.PageSize <= 200)
|
||||||
|
{
|
||||||
|
pageSize = UiSettings.Value.SongLeaderboardSettings.PageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task GetLeaderboardData()
|
private async Task GetLeaderboardData()
|
||||||
{
|
{
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
response = await Client.GetFromJsonAsync<SongLeaderboardResponse>($"api/SongLeaderboard/{(uint)SongId}?baid={(uint)Baid}&difficulty={(uint)Difficulty}&page={currentPage}&limit={pageSize}");
|
response = await Client.GetFromJsonAsync<SongLeaderboardResponse>($"api/SongLeaderboard/{(uint)SongId}?baid={(uint)Baid}&difficulty={(uint)Difficulty}&page={currentPage}&limit={pageSize}");
|
||||||
response.ThrowIfNull();
|
response.ThrowIfNull();
|
||||||
|
|
||||||
|
LeaderboardScores.Clear();
|
||||||
|
LeaderboardScores.AddRange(response.LeaderboardData);
|
||||||
|
|
||||||
|
// set TotalPages
|
||||||
TotalPages = response.TotalPages;
|
TotalPages = response.TotalPages;
|
||||||
|
|
||||||
|
if (response.UserScore != null)
|
||||||
|
{
|
||||||
|
// if baid isn't already in the LeaderboardScores, add it
|
||||||
|
if (LeaderboardScores.All(x => x.Baid != response.UserScore.Baid))
|
||||||
|
{
|
||||||
|
LeaderboardScores.Add(new SongLeaderboard()); // Add an empty row
|
||||||
|
LeaderboardScores.Add(response.UserScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TotalRows = LeaderboardScores.Count;
|
||||||
|
|
||||||
|
// log the LeaderboardScores
|
||||||
|
foreach (var score in LeaderboardScores)
|
||||||
|
{
|
||||||
|
Console.WriteLine(score.Rank + " " + score.Baid + " " + score.UserName + " " + score.BestScore + " " + score.BestRate + " " + score.BestCrown + " " + score.BestScoreRank);
|
||||||
|
}
|
||||||
|
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,5 +9,13 @@ public class WebUiSettings
|
|||||||
public bool RegisterWithLastPlayTime { get; set; }
|
public bool RegisterWithLastPlayTime { get; set; }
|
||||||
public bool AllowUserDelete { get; set; }
|
public bool AllowUserDelete { get; set; }
|
||||||
public bool AllowFreeProfileEditing { get; set; }
|
public bool AllowFreeProfileEditing { get; set; }
|
||||||
|
|
||||||
|
public SongLeaderboardSettings SongLeaderboardSettings { get; set; } = new SongLeaderboardSettings();
|
||||||
public Language[] SupportedLanguages { get; set; } = Array.Empty<Language>();
|
public Language[] SupportedLanguages { get; set; } = Array.Empty<Language>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SongLeaderboardSettings
|
||||||
|
{
|
||||||
|
public bool DisablePagination { get; set; } = false;
|
||||||
|
public int PageSize { get; set; } = 10;
|
||||||
}
|
}
|
@ -7,6 +7,10 @@
|
|||||||
"RegisterWithLastPlayTime": "false",
|
"RegisterWithLastPlayTime": "false",
|
||||||
"AllowUserDelete": "true",
|
"AllowUserDelete": "true",
|
||||||
"AllowFreeProfileEditing": "true",
|
"AllowFreeProfileEditing": "true",
|
||||||
|
"SongLeaderboardSettings": {
|
||||||
|
"DisablePagination": "false",
|
||||||
|
"PageSize": "10"
|
||||||
|
},
|
||||||
"SupportedLanguages": [
|
"SupportedLanguages": [
|
||||||
{
|
{
|
||||||
"CultureCode": "en-US",
|
"CultureCode": "en-US",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user