Users api optimizations
* Moves the logic to attach user settings to the server * Adds pagination to users request
This commit is contained in:
parent
08ba4da763
commit
510a93c1aa
9
SharedProject/Models/Responses/UsersResponse.cs
Normal file
9
SharedProject/Models/Responses/UsersResponse.cs
Normal 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;
|
||||
}
|
@ -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();
|
||||
}
|
@ -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}")]
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user