Compare commits
	
		
			3 Commits
		
	
	
		
			8c748fd57a
			...
			915054fce0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 915054fce0 | |||
| 63653680ba | |||
| 84c4df6620 | 
| @@ -10,6 +10,8 @@ public abstract class ActionLogType | |||||||
|     public const string PostUpdate = "posts.update"; |     public const string PostUpdate = "posts.update"; | ||||||
|     public const string PostDelete = "posts.delete"; |     public const string PostDelete = "posts.delete"; | ||||||
|     public const string PostReact = "posts.react"; |     public const string PostReact = "posts.react"; | ||||||
|  |     public const string PostPin = "posts.pin"; | ||||||
|  |     public const string PostUnpin = "posts.unpin"; | ||||||
|     public const string MessageCreate = "messages.create"; |     public const string MessageCreate = "messages.create"; | ||||||
|     public const string MessageUpdate = "messages.update"; |     public const string MessageUpdate = "messages.update"; | ||||||
|     public const string MessageDelete = "messages.delete"; |     public const string MessageDelete = "messages.delete"; | ||||||
|   | |||||||
							
								
								
									
										2006
									
								
								DysonNetwork.Sphere/Migrations/20250825045548_AddPostPin.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2006
									
								
								DysonNetwork.Sphere/Migrations/20250825045548_AddPostPin.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										28
									
								
								DysonNetwork.Sphere/Migrations/20250825045548_AddPostPin.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								DysonNetwork.Sphere/Migrations/20250825045548_AddPostPin.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore.Migrations; | ||||||
