Leaderboard API and client-side adjustments
This commit is contained in:
parent
dc8916fea8
commit
ed8640c23f
@ -3,6 +3,9 @@
|
||||
public class SongLeaderboardResponse
|
||||
{
|
||||
public List<SongLeaderboard> LeaderboardData { get; set; } = new();
|
||||
public SongLeaderboard? UserScore { get; set; } = null;
|
||||
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." });
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
LeaderboardData = leaderboard,
|
||||
UserScore = userScore,
|
||||
CurrentPage = page,
|
||||
TotalPages = totalPages
|
||||
TotalPages = totalPages,
|
||||
TotalScores = totalScores
|
||||
});
|
||||
}
|
||||
}
|
@ -3,5 +3,5 @@
|
||||
using SharedProject.Models;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
.OrderByDescending(x => x.BestScore)
|
||||
.ThenByDescending(x => x.BestRate)
|
||||
.ThenByDescending(x => x.BestCrown)
|
||||
.Skip((page - 1) * limit) // Subtract 1 because page numbers now start at 1
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
@ -48,10 +49,6 @@ public class SongLeaderboardService : ISongLeaderboardService
|
||||
.Where(x => x.Baid == score.Baid)
|
||||
.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
|
||||
var rank = await context.SongBestData
|
||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.BestScore > score.BestScore)
|
||||
@ -68,46 +65,38 @@ public class SongLeaderboardService : ISongLeaderboardService
|
||||
BestScoreRank = score.BestScoreRank
|
||||
});
|
||||
}
|
||||
|
||||
if (baid == 0)
|
||||
{
|
||||
return (leaderboard, totalPages);
|
||||
}
|
||||
|
||||
// get current user score if it exists
|
||||
var currentUserScore = await context.SongBestData
|
||||
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.Baid == baid)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (currentUserScore != null)
|
||||
// Get UserScore if baid is provided
|
||||
SongLeaderboard? userBestScore = null;
|
||||
if (baid != 0)
|
||||
{
|
||||
// check if they are in the limit
|
||||
var userRank = leaderboard.FindIndex(x => x.Baid == baid);
|
||||
var score = await context.SongBestData
|
||||
.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
|
||||
.Where(x => x.Baid == baid)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
|
||||
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();
|
||||
|
||||
leaderboard.Add(new SongLeaderboard
|
||||
userBestScore = new SongLeaderboard
|
||||
{
|
||||
Rank = rank + 1,
|
||||
Baid = currentUserScore.Baid,
|
||||
Baid = score.Baid,
|
||||
UserName = user?.MyDonName,
|
||||
BestScore = currentUserScore.BestScore,
|
||||
BestRate = currentUserScore.BestRate,
|
||||
BestCrown = currentUserScore.BestCrown,
|
||||
BestScoreRank = currentUserScore.BestScoreRank
|
||||
});
|
||||
BestScore = score.BestScore,
|
||||
BestRate = score.BestRate,
|
||||
BestCrown = score.BestCrown,
|
||||
BestScoreRank = score.BestScoreRank
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return (leaderboard, totalPages);
|
||||
return (leaderboard, userBestScore, totalPages, totalScores);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
@inject HttpClient Client;
|
||||
@inject Blazored.LocalStorage.ILocalStorageService LocalStorage;
|
||||
|
||||
@using Microsoft.Extensions.Options;
|
||||
@using TaikoWebUI.Settings;
|
||||
@using TaikoWebUI.Utilities;
|
||||
|
||||
<MudCard Outlined="true" Elevation="0">
|
||||
@ -44,7 +45,7 @@
|
||||
}
|
||||
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>
|
||||
<MudTh>@Localizer["Rank"]</MudTh>
|
||||
<MudTh>@Localizer["Player"]</MudTh>
|
||||
@ -53,37 +54,46 @@
|
||||
<MudTh>@Localizer["Rank"]</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Rank</MudTd>
|
||||
<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)
|
||||
@if ( @context.Rank > 0 && @context.BestScore > 0)
|
||||
{
|
||||
<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>
|
||||
<MudTd>@context.Rank</MudTd>
|
||||
<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>
|
||||
}
|
||||
</PagerContent>
|
||||
else
|
||||
{
|
||||
<MudTd colspan="6">
|
||||
<MudText Align="Align.Center" Class="my-1">
|
||||
...
|
||||
</MudText>
|
||||
</MudTd>
|
||||
}
|
||||
</RowTemplate>
|
||||
</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>
|
||||
</MudCard>
|
@ -1,9 +1,14 @@
|
||||
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoWebUI.Settings;
|
||||
|
||||
namespace TaikoWebUI.Components.Song;
|
||||
|
||||
public partial class SongLeaderboardCard
|
||||
{
|
||||
private SongLeaderboardResponse? response = null;
|
||||
[Inject]
|
||||
IOptions<WebUiSettings> UiSettings { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public int SongId { get; set; }
|
||||
@ -13,21 +18,68 @@ public partial class SongLeaderboardCard
|
||||
|
||||
[Parameter]
|
||||
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 int currentPage = 1;
|
||||
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()
|
||||
{
|
||||
isLoading = true;
|
||||
response = await Client.GetFromJsonAsync<SongLeaderboardResponse>($"api/SongLeaderboard/{(uint)SongId}?baid={(uint)Baid}&difficulty={(uint)Difficulty}&page={currentPage}&limit={pageSize}");
|
||||
response.ThrowIfNull();
|
||||
|
||||
LeaderboardScores.Clear();
|
||||
LeaderboardScores.AddRange(response.LeaderboardData);
|
||||
|
||||
// set 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;
|
||||
}
|
||||
|
||||
|
@ -9,5 +9,13 @@ public class WebUiSettings
|
||||
public bool RegisterWithLastPlayTime { get; set; }
|
||||
public bool AllowUserDelete { get; set; }
|
||||
public bool AllowFreeProfileEditing { get; set; }
|
||||
|
||||
public SongLeaderboardSettings SongLeaderboardSettings { get; set; } = new SongLeaderboardSettings();
|
||||
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",
|
||||
"AllowUserDelete": "true",
|
||||
"AllowFreeProfileEditing": "true",
|
||||
"SongLeaderboardSettings": {
|
||||
"DisablePagination": "false",
|
||||
"PageSize": "10"
|
||||
},
|
||||
"SupportedLanguages": [
|
||||
{
|
||||
"CultureCode": "en-US",
|
||||
|
Loading…
x
Reference in New Issue
Block a user