1
0
mirror of synced 2025-01-31 04:13:50 +01:00

Leaderboard API and client-side adjustments

This commit is contained in:
shiibe 2024-06-09 15:12:12 -04:00
parent dc8916fea8
commit ed8640c23f
8 changed files with 136 additions and 68 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,10 @@
"RegisterWithLastPlayTime": "false",
"AllowUserDelete": "true",
"AllowFreeProfileEditing": "true",
"SongLeaderboardSettings": {
"DisablePagination": "false",
"PageSize": "10"
},
"SupportedLanguages": [
{
"CultureCode": "en-US",