[backend/api] Add relations to user profile response (ISH-347)
This commit is contained in:
parent
167fd5f0d6
commit
ffa46eded6
3 changed files with 70 additions and 13 deletions
|
@ -10,12 +10,24 @@ public class UserProfileRenderer(DatabaseContext db)
|
||||||
{
|
{
|
||||||
public async Task<UserProfileResponse> RenderOne(User user, User? localUser, UserRendererDto? data = null)
|
public async Task<UserProfileResponse> RenderOne(User user, User? localUser, UserRendererDto? data = null)
|
||||||
{
|
{
|
||||||
var isFollowing = (data?.Following ?? await GetFollowing([user], localUser)).Contains(user.Id);
|
(data?.Relations ?? await GetRelations([user], localUser)).TryGetValue(user.Id, out var relations);
|
||||||
|
relations ??= new RelationData
|
||||||
|
{
|
||||||
|
UserId = user.Id,
|
||||||
|
IsSelf = user.Id == localUser?.Id,
|
||||||
|
IsFollowing = false,
|
||||||
|
IsFollowedBy = false,
|
||||||
|
IsBlocking = false,
|
||||||
|
IsMuting = false,
|
||||||
|
IsRequested = false,
|
||||||
|
IsRequestedBy = false
|
||||||
|
};
|
||||||
|
|
||||||
var ffVisibility = user.UserProfile?.FFVisibility ?? UserProfile.UserProfileFFVisibility.Public;
|
var ffVisibility = user.UserProfile?.FFVisibility ?? UserProfile.UserProfileFFVisibility.Public;
|
||||||
var followers = ffVisibility switch
|
var followers = ffVisibility switch
|
||||||
{
|
{
|
||||||
UserProfile.UserProfileFFVisibility.Public => user.FollowersCount,
|
UserProfile.UserProfileFFVisibility.Public => user.FollowersCount,
|
||||||
UserProfile.UserProfileFFVisibility.Followers => isFollowing ? user.FollowersCount : null,
|
UserProfile.UserProfileFFVisibility.Followers => relations.IsFollowing ? user.FollowersCount : null,
|
||||||
UserProfile.UserProfileFFVisibility.Private => (int?)null,
|
UserProfile.UserProfileFFVisibility.Private => (int?)null,
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
|
@ -23,7 +35,7 @@ public class UserProfileRenderer(DatabaseContext db)
|
||||||
var following = ffVisibility switch
|
var following = ffVisibility switch
|
||||||
{
|
{
|
||||||
UserProfile.UserProfileFFVisibility.Public => user.FollowingCount,
|
UserProfile.UserProfileFFVisibility.Public => user.FollowingCount,
|
||||||
UserProfile.UserProfileFFVisibility.Followers => isFollowing ? user.FollowingCount : null,
|
UserProfile.UserProfileFFVisibility.Followers => relations.IsFollowing ? user.FollowingCount : null,
|
||||||
UserProfile.UserProfileFFVisibility.Private => (int?)null,
|
UserProfile.UserProfileFFVisibility.Private => (int?)null,
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
|
@ -43,27 +55,42 @@ public class UserProfileRenderer(DatabaseContext db)
|
||||||
Fields = fields?.ToList(),
|
Fields = fields?.ToList(),
|
||||||
Location = user.UserProfile?.Location,
|
Location = user.UserProfile?.Location,
|
||||||
Followers = followers,
|
Followers = followers,
|
||||||
Following = following
|
Following = following,
|
||||||
|
Relations = relations
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<string>> GetFollowing(IEnumerable<User> users, User? localUser)
|
private async Task<Dictionary<string, RelationData>> GetRelations(IEnumerable<User> users, User? localUser)
|
||||||
{
|
{
|
||||||
|
var ids = users.Select(p => p.Id).ToList();
|
||||||
|
if (ids.Count == 0) return [];
|
||||||
if (localUser == null) return [];
|
if (localUser == null) return [];
|
||||||
return await db.Followings.Where(p => p.Follower == localUser && users.Contains(p.Followee))
|
|
||||||
.Select(p => p.FolloweeId)
|
return await db.Users
|
||||||
.ToListAsync();
|
.Where(p => ids.Contains(p.Id))
|
||||||
|
.Select(p => new RelationData
|
||||||
|
{
|
||||||
|
UserId = p.Id,
|
||||||
|
IsSelf = p.Id == localUser.Id,
|
||||||
|
IsFollowing = localUser.IsFollowing(p),
|
||||||
|
IsFollowedBy = localUser.IsFollowedBy(p),
|
||||||
|
IsBlocking = localUser.IsBlocking(p),
|
||||||
|
IsMuting = localUser.IsMuting(p),
|
||||||
|
IsRequested = localUser.IsRequested(p),
|
||||||
|
IsRequestedBy = localUser.IsRequestedBy(p)
|
||||||
|
})
|
||||||
|
.ToDictionaryAsync(p => p.UserId, p => p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<UserProfileResponse>> RenderMany(IEnumerable<User> users, User? localUser)
|
public async Task<IEnumerable<UserProfileResponse>> RenderMany(IEnumerable<User> users, User? localUser)
|
||||||
{
|
{
|
||||||
var userList = users.ToList();
|
var userList = users.ToList();
|
||||||
var data = new UserRendererDto { Following = await GetFollowing(userList, localUser) };
|
var data = new UserRendererDto { Relations = await GetRelations(userList, localUser) };
|
||||||
return await userList.Select(p => RenderOne(p, localUser, data)).AwaitAllAsync();
|
return await userList.Select(p => RenderOne(p, localUser, data)).AwaitAllAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserRendererDto
|
public class UserRendererDto
|
||||||
{
|
{
|
||||||
public List<string>? Following;
|
public Dictionary<string, RelationData>? Relations;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,8 @@ using Microsoft.EntityFrameworkCore;
|
||||||
namespace Iceshrimp.Backend.Controllers;
|
namespace Iceshrimp.Backend.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
[Authenticate]
|
||||||
|
[Authorize]
|
||||||
[EnableRateLimiting("sliding")]
|
[EnableRateLimiting("sliding")]
|
||||||
[Route("/api/iceshrimp/users/{id}")]
|
[Route("/api/iceshrimp/users/{id}")]
|
||||||
[Produces(MediaTypeNames.Application.Json)]
|
[Produces(MediaTypeNames.Application.Json)]
|
||||||
|
@ -51,7 +53,6 @@ public class UserController(
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("notes")]
|
[HttpGet("notes")]
|
||||||
[Authenticate]
|
|
||||||
[LinkPagination(20, 80)]
|
[LinkPagination(20, 80)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<NoteResponse>))]
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<NoteResponse>))]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))]
|
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Iceshrimp.Shared.Schemas;
|
namespace Iceshrimp.Shared.Schemas;
|
||||||
|
|
||||||
public class UserProfileResponse
|
public class UserProfileResponse
|
||||||
|
@ -9,6 +11,33 @@ public class UserProfileResponse
|
||||||
public required string? Bio { get; set; }
|
public required string? Bio { get; set; }
|
||||||
public required int? Followers { get; set; }
|
public required int? Followers { get; set; }
|
||||||
public required int? Following { get; set; }
|
public required int? Following { get; set; }
|
||||||
|
public required RelationData Relations { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RelationData
|
||||||
|
{
|
||||||
|
[JsonIgnore] public required string UserId;
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsSelf { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsFollowing { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsFollowedBy { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsRequested { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsRequestedBy { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsBlocking { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public required bool IsMuting { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserProfileField
|
public class UserProfileField
|
||||||
|
|
Loading…
Add table
Reference in a new issue