using DysonNetwork.Sphere.Post; using Microsoft.EntityFrameworkCore; using NodaTime; namespace DysonNetwork.Sphere.Activity; public class ActivityReaderService(AppDatabase db, PostService ps) { 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.ThreadedPost) .Include(e => e.ForwardedPost) .Include(e => e.Attachments) .Include(e => e.Categories) .Include(e => e.Tags) .FilterWithVisibility(currentUser, userFriends) .ToListAsync(); posts = PostService.TruncatePostContent(posts); var reactionMaps = await ps.GetPostReactionMapBatch(postsId); foreach (var post in posts) post.ReactionsCount = reactionMaps.TryGetValue(post.Id, out var count) ? count : new Dictionary(); var postsDict = posts.ToDictionary(p => p.Id); foreach (var item in input) { var resourceIdentifier = item.ResourceIdentifier; if (!resourceIdentifier.StartsWith("posts/")) continue; var postId = long.Parse(resourceIdentifier.Split("/").Last()); if (postsDict.TryGetValue(postId, out var post) && item.Data is null) { item.Data = post; } } } var statusesId = input .Where(e => e.ResourceIdentifier.StartsWith("account.statuses/")) .Select(e => Guid.Parse(e.ResourceIdentifier.Split("/").Last())) .Distinct() .ToList(); if (statusesId.Count > 0) { var statuses = await db.AccountStatuses.Where(e => statusesId.Contains(e.Id)) .Include(e => e.Account) .Include(e => e.Account.Profile) .ToListAsync(); var statusesDict = statuses.ToDictionary(p => p.Id); foreach (var item in input) { var resourceIdentifier = item.ResourceIdentifier; if (!resourceIdentifier.StartsWith("account.statuses/")) continue; var statusId = Guid.Parse(resourceIdentifier.Split("/").Last()); if (statusesDict.TryGetValue(statusId, out var status) && item.Data is null) { item.Data = status; } } } var checkInId = input .Where(e => e.ResourceIdentifier.StartsWith("account.check-in/")) .Select(e => Guid.Parse(e.ResourceIdentifier.Split("/").Last())) .Distinct() .ToList(); if (checkInId.Count > 0) { var checkIns = await db.AccountCheckInResults.Where(e => checkInId.Contains(e.Id)) .Include(e => e.Account) .Include(e => e.Account.Profile) .ToListAsync(); var checkInsDict = checkIns.ToDictionary(p => p.Id); foreach (var item in input) { var resourceIdentifier = item.ResourceIdentifier; if (!resourceIdentifier.StartsWith("account.check-in/")) continue; var checkInResultId = Guid.Parse(resourceIdentifier.Split("/").Last()); if (checkInsDict.TryGetValue(checkInResultId, out var checkIn) && item.Data is null) { item.Data = checkIn; } } } return input; } } public class ActivityService(AppDatabase db) { public async Task CreateActivity( Account.Account user, string type, string identifier, ActivityVisibility visibility = ActivityVisibility.Public, List? visibleUsers = null ) { var activity = new Activity { Type = type, ResourceIdentifier = identifier, Visibility = visibility, AccountId = user.Id, UsersVisible = visibleUsers ?? [] }; db.Activities.Add(activity); await db.SaveChangesAsync(); return activity; } public async Task CreateNewPostActivity(Account.Account user, Post.Post post) { if (post.Visibility is PostVisibility.Unlisted or PostVisibility.Private) return; var identifier = $"posts/{post.Id}"; 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 ); } } public static class ActivityQueryExtensions { public static IQueryable FilterWithVisibility(this IQueryable source, Account.Account? currentUser, List userFriends) { 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) .Where(e => e.Visibility != ActivityVisibility.Selected || EF.Functions.JsonExists(e.UsersVisible, currentUser.Id.ToString())); } }