diff --git a/DysonNetwork.Sphere/Activity/Activity.cs b/DysonNetwork.Sphere/Activity/Activity.cs index 4e90e80..fba0f2c 100644 --- a/DysonNetwork.Sphere/Activity/Activity.cs +++ b/DysonNetwork.Sphere/Activity/Activity.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace DysonNetwork.Sphere.Activity; @@ -6,6 +7,7 @@ public enum ActivityVisibility { Public, Friends, + Selected } public class Activity : ModelBase @@ -14,8 +16,11 @@ public class Activity : ModelBase [MaxLength(1024)] public string Type { get; set; } = null!; [MaxLength(4096)] public string ResourceIdentifier { get; set; } = null!; public ActivityVisibility Visibility { get; set; } = ActivityVisibility.Public; - public Dictionary Meta = new(); - + [Column(TypeName = "jsonb")] public Dictionary Meta = new(); + [Column(TypeName = "jsonb")] public ICollection UsersVisible = new List(); + public long AccountId { get; set; } public Account.Account Account { get; set; } = null!; + + [NotMapped] public object? Data { get; set; } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/Activity/ActivityController.cs b/DysonNetwork.Sphere/Activity/ActivityController.cs index b772ac0..2f362ae 100644 --- a/DysonNetwork.Sphere/Activity/ActivityController.cs +++ b/DysonNetwork.Sphere/Activity/ActivityController.cs @@ -13,21 +13,22 @@ public class ActivityController(AppDatabase db, ActivityService act, Relationshi { HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); var currentUser = currentUserValue as Account.Account; - var userFriends = await rels.ListAccountFriends(currentUser!); + var userFriends = currentUser is null ? null : await rels.ListAccountFriends(currentUser); var totalCount = await db.Activities .FilterWithVisibility(currentUser, userFriends) .CountAsync(); - var posts = await db.Activities + var activities = await db.Activities .Include(e => e.Account) .FilterWithVisibility(currentUser, userFriends) .OrderByDescending(e => e.CreatedAt) .Skip(offset) .Take(take) .ToListAsync(); + activities = await act.LoadActivityData(activities, currentUser, userFriends); Response.Headers["X-Total"] = totalCount.ToString(); - return Ok(posts); + return Ok(activities); } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/Activity/ActivityService.cs b/DysonNetwork.Sphere/Activity/ActivityService.cs index 4258ca1..e2d1583 100644 --- a/DysonNetwork.Sphere/Activity/ActivityService.cs +++ b/DysonNetwork.Sphere/Activity/ActivityService.cs @@ -1,15 +1,59 @@ using DysonNetwork.Sphere.Post; +using Microsoft.EntityFrameworkCore; using NodaTime; namespace DysonNetwork.Sphere.Activity; public class ActivityService(AppDatabase db) { + public async Task> LoadActivityData(List input, Account.Account? currentUser, + List userFriends) + { + if (input.Count == 0) return input; + + var postsId = input + .Where(e => e.ResourceIdentifier.StartsWith("posts/")) + .Select(e => long.Parse(e.ResourceIdentifier.Split("/").Last())) + .Distinct() + .ToList(); + + if (postsId.Count > 0) + { + var posts = await db.Posts.Where(e => postsId.Contains(e.Id)) + .Include(e => e.Publisher) + .Include(e => e.Publisher.Picture) + .Include(e => e.Publisher.Background) + .Include(e => e.ThreadedPost) + .Include(e => e.ForwardedPost) + .Include(e => e.Attachments) + .Include(e => e.Categories) + .Include(e => e.Tags) + .FilterWithVisibility(currentUser, userFriends) + .ToListAsync(); + + var postsDict = posts.ToDictionary(p => p.Id); + + for (var idx = 0; idx < input.Count; idx++) + { + var resourceIdentifier = input[idx].ResourceIdentifier; + if (!resourceIdentifier.StartsWith("posts/")) continue; + var postId = long.Parse(resourceIdentifier.Split("/").Last()); + if (postsDict.TryGetValue(postId, out var post) && input[idx].Data is null) + { + input[idx].Data = post; + } + } + } + + return input; + } + public async Task CreateActivity( Account.Account user, string type, string identifier, - ActivityVisibility visibility = ActivityVisibility.Public + ActivityVisibility visibility = ActivityVisibility.Public, + List? visibleUsers = null ) { var activity = new Activity @@ -18,6 +62,7 @@ public class ActivityService(AppDatabase db) ResourceIdentifier = identifier, Visibility = visibility, AccountId = user.Id, + UsersVisible = visibleUsers ?? [] }; db.Activities.Add(activity); @@ -31,8 +76,26 @@ public class ActivityService(AppDatabase db) if (post.Visibility is PostVisibility.Unlisted or PostVisibility.Private) return; var identifier = $"posts/{post.Id}"; - await CreateActivity(user, "posts.new", identifier, - post.Visibility == PostVisibility.Friends ? ActivityVisibility.Friends : ActivityVisibility.Public); + if (post.RepliedPostId is not null) + { + var ogPost = await db.Posts.Where(e => e.Id == post.RepliedPostId).Include(e => e.Publisher) + .FirstOrDefaultAsync(); + if (ogPost == null) return; + await CreateActivity( + user, + "posts.new.replies", + identifier, + ActivityVisibility.Selected, + [ogPost.Publisher.AccountId!.Value] + ); + } + + await CreateActivity( + user, + "posts.new", + identifier, + post.Visibility == PostVisibility.Friends ? ActivityVisibility.Friends : ActivityVisibility.Public + ); } } @@ -41,14 +104,13 @@ public static class ActivityQueryExtensions public static IQueryable FilterWithVisibility(this IQueryable source, Account.Account? currentUser, List userFriends) { - var now = Instant.FromDateTimeUtc(DateTime.UtcNow); - if (currentUser is null) return source.Where(e => e.Visibility == ActivityVisibility.Public); return source .Where(e => e.Visibility != ActivityVisibility.Friends || userFriends.Contains(e.AccountId) || - e.AccountId == currentUser.Id); + e.AccountId == currentUser.Id) + .Where(e => e.Visibility != ActivityVisibility.Selected || e.UsersVisible.Contains(currentUser.Id)); } } \ No newline at end of file