Updated follow APIs for better usability

This commit is contained in:
2025-12-31 00:58:13 +08:00
parent f2856c10a3
commit b9230699c5

View File

@@ -7,6 +7,43 @@ using NodaTime;
namespace DysonNetwork.Sphere.ActivityPub; namespace DysonNetwork.Sphere.ActivityPub;
public class FediverseActorWithFollowStatus : SnFediverseActor
{
public bool IsFollowing { get; set; }
public static FediverseActorWithFollowStatus FromActor(SnFediverseActor actor, bool isFollowing = false)
{
return new FediverseActorWithFollowStatus
{
Id = actor.Id,
Type = actor.Type,
Uri = actor.Uri,
Username = actor.Username,
DisplayName = actor.DisplayName,
Bio = actor.Bio,
InboxUri = actor.InboxUri,
OutboxUri = actor.OutboxUri,
FollowersUri = actor.FollowersUri,
FollowingUri = actor.FollowingUri,
FeaturedUri = actor.FeaturedUri,
PublicKeyId = actor.PublicKeyId,
PublicKey = actor.PublicKey,
Metadata = actor.Metadata,
AvatarUrl = actor.AvatarUrl,
HeaderUrl = actor.HeaderUrl,
IsBot = actor.IsBot,
IsLocked = actor.IsLocked,
IsDiscoverable = actor.IsDiscoverable,
InstanceId = actor.InstanceId,
Instance = actor.Instance,
LastFetchedAt = actor.LastFetchedAt,
LastActivityAt = actor.LastActivityAt,
PublisherId = actor.PublisherId,
IsFollowing = isFollowing
};
}
}
[ApiController] [ApiController]
[Route("/api/activitypub")] [Route("/api/activitypub")]
[Authorize] [Authorize]
@@ -90,8 +127,9 @@ public class ActivityPubFollowController(
} }
[HttpGet("following")] [HttpGet("following")]
public async Task<ActionResult<List<SnFediverseActor>>> GetFollowing( public async Task<ActionResult<List<FediverseActorWithFollowStatus>>> GetFollowing(
[FromQuery] int limit = 50 [FromQuery] int take = 50,
[FromQuery] int offset = 0
) )
{ {
var currentUser = GetCurrentUser(); var currentUser = GetCurrentUser();
@@ -104,7 +142,16 @@ public class ActivityPubFollowController(
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (publisher == null) if (publisher == null)
return Ok(new List<SnFediverseActor>()); {
Response.Headers.Append("X-Total", "0");
return Ok(new List<FediverseActorWithFollowStatus>());
}
var totalCount = await db.FediverseRelationships
.CountAsync(r =>
r.Actor.PublisherId == publisher.Id &&
r.IsFollowing &&
r.State == RelationshipState.Accepted);
var actors = await db.FediverseRelationships var actors = await db.FediverseRelationships
.Include(r => r.TargetActor) .Include(r => r.TargetActor)
@@ -114,16 +161,21 @@ public class ActivityPubFollowController(
r.IsFollowing && r.IsFollowing &&
r.State == RelationshipState.Accepted) r.State == RelationshipState.Accepted)
.OrderByDescending(r => r.FollowedAt) .OrderByDescending(r => r.FollowedAt)
.Skip(offset)
.Take(take)
.Select(r => r.TargetActor) .Select(r => r.TargetActor)
.Take(limit)
.ToListAsync(); .ToListAsync();
return Ok(actors); var result = actors.Select(a => FediverseActorWithFollowStatus.FromActor(a, true)).ToList();
Response.Headers.Append("X-Total", totalCount.ToString());
return Ok(result);
} }
[HttpGet("followers")] [HttpGet("followers")]
public async Task<ActionResult<List<SnFediverseActor>>> GetFollowers( public async Task<ActionResult<List<FediverseActorWithFollowStatus>>> GetFollowers(
[FromQuery] int limit = 50 [FromQuery] int take = 50,
[FromQuery] int offset = 0
) )
{ {
var currentUser = GetCurrentUser(); var currentUser = GetCurrentUser();
@@ -136,7 +188,16 @@ public class ActivityPubFollowController(
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (publisher == null) if (publisher == null)
return Ok(new List<SnFediverseActor>()); {
Response.Headers.Append("X-Total", "0");
return Ok(new List<FediverseActorWithFollowStatus>());
}
var totalCount = await db.FediverseRelationships
.CountAsync(r =>
r.Actor.PublisherId == publisher.Id &&
r.IsFollowedBy &&
r.State == RelationshipState.Accepted);
var actors = await db.FediverseRelationships var actors = await db.FediverseRelationships
.Include(r => r.Actor) .Include(r => r.Actor)
@@ -146,15 +207,19 @@ public class ActivityPubFollowController(
r.IsFollowedBy && r.IsFollowedBy &&
r.State == RelationshipState.Accepted) r.State == RelationshipState.Accepted)
.OrderByDescending(r => r.FollowedAt ?? r.CreatedAt) .OrderByDescending(r => r.FollowedAt ?? r.CreatedAt)
.Skip(offset)
.Take(take)
.Select(r => r.Actor) .Select(r => r.Actor)
.Take(limit)
.ToListAsync(); .ToListAsync();
return Ok(actors); var result = await GetActorsWithFollowStatusAsync(actors, publisher.Id);
Response.Headers.Append("X-Total", totalCount.ToString());
return Ok(result);
} }
[HttpGet("search")] [HttpGet("search")]
public async Task<ActionResult<List<SnFediverseActor>>> SearchRemoteUsers( public async Task<ActionResult<List<FediverseActorWithFollowStatus>>> SearchRemoteUsers(
[FromQuery] string query, [FromQuery] string query,
[FromQuery] int limit = 20 [FromQuery] int limit = 20
) )
@@ -164,7 +229,21 @@ public class ActivityPubFollowController(
var actors = await discSrv.SearchActorsAsync(query, limit, includeRemoteDiscovery: true); var actors = await discSrv.SearchActorsAsync(query, limit, includeRemoteDiscovery: true);
return Ok(actors); var currentUser = GetCurrentUser();
if (currentUser == null)
return Ok(actors.Select(a => FediverseActorWithFollowStatus.FromActor(a)).ToList());
var publisher = await db.Publishers
.Include(p => p.Members)
.Where(p => p.Members.Any(m => m.AccountId == currentUser))
.FirstOrDefaultAsync();
if (publisher == null)
return Ok(actors.Select(a => FediverseActorWithFollowStatus.FromActor(a)).ToList());
var result = await GetActorsWithFollowStatusAsync(actors, publisher.Id);
return Ok(result);
} }
[HttpGet("relationships")] [HttpGet("relationships")]
@@ -344,6 +423,41 @@ public class ActivityPubFollowController(
} }
} }
private async Task<List<FediverseActorWithFollowStatus>> GetActorsWithFollowStatusAsync(
List<SnFediverseActor> actors,
Guid publisherId
)
{
if (actors.Count == 0)
return new List<FediverseActorWithFollowStatus>();
var actorIds = actors.Select(a => a.Id).ToList();
var userActor = await db.FediverseActors
.Where(a => a.PublisherId == publisherId)
.FirstOrDefaultAsync();
if (userActor == null)
return actors.Select(a => FediverseActorWithFollowStatus.FromActor(a, false)).ToList();
var followingActorIds = await db.FediverseRelationships
.Where(r =>
r.ActorId == userActor.Id &&
actorIds.Contains(r.TargetActorId) &&
r.IsFollowing &&
r.State == RelationshipState.Accepted)
.Select(r => r.TargetActorId)
.ToListAsync();
var result = actors.Select(actor =>
{
var isFollowing = followingActorIds.Contains(actor.Id);
return FediverseActorWithFollowStatus.FromActor(actor, isFollowing);
}).ToList();
return result;
}
private Guid? GetCurrentUser() private Guid? GetCurrentUser()
{ {
HttpContext.Items.TryGetValue("CurrentUser", out var currentUser); HttpContext.Items.TryGetValue("CurrentUser", out var currentUser);