From 27276c66c5785920f4fab330fcdcc7a35fb07969 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 20 Jun 2025 00:31:16 +0800 Subject: [PATCH] :sparkles: Post reactions and replies counting :art: Improve the styles of post service --- .../Activity/ActivityService.cs | 6 +- DysonNetwork.Sphere/Post/Post.cs | 1 + DysonNetwork.Sphere/Post/PostController.cs | 27 +------- DysonNetwork.Sphere/Post/PostService.cs | 62 +++++++++++++++++++ 4 files changed, 68 insertions(+), 28 deletions(-) diff --git a/DysonNetwork.Sphere/Activity/ActivityService.cs b/DysonNetwork.Sphere/Activity/ActivityService.cs index c59b746..e294a2f 100644 --- a/DysonNetwork.Sphere/Activity/ActivityService.cs +++ b/DysonNetwork.Sphere/Activity/ActivityService.cs @@ -24,8 +24,7 @@ public class ActivityService(AppDatabase db, PublisherService pub, RelationshipS .FilterWithVisibility(null, [], [], isListing: true) .Take(take) .ToListAsync(); - posts = PostService.TruncatePostContent(posts); - posts = await ps.LoadPublishers(posts); + posts = await ps.LoadPostInfo(posts, null, true); var postsId = posts.Select(e => e.Id).ToList(); var reactionMaps = await ps.GetPostReactionMapBatch(postsId); @@ -63,8 +62,7 @@ public class ActivityService(AppDatabase db, PublisherService pub, RelationshipS .FilterWithVisibility(currentUser, userFriends, userPublishers, isListing: true) .Take(take) .ToListAsync(); - posts = PostService.TruncatePostContent(posts); - posts = await ps.LoadPublishers(posts); + posts = await ps.LoadPostInfo(posts, currentUser, true); var postsId = posts.Select(e => e.Id).ToList(); var reactionMaps = await ps.GetPostReactionMapBatch(postsId); diff --git a/DysonNetwork.Sphere/Post/Post.cs b/DysonNetwork.Sphere/Post/Post.cs index d3ce4ea..0a107bd 100644 --- a/DysonNetwork.Sphere/Post/Post.cs +++ b/DysonNetwork.Sphere/Post/Post.cs @@ -44,6 +44,7 @@ public class Post : ModelBase, IIdentifiedResource, IActivity public int Upvotes { get; set; } public int Downvotes { get; set; } [NotMapped] public Dictionary ReactionsCount { get; set; } = new(); + [NotMapped] public int RepliesCount { get; set; } public Guid? RepliedPostId { get; set; } public Post? RepliedPost { get; set; } diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 1a9820a..cbfafcf 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -52,26 +52,7 @@ public class PostController( .Skip(offset) .Take(take) .ToListAsync(); - posts = PostService.TruncatePostContent(posts); - posts = await ps.LoadPublishers(posts); - - var postsId = posts.Select(e => e.Id).ToList(); - var reactionMaps = await ps.GetPostReactionMapBatch(postsId); - foreach (var post in posts) - { - post.ReactionsCount = - reactionMaps.TryGetValue(post.Id, out var count) ? count : new Dictionary(); - - // Track view for each post in the list - if (currentUser != null) - { - await ps.IncreaseViewCount(post.Id, currentUser.Id.ToString()); - } - else - { - await ps.IncreaseViewCount(post.Id); - } - } + posts = await ps.LoadPostInfo(posts, currentUser, true); Response.Headers["X-Total"] = totalCount.ToString(); @@ -94,8 +75,7 @@ public class PostController( .FilterWithVisibility(currentUser, userFriends, userPublishers) .FirstOrDefaultAsync(); if (post is null) return NotFound(); - - post.ReactionsCount = await ps.GetPostReactionMap(post.Id); + post = await ps.LoadPostInfo(post, currentUser); // Track view - use the account ID as viewer ID if user is logged in await ps.IncreaseViewCount(post.Id, currentUser?.Id.ToString()); @@ -131,8 +111,7 @@ public class PostController( .Skip(offset) .Take(take) .ToListAsync(); - posts = PostService.TruncatePostContent(posts); - posts = await ps.LoadPublishers(posts); + posts = await ps.LoadPostInfo(posts, currentUser, true); var postsId = posts.Select(e => e.Id).ToList(); var reactionMaps = await ps.GetPostReactionMapBatch(postsId); diff --git a/DysonNetwork.Sphere/Post/PostService.cs b/DysonNetwork.Sphere/Post/PostService.cs index 5a7e2ee..a0759b1 100644 --- a/DysonNetwork.Sphere/Post/PostService.cs +++ b/DysonNetwork.Sphere/Post/PostService.cs @@ -435,6 +435,68 @@ public class PostService( return posts; } + + public async Task> LoadInteractive(List posts, Account.Account? currentUser = null) + { + if (posts.Count == 0) return posts; + + var postsId = posts.Select(e => e.Id).ToList(); + + var reactionMaps = await GetPostReactionMapBatch(postsId); + var repliesCountMap = await GetPostRepliesCountBatch(postsId); + + foreach (var post in posts) + { + // Set reactions count + post.ReactionsCount = reactionMaps.TryGetValue(post.Id, out var count) + ? count + : new Dictionary(); + + // Set replies count + post.RepliesCount = repliesCountMap.TryGetValue(post.Id, out var repliesCount) + ? repliesCount + : 0; + + // Track view for each post in the list + if (currentUser != null) + await IncreaseViewCount(post.Id, currentUser.Id.ToString()); + else + await IncreaseViewCount(post.Id); + } + + return posts; + } + + private async Task> GetPostRepliesCountBatch(List postIds) + { + return await db.Posts + .Where(p => p.RepliedPostId != null && postIds.Contains(p.RepliedPostId.Value)) + .GroupBy(p => p.RepliedPostId!.Value) + .ToDictionaryAsync( + g => g.Key, + g => g.Count() + ); + } + + public async Task> LoadPostInfo(List posts, Account.Account? currentUser = null, bool truncate = false) + { + if (posts.Count == 0) return posts; + + posts = await LoadPublishers(posts); + posts = await LoadInteractive(posts, currentUser); + + if (truncate) + posts = TruncatePostContent(posts); + + return posts; + } + + public async Task LoadPostInfo(Post post, Account.Account? currentUser = null, bool truncate = false) + { + // Convert single post to list, process it, then return the single post + var posts = await LoadPostInfo([post], currentUser, truncate); + return posts.First(); + } } public static class PostQueryExtensions