|  |  | ||||||
|  | #nullable disable | ||||||
|  |  | ||||||
|  | namespace DysonNetwork.Sphere.Migrations | ||||||
|  | { | ||||||
|  |     /// <inheritdoc /> | ||||||
|  |     public partial class AddPostPin : Migration | ||||||
|  |     { | ||||||
|  |         /// <inheritdoc /> | ||||||
|  |         protected override void Up(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.AddColumn<int>( | ||||||
|  |                 name: "pin_mode", | ||||||
|  |                 table: "posts", | ||||||
|  |                 type: "integer", | ||||||
|  |                 nullable: true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc /> | ||||||
|  |         protected override void Down(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.DropColumn( | ||||||
|  |                 name: "pin_mode", | ||||||
|  |                 table: "posts"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -566,6 +566,10 @@ namespace DysonNetwork.Sphere.Migrations | |||||||
|                         .HasColumnType("jsonb") |                         .HasColumnType("jsonb") | ||||||
|                         .HasColumnName("meta"); |                         .HasColumnName("meta"); | ||||||
|  |  | ||||||
|  |                     b.Property<int?>("PinMode") | ||||||
|  |                         .HasColumnType("integer") | ||||||
|  |                         .HasColumnName("pin_mode"); | ||||||
|  |  | ||||||
|                     b.Property<Instant?>("PublishedAt") |                     b.Property<Instant?>("PublishedAt") | ||||||
|                         .HasColumnType("timestamp with time zone") |                         .HasColumnType("timestamp with time zone") | ||||||
|                         .HasColumnName("published_at"); |                         .HasColumnName("published_at"); | ||||||
|   | |||||||
| @@ -23,6 +23,13 @@ public enum PostVisibility | |||||||
|     Private |     Private | ||||||
| } | } | ||||||
|  |  | ||||||
|  | public enum PostPinMode | ||||||
|  | { | ||||||
|  |     PublisherPage, | ||||||
|  |     RealmPage, | ||||||
|  |     ReplyPage, | ||||||
|  | } | ||||||
|  |  | ||||||
| public class Post : ModelBase, IIdentifiedResource, IActivity | public class Post : ModelBase, IIdentifiedResource, IActivity | ||||||
| { | { | ||||||
|     public Guid Id { get; set; } |     public Guid Id { get; set; } | ||||||
| @@ -37,6 +44,7 @@ public class Post : ModelBase, IIdentifiedResource, IActivity | |||||||
|     public string? Content { get; set; } |     public string? Content { get; set; } | ||||||
|  |  | ||||||
|     public PostType Type { get; set; } |     public PostType Type { get; set; } | ||||||
|  |     public PostPinMode? PinMode { get; set; } | ||||||
|     [Column(TypeName = "jsonb")] public Dictionary<string, object>? Meta { get; set; } |     [Column(TypeName = "jsonb")] public Dictionary<string, object>? Meta { get; set; } | ||||||
|     [Column(TypeName = "jsonb")] public List<ContentSensitiveMark>? SensitiveMarks { get; set; } = []; |     [Column(TypeName = "jsonb")] public List<ContentSensitiveMark>? SensitiveMarks { get; set; } = []; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -81,7 +81,8 @@ public class PostController( | |||||||
|         [FromQuery(Name = "query")] string? queryTerm = null, |         [FromQuery(Name = "query")] string? queryTerm = null, | ||||||
|         [FromQuery(Name = "vector")] bool queryVector = false, |         [FromQuery(Name = "vector")] bool queryVector = false, | ||||||
|         [FromQuery(Name = "media")] bool onlyMedia = false, |         [FromQuery(Name = "media")] bool onlyMedia = false, | ||||||
|         [FromQuery(Name = "shuffle")] bool shuffle = false |         [FromQuery(Name = "shuffle")] bool shuffle = false, | ||||||
|  |         [FromQuery(Name = "pinned")] bool pinned = false | ||||||
|     ) |     ) | ||||||
|     { |     { | ||||||
|         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); |         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); | ||||||
| @@ -91,7 +92,7 @@ public class PostController( | |||||||
|         if (currentUser != null) |         if (currentUser != null) | ||||||
|         { |         { | ||||||
|             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest |             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|                 { AccountId = currentUser.Id }); |             { AccountId = currentUser.Id }); | ||||||
|             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); |             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -117,6 +118,13 @@ public class PostController( | |||||||
|         if (onlyMedia) |         if (onlyMedia) | ||||||
|             query = query.Where(e => e.Attachments.Count > 0); |             query = query.Where(e => e.Attachments.Count > 0); | ||||||
|  |  | ||||||
|  |         if (pinned) | ||||||
|  |         { | ||||||
|  |             if (realm != null) query = query.Where(p => p.PinMode == PostPinMode.RealmPage); | ||||||
|  |             else if (publisher != null) query = query.Where(p => p.PinMode == PostPinMode.PublisherPage); | ||||||
|  |             else return BadRequest("You need pass extra realm or publisher params in order to filter with pinned posts."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         query = includeReplies switch |         query = includeReplies switch | ||||||
|         { |         { | ||||||
|             false => query.Where(e => e.RepliedPostId == null), |             false => query.Where(e => e.RepliedPostId == null), | ||||||
| @@ -169,7 +177,7 @@ public class PostController( | |||||||
|         if (currentUser != null) |         if (currentUser != null) | ||||||
|         { |         { | ||||||
|             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest |             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|                 { AccountId = currentUser.Id }); |             { AccountId = currentUser.Id }); | ||||||
|             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); |             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -188,9 +196,6 @@ public class PostController( | |||||||
|         if (post is null) return NotFound(); |         if (post is null) return NotFound(); | ||||||
|         post = await ps.LoadPostInfo(post, currentUser); |         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); |  | ||||||
|  |  | ||||||
|         return Ok(post); |         return Ok(post); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -203,7 +208,7 @@ public class PostController( | |||||||
|         if (currentUser != null) |         if (currentUser != null) | ||||||
|         { |         { | ||||||
|             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest |             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|                 { AccountId = currentUser.Id }); |             { AccountId = currentUser.Id }); | ||||||
|             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); |             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -222,9 +227,6 @@ public class PostController( | |||||||
|         if (post is null) return NotFound(); |         if (post is null) return NotFound(); | ||||||
|         post = await ps.LoadPostInfo(post, currentUser); |         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); |  | ||||||
|  |  | ||||||
|         return Ok(post); |         return Ok(post); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -261,7 +263,7 @@ public class PostController( | |||||||
|         if (currentUser != null) |         if (currentUser != null) | ||||||
|         { |         { | ||||||
|             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest |             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|                 { AccountId = currentUser.Id }); |             { AccountId = currentUser.Id }); | ||||||
|             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); |             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -278,12 +280,36 @@ public class PostController( | |||||||
|             .FilterWithVisibility(currentUser, userFriends, userPublishers) |             .FilterWithVisibility(currentUser, userFriends, userPublishers) | ||||||
|             .FirstOrDefaultAsync(); |             .FirstOrDefaultAsync(); | ||||||
|         if (post is null) return NotFound(); |         if (post is null) return NotFound(); | ||||||
|         post = await ps.LoadPostInfo(post, currentUser); |         post = await ps.LoadPostInfo(post, currentUser, true); | ||||||
|  |  | ||||||
|         // Track view - use the account ID as viewer ID if user is logged in |         return Ok(post); | ||||||
|         await ps.IncreaseViewCount(post.Id, currentUser?.Id); |     } | ||||||
|  |  | ||||||
|         return await ps.LoadPostInfo(post); |     [HttpGet("{id:guid}/replies/pinned")] | ||||||
|  |     public async Task<ActionResult<List<Post>>> ListPinnedReplies(Guid id) | ||||||
|  |     { | ||||||
|  |         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); | ||||||
|  |         var currentUser = currentUserValue as Account; | ||||||
|  |         List<Guid> userFriends = []; | ||||||
|  |         if (currentUser != null) | ||||||
|  |         { | ||||||
|  |             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|  |             { AccountId = currentUser.Id }); | ||||||
|  |             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(Guid.Parse(currentUser.Id)); | ||||||
|  |  | ||||||
|  |         var now = SystemClock.Instance.GetCurrentInstant(); | ||||||
|  |         var posts = await db.Posts | ||||||
|  |             .Where(e => e.RepliedPostId == id && e.PinMode == PostPinMode.ReplyPage) | ||||||
|  |             .OrderByDescending(p => p.CreatedAt) | ||||||
|  |             .FilterWithVisibility(currentUser, userFriends, userPublishers) | ||||||
|  |             .ToListAsync(); | ||||||
|  |         if (posts is null) return NotFound(); | ||||||
|  |         posts = await ps.LoadPostInfo(posts, currentUser); | ||||||
|  |  | ||||||
|  |         return Ok(posts); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [HttpGet("{id:guid}/replies")] |     [HttpGet("{id:guid}/replies")] | ||||||
| @@ -297,7 +323,7 @@ public class PostController( | |||||||
|         if (currentUser != null) |         if (currentUser != null) | ||||||
|         { |         { | ||||||
|             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest |             var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|                 { AccountId = currentUser.Id }); |             { AccountId = currentUser.Id }); | ||||||
|             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); |             userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -484,7 +510,7 @@ public class PostController( | |||||||
|  |  | ||||||
|         var friendsResponse = |         var friendsResponse = | ||||||
|             await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest |             await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest | ||||||
|                 { AccountId = currentUser.Id.ToString() }); |             { AccountId = currentUser.Id.ToString() }); | ||||||
|         var userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); |         var userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); | ||||||
|         var userPublishers = await pub.GetUserPublishers(Guid.Parse(currentUser.Id)); |         var userPublishers = await pub.GetUserPublishers(Guid.Parse(currentUser.Id)); | ||||||
|  |  | ||||||
| @@ -535,6 +561,88 @@ public class PostController( | |||||||
|         return Ok(reaction); |         return Ok(reaction); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public class PostPinRequest | ||||||
|  |     { | ||||||
|  |         [Required] public PostPinMode Mode { get; set; } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [HttpPost("{id:guid}/pin")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<ActionResult<Post>> PinPost(Guid id, [FromBody] PostPinRequest request) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var post = await db.Posts | ||||||
|  |             .Where(e => e.Id == id) | ||||||
|  |             .Include(e => e.Publisher) | ||||||
|  |             .Include(e => e.RepliedPost) | ||||||
|  |             .FirstOrDefaultAsync(); | ||||||
|  |         if (post is null) return NotFound(); | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ps.PinPostAsync(post, currentUser, request.Mode); | ||||||
|  |         } | ||||||
|  |         catch (InvalidOperationException err) | ||||||
|  |         { | ||||||
|  |             return BadRequest(err.Message); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |  | ||||||
|  |         _ = als.CreateActionLogAsync(new CreateActionLogRequest | ||||||
|  |         { | ||||||
|  |             Action = ActionLogType.PostPin, | ||||||
|  |             Meta = | ||||||
|  |             { | ||||||
|  |                 { "post_id", Google.Protobuf.WellKnownTypes.Value.ForString(post.Id.ToString()) }, | ||||||
|  |                 { "mode", Google.Protobuf.WellKnownTypes.Value.ForString(request.Mode.ToString()) } | ||||||
|  |             }, | ||||||
|  |             AccountId = currentUser.Id.ToString(), | ||||||
|  |             UserAgent = Request.Headers.UserAgent, | ||||||
|  |             IpAddress = Request.HttpContext.Connection.RemoteIpAddress?.ToString() | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         return Ok(post); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [HttpDelete("{id:guid}/pin")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<ActionResult<Post>> UnpinPost(Guid id) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var post = await db.Posts | ||||||
|  |             .Where(e => e.Id == id) | ||||||
|  |             .Include(e => e.Publisher) | ||||||
|  |             .Include(e => e.RepliedPost) | ||||||
|  |             .FirstOrDefaultAsync(); | ||||||
|  |         if (post is null) return NotFound(); | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ps.UnpinPostAsync(post, currentUser); | ||||||
|  |         } | ||||||
|  |         catch (InvalidOperationException err) | ||||||
|  |         { | ||||||
|  |             return BadRequest(err.Message); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         _ = als.CreateActionLogAsync(new CreateActionLogRequest | ||||||
|  |         { | ||||||
|  |             Action = ActionLogType.PostUnpin, | ||||||
|  |             Meta = | ||||||
|  |             { | ||||||
|  |                 { "post_id", Google.Protobuf.WellKnownTypes.Value.ForString(post.Id.ToString()) } | ||||||
|  |             }, | ||||||
|  |             AccountId = currentUser.Id.ToString(), | ||||||
|  |             UserAgent = Request.Headers.UserAgent, | ||||||
|  |             IpAddress = Request.HttpContext.Connection.RemoteIpAddress?.ToString() | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         return Ok(post); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     [HttpPatch("{id:guid}")] |     [HttpPatch("{id:guid}")] | ||||||
|     public async Task<ActionResult<Post>> UpdatePost( |     public async Task<ActionResult<Post>> UpdatePost( | ||||||
|         Guid id, |         Guid id, | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ public partial class PostService( | |||||||
|     FileService.FileServiceClient files, |     FileService.FileServiceClient files, | ||||||
|     FileReferenceService.FileReferenceServiceClient fileRefs, |     FileReferenceService.FileReferenceServiceClient fileRefs, | ||||||
|     PollService polls, |     PollService polls, | ||||||
|  |     Publisher.PublisherService ps, | ||||||
|     WebReaderService reader |     WebReaderService reader | ||||||
| ) | ) | ||||||
| { | { | ||||||
| @@ -418,6 +419,56 @@ public partial class PostService( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task<Post> PinPostAsync(Post post, Account currentUser, PostPinMode pinMode) | ||||||
|  |     { | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |         if (post.RepliedPostId != null) | ||||||
|  |         { | ||||||
|  |             if (pinMode != PostPinMode.ReplyPage) throw new InvalidOperationException("Replies can only be pinned in the reply page."); | ||||||
|  |             if (post.RepliedPost == null) throw new ArgumentNullException(nameof(post.RepliedPost)); | ||||||
|  |  | ||||||
|  |             if (!await ps.IsMemberWithRole(post.RepliedPost.PublisherId, accountId, Publisher.PublisherMemberRole.Editor)) | ||||||
|  |                 throw new InvalidOperationException("Only editors of original post can pin replies."); | ||||||
|  |  | ||||||
|  |             post.PinMode = pinMode; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             if (!await ps.IsMemberWithRole(post.PublisherId, accountId, Publisher.PublisherMemberRole.Editor)) | ||||||
|  |                 throw new InvalidOperationException("Only editors can pin replies."); | ||||||
|  |  | ||||||
|  |             post.PinMode = pinMode; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         db.Update(post); | ||||||
|  |         await db.SaveChangesAsync(); | ||||||
|  |  | ||||||
|  |         return post; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task<Post> UnpinPostAsync(Post post, Account currentUser) | ||||||
|  |     { | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |         if (post.RepliedPostId != null) | ||||||
|  |         { | ||||||
|  |             if (post.RepliedPost == null) throw new ArgumentNullException(nameof(post.RepliedPost)); | ||||||
|  |  | ||||||
|  |             if (!await ps.IsMemberWithRole(post.RepliedPost.PublisherId, accountId, Publisher.PublisherMemberRole.Editor)) | ||||||
|  |                 throw new InvalidOperationException("Only editors of original post can unpin replies."); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             if (!await ps.IsMemberWithRole(post.PublisherId, accountId, Publisher.PublisherMemberRole.Editor)) | ||||||
|  |                 throw new InvalidOperationException("Only editors can unpin posts."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         post.PinMode = null; | ||||||
|  |         db.Update(post); | ||||||
|  |         await db.SaveChangesAsync(); | ||||||
|  |  | ||||||
|  |         return post; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Calculate the total number of votes for a post. |     /// Calculate the total number of votes for a post. | ||||||
|     /// This function helps you save the new reactions. |     /// This function helps you save the new reactions. | ||||||
| @@ -770,7 +821,6 @@ public partial class PostService( | |||||||
|             var reactSocialPoints = await db.PostReactions |             var reactSocialPoints = await db.PostReactions | ||||||
|                 .Include(e => e.Post) |                 .Include(e => e.Post) | ||||||
|                 .Where(e => e.Post.Visibility == PostVisibility.Public) |                 .Where(e => e.Post.Visibility == PostVisibility.Public) | ||||||
|                 .Where(e => e.CreatedAt >= periodStart && e.CreatedAt < periodEnd) |  | ||||||
|                 .Where(e => e.Post.CreatedAt >= periodStart && e.Post.CreatedAt < periodEnd) |                 .Where(e => e.Post.CreatedAt >= periodStart && e.Post.CreatedAt < periodEnd) | ||||||
|                 .GroupBy(e => e.PostId) |                 .GroupBy(e => e.PostId) | ||||||
|                 .Select(e => new |                 .Select(e => new | ||||||
| @@ -784,16 +834,27 @@ public partial class PostService( | |||||||
|  |  | ||||||
|             featuredIds = reactSocialPoints.Select(e => e.Key).ToList(); |             featuredIds = reactSocialPoints.Select(e => e.Key).ToList(); | ||||||
|  |  | ||||||
|             await cache.SetAsync(FeaturedPostCacheKey, featuredIds, TimeSpan.FromHours(24)); |             await cache.SetAsync(FeaturedPostCacheKey, featuredIds, TimeSpan.FromHours(4)); | ||||||
|  |  | ||||||
|             // Create featured record |             // Create featured record | ||||||
|             var records = reactSocialPoints.Select(e => new PostFeaturedRecord |             var existingFeaturedPostIds = await db.PostFeaturedRecords | ||||||
|  |                 .Where(r => featuredIds.Contains(r.PostId)) | ||||||
|  |                 .Select(r => r.PostId) | ||||||
|  |                 .ToListAsync(); | ||||||
|  |  | ||||||
|  |             var records = reactSocialPoints | ||||||
|  |                 .Where(p => !existingFeaturedPostIds.Contains(p.Key)) | ||||||
|  |                 .Select(e => new PostFeaturedRecord | ||||||
|  |                 { | ||||||
|  |                     PostId = e.Key, | ||||||
|  |                     SocialCredits = e.Value | ||||||
|  |                 }).ToList(); | ||||||
|  |  | ||||||
|  |             if (records.Any()) | ||||||
|             { |             { | ||||||
|                 PostId = e.Key, |                 db.PostFeaturedRecords.AddRange(records); | ||||||
|                 SocialCredits = e.Value |                 await db.SaveChangesAsync(); | ||||||
|             }).ToList(); |             } | ||||||
|             db.PostFeaturedRecords.AddRange(records); |  | ||||||
|             await db.SaveChangesAsync(); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var posts = await db.Posts |         var posts = await db.Posts | ||||||
|   | |||||||
| @@ -245,14 +245,14 @@ public class RealmController( | |||||||
|                 members.Select(m => m.AccountId).ToList() |                 members.Select(m => m.AccountId).ToList() | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|                 members = members |             members = members | ||||||
|                     .Select(m => |                 .Select(m => | ||||||
|                     { |                 { | ||||||
|                         m.Status = memberStatuses.TryGetValue(m.AccountId, out var s) ? s : null; |                     m.Status = memberStatuses.TryGetValue(m.AccountId, out var s) ? s : null; | ||||||
|                         return m; |                     return m; | ||||||
|                     }) |                 }) | ||||||
|                     .OrderByDescending(m => m.Status?.IsOnline ?? false) |                 .OrderByDescending(m => m.Status?.IsOnline ?? false) | ||||||
|                     .ToList(); |                 .ToList(); | ||||||
|  |  | ||||||
|             var total = members.Count; |             var total = members.Count; | ||||||
|             Response.Headers.Append("X-Total", total.ToString()); |             Response.Headers.Append("X-Total", total.ToString()); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user