✨ Featured post
This commit is contained in:
@@ -4,7 +4,6 @@ using DysonNetwork.Shared.Content;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using DysonNetwork.Sphere.Poll;
|
using DysonNetwork.Sphere.Poll;
|
||||||
using DysonNetwork.Sphere.Publisher;
|
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -26,6 +25,16 @@ public class PostController(
|
|||||||
)
|
)
|
||||||
: ControllerBase
|
: ControllerBase
|
||||||
{
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<List<Post>>> ListFeaturedPosts()
|
||||||
|
{
|
||||||
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
|
var currentUser = currentUserValue as Account;
|
||||||
|
|
||||||
|
var posts = await ps.ListFeaturedPostsAsync(currentUser);
|
||||||
|
return Ok(posts);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<List<Post>>> ListPosts(
|
public async Task<ActionResult<List<Post>>> ListPosts(
|
||||||
[FromQuery] int offset = 0,
|
[FromQuery] int offset = 0,
|
||||||
|
@@ -730,6 +730,57 @@ public partial class PostService(
|
|||||||
var posts = await LoadPostInfo([post], currentUser, truncate);
|
var posts = await LoadPostInfo([post], currentUser, truncate);
|
||||||
return posts.First();
|
return posts.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string FeaturedPostCacheKey = "posts:featured";
|
||||||
|
|
||||||
|
public async Task<List<Post>> ListFeaturedPostsAsync(Account? currentUser = null)
|
||||||
|
{
|
||||||
|
// Check cache first for featured post IDs
|
||||||
|
var featuredIds = await cache.GetAsync<List<Guid>>(FeaturedPostCacheKey);
|
||||||
|
|
||||||
|
if (featuredIds is null)
|
||||||
|
{
|
||||||
|
var today = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
var todayStart = today.InUtc().Date.AtStartOfDayInZone(DateTimeZone.Utc).ToInstant();
|
||||||
|
var todayEnd = today.InUtc().Date.PlusDays(1).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant();
|
||||||
|
|
||||||
|
var reactSocialPoints = await db.PostReactions
|
||||||
|
.Include(e => e.Post)
|
||||||
|
.Where(e => e.Post.Visibility == PostVisibility.Public)
|
||||||
|
.Where(e => e.CreatedAt >= todayStart && e.CreatedAt < todayEnd)
|
||||||
|
.GroupBy(e => e.PostId)
|
||||||
|
.Select(e => new
|
||||||
|
{
|
||||||
|
PostId = e.Key,
|
||||||
|
Count = e.Sum(r => r.Attitude == PostReactionAttitude.Positive ? 1 : -1)
|
||||||
|
})
|
||||||
|
.OrderByDescending(e => e.Count)
|
||||||
|
.Take(3)
|
||||||
|
.ToDictionaryAsync(e => e.PostId, e => e.Count);
|
||||||
|
|
||||||
|
var featuredPostsId = reactSocialPoints.Select(e => e.Key).ToList();
|
||||||
|
|
||||||
|
await cache.SetAsync(FeaturedPostCacheKey, featuredPostsId, TimeSpan.FromMinutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (featuredIds is null)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Failed to load featured post IDs from cache and the database is empty...");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var posts = await db.Posts
|
||||||
|
.Where(e => featuredIds.Contains(e.Id))
|
||||||
|
.Include(e => e.ForwardedPost)
|
||||||
|
.Include(e => e.Categories)
|
||||||
|
.Include(e => e.Publisher)
|
||||||
|
.Take(featuredIds.Count)
|
||||||
|
.ToListAsync();
|
||||||
|
posts = posts.OrderBy(e => featuredIds.IndexOf(e.Id)).ToList();
|
||||||
|
posts = await LoadPostInfo(posts, currentUser, true);
|
||||||
|
|
||||||
|
return posts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PostQueryExtensions
|
public static class PostQueryExtensions
|
||||||
|
Reference in New Issue
Block a user