1
0
mirror of synced 2024-11-24 06:50:15 +01:00

Users api optimizations

* Moves the logic to attach user settings to the server
* Adds pagination to users request
This commit is contained in:
shiibe 2024-06-05 11:33:37 -04:00
parent 08ba4da763
commit 510a93c1aa
7 changed files with 149 additions and 32 deletions

View File

@ -0,0 +1,9 @@
namespace SharedProject.Models.Responses;
public class UsersResponse
{
public List<User> Users { get; set; } = new();
public int Page { get; set; } = 1;
public int TotalPages { get; set; } = 0;
public int TotalUsers { get; set; } = 0;
}

View File

@ -7,4 +7,6 @@ public class User
public List<string> AccessCodes { get; set; } = new();
public bool IsAdmin { get; set; }
public UserSetting UserSetting { get; set; } = new();
}

View File

@ -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<IEnumerable<User>> GetUsers()
public async Task<ActionResult<UsersResponse>> 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<User>();
return new UsersResponse();
}
if (!tokenInfo.Value.isAdmin)
{
return Array.Empty<User>();
return new UsersResponse();
}
}
return await authService.GetUsersFromCards();
return await authService.GetUsersFromCards(page, limit);
}
[HttpDelete("{baid}")]

View File

@ -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<Card?> GetCardByAccessCode(string accessCode)
{
return await context.Cards.FindAsync(accessCode);
@ -26,17 +30,96 @@ public class AuthService(TaikoDbContext context) : IAuthService
};
}
public async Task<List<User>> GetUsersFromCards()
public async Task<UsersResponse> 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<User>();
// 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<List<uint>> 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)

View File

@ -1,4 +1,5 @@
using SharedProject.Models;
using SharedProject.Models.Responses;
namespace TaikoLocalServer.Services.Interfaces;
@ -8,7 +9,7 @@ public interface IAuthService
public Task<Card?> GetCardByAccessCode(string accessCode);
public Task<List<User>> GetUsersFromCards();
public Task<UsersResponse> GetUsersFromCards(int page = 0, int limit = 12);
public Task AddCard(Card card);

View File

@ -2,14 +2,15 @@
@inject AuthService AuthService
@inject NavigationManager NavigationManager
@using TaikoWebUI.Components
@using TaikoWebUI.Components;
@page "/Users"
<MudText Typo="Typo.h4">@Localizer["Users"]</MudText>
<MudGrid Class="my-8">
@if (!AuthService.LoginRequired || (AuthService.LoginRequired && AuthService.IsAdmin)) {
if (usersWithSettings == null) {
if (isLoading == true)
{
// Loading...
for (uint i = 0; i < 6; i++) {
<MudItem xs="12" md="6" lg="4">
@ -28,11 +29,11 @@
</MudCard>
</MudItem>
}
} else if (usersWithSettings.Count > 0) {
foreach (var (user, userSetting) in usersWithSettings)
} else if (response.Users.Count > 0) {
foreach (var user in response.Users)
{
<MudItem xs="12" md="6" lg="4">
<UserCard User="user" UserSetting="userSetting" />
<UserCard User="user" UserSetting="user.UserSetting" />
</MudItem>
}
} else { // No users in the database

View File

@ -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<UsersResponse>($"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<List<User>>("api/Users");
var userSettings = await Client.GetFromJsonAsync<List<UserSetting>>("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();
}
}