From 510a93c1aa8a875e92a39fa8e901f4519f7ccf08 Mon Sep 17 00:00:00 2001 From: shiibe <82057235+shiibe@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:33:37 -0400 Subject: [PATCH] Users api optimizations * Moves the logic to attach user settings to the server * Adds pagination to users request --- .../Models/Responses/UsersResponse.cs | 9 ++ SharedProject/Models/User.cs | 2 + .../Controllers/Api/UsersController.cs | 19 +++- TaikoLocalServer/Services/AuthService.cs | 101 ++++++++++++++++-- .../Services/Interfaces/IAuthService.cs | 3 +- TaikoWebUI/Pages/Users.razor | 11 +- TaikoWebUI/Pages/Users.razor.cs | 36 ++++--- 7 files changed, 149 insertions(+), 32 deletions(-) create mode 100644 SharedProject/Models/Responses/UsersResponse.cs diff --git a/SharedProject/Models/Responses/UsersResponse.cs b/SharedProject/Models/Responses/UsersResponse.cs new file mode 100644 index 0000000..27344f7 --- /dev/null +++ b/SharedProject/Models/Responses/UsersResponse.cs @@ -0,0 +1,9 @@ +namespace SharedProject.Models.Responses; + +public class UsersResponse +{ + public List Users { get; set; } = new(); + public int Page { get; set; } = 1; + public int TotalPages { get; set; } = 0; + public int TotalUsers { get; set; } = 0; +} \ No newline at end of file diff --git a/SharedProject/Models/User.cs b/SharedProject/Models/User.cs index 2dedcc4..ad50e18 100644 --- a/SharedProject/Models/User.cs +++ b/SharedProject/Models/User.cs @@ -7,4 +7,6 @@ public class User public List AccessCodes { get; set; } = new(); public bool IsAdmin { get; set; } + + public UserSetting UserSetting { get; set; } = new(); } \ No newline at end of file diff --git a/TaikoLocalServer/Controllers/Api/UsersController.cs b/TaikoLocalServer/Controllers/Api/UsersController.cs index f305a0d..eaf84ec 100644 --- a/TaikoLocalServer/Controllers/Api/UsersController.cs +++ b/TaikoLocalServer/Controllers/Api/UsersController.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Options; using SharedProject.Models; +using SharedProject.Models.Responses; using TaikoLocalServer.Filters; using TaikoLocalServer.Settings; @@ -36,23 +37,33 @@ public class UsersController(IUserDatumService userDatumService, IAuthService au [HttpGet] [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] - public async Task> GetUsers() + public async Task> GetUsers([FromQuery] int page = 1, [FromQuery] int limit = 10) { + if (page < 1) + { + return BadRequest( new { Message = "Page number cannot be less than 1." }); + } + + if (limit > 200) + { + return BadRequest( new { Message = "Limit cannot be greater than 200." }); + } + if (authSettings.AuthenticationRequired) { var tokenInfo = authService.ExtractTokenInfo(HttpContext); if (tokenInfo == null) { - return Array.Empty(); + return new UsersResponse(); } if (!tokenInfo.Value.isAdmin) { - return Array.Empty(); + return new UsersResponse(); } } - return await authService.GetUsersFromCards(); + return await authService.GetUsersFromCards(page, limit); } [HttpDelete("{baid}")] diff --git a/TaikoLocalServer/Services/AuthService.cs b/TaikoLocalServer/Services/AuthService.cs index 296cd27..d12a3e3 100644 --- a/TaikoLocalServer/Services/AuthService.cs +++ b/TaikoLocalServer/Services/AuthService.cs @@ -2,12 +2,16 @@ using System.Security.Claims; using GameDatabase.Context; using SharedProject.Models; +using SharedProject.Models.Responses; using Swan.Mapping; +using TaikoLocalServer.Controllers.Api; +using SharedProject.Utils; namespace TaikoLocalServer.Services; public class AuthService(TaikoDbContext context) : IAuthService { + private readonly UserSettingsController _userSettingsController; public async Task GetCardByAccessCode(string accessCode) { return await context.Cards.FindAsync(accessCode); @@ -26,17 +30,96 @@ public class AuthService(TaikoDbContext context) : IAuthService }; } - public async Task> GetUsersFromCards() + public async Task GetUsersFromCards(int page = 1, int limit = 12) { - var cardEntries = await context.Cards.ToListAsync(); - var userEntries = await context.UserData.ToListAsync(); - var users = userEntries.Select(userEntry => new User + // Get the total count of users + var totalUsers = await context.UserData.CountAsync(); + + var users = new List(); + + // Calculate the total pages + var totalPages = totalUsers / limit; + + // If there is a remainder, add one to the total pages + if (totalUsers % limit > 0) { - Baid = userEntry.Baid, - AccessCodes = cardEntries.Where(cardEntry => cardEntry.Baid == userEntry.Baid).Select(cardEntry => cardEntry.AccessCode).ToList(), - IsAdmin = userEntry.IsAdmin - }).ToList(); - return users; + totalPages++; + } + + var cardEntries = await context.Cards.ToListAsync(); + var userEntries = await context.UserData + .OrderBy(user => user.Baid) + .Skip((page - 1) * limit) + .Take(limit) + .ToListAsync(); + + foreach (var user in userEntries) + { + List> costumeUnlockData = + [user.UnlockedKigurumi, user.UnlockedHead, user.UnlockedBody, user.UnlockedFace, user.UnlockedPuchi]; + + var unlockedTitle = user.TitleFlgArray + .ToList(); + + for (var i = 0; i < 5; i++) + { + if (!costumeUnlockData[i].Contains(0)) + { + costumeUnlockData[i].Add(0); + } + } + + var userSetting = new UserSetting + { + Baid = user.Baid, + AchievementDisplayDifficulty = user.AchievementDisplayDifficulty, + IsDisplayAchievement = user.DisplayAchievement, + IsDisplayDanOnNamePlate = user.DisplayDan, + DifficultySettingCourse = user.DifficultySettingCourse, + DifficultySettingStar = user.DifficultySettingStar, + DifficultySettingSort = user.DifficultySettingSort, + IsVoiceOn = user.IsVoiceOn, + IsSkipOn = user.IsSkipOn, + NotesPosition = user.NotesPosition, + PlaySetting = PlaySettingConverter.ShortToPlaySetting(user.OptionSetting), + ToneId = user.SelectedToneId, + MyDonName = user.MyDonName, + MyDonNameLanguage = user.MyDonNameLanguage, + Title = user.Title, + TitlePlateId = user.TitlePlateId, + Kigurumi = user.CurrentKigurumi, + Head = user.CurrentHead, + Body = user.CurrentBody, + Face = user.CurrentFace, + Puchi = user.CurrentPuchi, + UnlockedKigurumi = costumeUnlockData[0], + UnlockedHead = costumeUnlockData[1], + UnlockedBody = costumeUnlockData[2], + UnlockedFace = costumeUnlockData[3], + UnlockedPuchi = costumeUnlockData[4], + UnlockedTitle = unlockedTitle, + BodyColor = user.ColorBody, + FaceColor = user.ColorFace, + LimbColor = user.ColorLimb, + LastPlayDateTime = user.LastPlayDatetime + }; + + users.Add(new User + { + Baid = user.Baid, + AccessCodes = cardEntries.Where(card => card.Baid == user.Baid).Select(card => card.AccessCode).ToList(), + IsAdmin = user.IsAdmin, + UserSetting = userSetting + }); + } + + return new UsersResponse + { + Users = users, + Page = page, + TotalPages = totalPages, + TotalUsers = totalUsers + }; } public async Task AddCard(Card card) diff --git a/TaikoLocalServer/Services/Interfaces/IAuthService.cs b/TaikoLocalServer/Services/Interfaces/IAuthService.cs index a90aca8..3f69b9a 100644 --- a/TaikoLocalServer/Services/Interfaces/IAuthService.cs +++ b/TaikoLocalServer/Services/Interfaces/IAuthService.cs @@ -1,4 +1,5 @@ using SharedProject.Models; +using SharedProject.Models.Responses; namespace TaikoLocalServer.Services.Interfaces; @@ -8,7 +9,7 @@ public interface IAuthService public Task GetCardByAccessCode(string accessCode); - public Task> GetUsersFromCards(); + public Task GetUsersFromCards(int page = 0, int limit = 12); public Task AddCard(Card card); diff --git a/TaikoWebUI/Pages/Users.razor b/TaikoWebUI/Pages/Users.razor index 4154ab7..3f50d69 100644 --- a/TaikoWebUI/Pages/Users.razor +++ b/TaikoWebUI/Pages/Users.razor @@ -2,14 +2,15 @@ @inject AuthService AuthService @inject NavigationManager NavigationManager -@using TaikoWebUI.Components +@using TaikoWebUI.Components; @page "/Users" @Localizer["Users"] @if (!AuthService.LoginRequired || (AuthService.LoginRequired && AuthService.IsAdmin)) { - if (usersWithSettings == null) { + if (isLoading == true) + { // Loading... for (uint i = 0; i < 6; i++) { @@ -28,11 +29,11 @@ } - } else if (usersWithSettings.Count > 0) { - foreach (var (user, userSetting) in usersWithSettings) + } else if (response.Users.Count > 0) { + foreach (var user in response.Users) { - + } } else { // No users in the database diff --git a/TaikoWebUI/Pages/Users.razor.cs b/TaikoWebUI/Pages/Users.razor.cs index 44e6278..704b1f2 100644 --- a/TaikoWebUI/Pages/Users.razor.cs +++ b/TaikoWebUI/Pages/Users.razor.cs @@ -2,8 +2,22 @@ public partial class Users { - // Tuple of User and UserSetting - private List<(User, UserSetting)>? usersWithSettings; + private UsersResponse? response = new(); + + private int TotalPages { get; set; } = 0; + private bool isLoading = true; + private int currentPage = 1; + private int pageSize = 12; + + private async Task GetUsersData() + { + isLoading = true; + response = await Client.GetFromJsonAsync($"api/Users?page={currentPage}&limit={pageSize}"); + response.ThrowIfNull(); + + TotalPages = response.TotalPages; + isLoading = false; + } protected override async Task OnInitializedAsync() { @@ -15,17 +29,13 @@ public partial class Users if (AuthService.IsAdmin || !AuthService.LoginRequired) { - var users = await Client.GetFromJsonAsync>("api/Users"); - var userSettings = await Client.GetFromJsonAsync>("api/UserSettings"); - if (users != null && userSettings != null) - { - // Combine User and UserSetting with the same Baid - usersWithSettings = users.Join(userSettings, - user => user.Baid, - setting => setting.Baid, - (user, setting) => (user, setting)) - .ToList(); - } + await GetUsersData(); } } + + private async Task OnPageChange(int page) + { + currentPage = page; + await GetUsersData(); + } } \ No newline at end of file