173 lines
5.8 KiB
C#
173 lines
5.8 KiB
C#
using System.Text.Json;
|
|
using Dapper;
|
|
using Femto.Common.Infrastructure.DbConnection;
|
|
using Femto.Modules.Blog.Application.Queries.GetPosts.Dto;
|
|
using MediatR;
|
|
|
|
namespace Femto.Modules.Blog.Application.Queries.GetPosts;
|
|
|
|
public class GetPostsQueryHandler(IDbConnectionFactory connectionFactory)
|
|
: IRequestHandler<GetPostsQuery, GetPostsQueryResult>
|
|
{
|
|
public async Task<GetPostsQueryResult> Handle(
|
|
GetPostsQuery query,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
using var conn = connectionFactory.GetConnection();
|
|
|
|
var username = query.Author;
|
|
var authorGuid = query.AuthorId;
|
|
var cursor = query.After;
|
|
var showPrivate = query.CurrentUserId is not null;
|
|
|
|
var loadPostsResult = await conn.QueryAsync<LoadPostRow>(
|
|
"""
|
|
select
|
|
blog.post.id as PostId,
|
|
blog.post.content as Content,
|
|
blog.post.posted_on as PostedOn,
|
|
blog.author.username as Username,
|
|
blog.author.id as AuthorId,
|
|
blog.post.possible_reactions as PossibleReactions
|
|
from blog.post
|
|
inner join blog.author on blog.author.id = blog.post.author_id
|
|
where (@username is null or blog.author.username = @username)
|
|
and (@postId is null or blog.post.id = @postId)
|
|
and (@showPrivate or blog.post.is_public = true)
|
|
and (@authorGuid is null or blog.author.id = @authorGuid)
|
|
and (@cursor is null or blog.post.id < @cursor)
|
|
order by blog.post.id desc
|
|
limit @amount
|
|
""",
|
|
new
|
|
{
|
|
username,
|
|
authorGuid,
|
|
cursor,
|
|
amount = query.PostId is not null ? 1 : query.Amount,
|
|
showPrivate,
|
|
postId = query.PostId,
|
|
}
|
|
);
|
|
|
|
var posts = loadPostsResult.ToList();
|
|
|
|
var postIds = posts.Select(p => p.PostId).ToList();
|
|
|
|
var loadMediaResult = await conn.QueryAsync<LoadMediaRow>(
|
|
"""
|
|
select
|
|
pm.url as MediaUrl,
|
|
pm.type as MediaType,
|
|
pm.width as MediaWidth,
|
|
pm.height as MediaHeight,
|
|
pm.post_id as PostId
|
|
from blog.post_media pm where pm.post_id = ANY (@postIds)
|
|
order by pm.ordering
|
|
""",
|
|
new { postIds }
|
|
);
|
|
|
|
var media = loadMediaResult.ToList();
|
|
|
|
var loadReactionsResult = await conn.QueryAsync<LoadReactionRow>(
|
|
"""
|
|
select
|
|
pr.post_id as PostId,
|
|
a.username as AuthorName,
|
|
pr.emoji as Emoji,
|
|
pr.created_at as CreatedOn
|
|
from blog.post_reaction pr
|
|
join blog.author a on a.id = pr.author_id
|
|
where pr.post_id = ANY (@postIds)
|
|
""",
|
|
new { postIds }
|
|
);
|
|
|
|
var reactions = loadReactionsResult.ToList();
|
|
|
|
var loadCommentsResult = await conn.QueryAsync<LoadCommentRow>(
|
|
"""
|
|
select
|
|
pc.id as CommentId,
|
|
pc.post_id as PostId,
|
|
pc.content as Content,
|
|
pc.created_at as PostedOn,
|
|
a.username as AuthorName
|
|
from blog.post_comment pc
|
|
join blog.author a on pc.author_id = a.id
|
|
where pc.post_id = ANY (@postIds)
|
|
""",
|
|
new { postIds }
|
|
);
|
|
|
|
var comments = loadCommentsResult.ToList();
|
|
|
|
return new GetPostsQueryResult(
|
|
posts
|
|
.Select(p => new PostDto(
|
|
p.PostId,
|
|
p.Content,
|
|
media
|
|
.Where(m => m.PostId == p.PostId)
|
|
.Select(m => new PostMediaDto(
|
|
new Uri(m.MediaUrl),
|
|
m.MediaWidth,
|
|
m.MediaHeight
|
|
))
|
|
.ToList(),
|
|
p.PostedOn,
|
|
new PostAuthorDto(p.AuthorId, p.Username),
|
|
reactions
|
|
.Where(r => r.PostId == p.PostId)
|
|
.Select(r => new PostReactionDto(r.Emoji, r.AuthorName, r.CreatedAt))
|
|
.ToList(),
|
|
!string.IsNullOrEmpty(p.PossibleReactions)
|
|
? JsonSerializer.Deserialize<IEnumerable<string>>(p.PossibleReactions)!
|
|
: [],
|
|
comments
|
|
.Where(c => c.PostId == p.PostId)
|
|
.Select(c => new PostCommentDto(c.AuthorName, c.Content, c.PostedOn))
|
|
.ToList()
|
|
))
|
|
.ToList()
|
|
);
|
|
}
|
|
|
|
internal record LoadPostRow
|
|
{
|
|
public Guid PostId { get; init; }
|
|
public string Content { get; init; }
|
|
public DateTimeOffset PostedOn { get; init; }
|
|
public string Username { get; init; }
|
|
public Guid AuthorId { get; init; }
|
|
public string? PossibleReactions { get; init; }
|
|
}
|
|
|
|
internal record LoadMediaRow
|
|
{
|
|
public string MediaUrl { get; init; }
|
|
public string? MediaType { get; init; }
|
|
public int? MediaWidth { get; init; }
|
|
public int? MediaHeight { get; init; }
|
|
public Guid PostId { get; init; }
|
|
}
|
|
|
|
internal record LoadReactionRow
|
|
{
|
|
public Guid PostId { get; init; }
|
|
public string AuthorName { get; init; }
|
|
public string Emoji { get; init; }
|
|
public DateTimeOffset CreatedAt { get; init; }
|
|
}
|
|
|
|
internal record LoadCommentRow
|
|
{
|
|
public Guid CommentId { get; init; }
|
|
public Guid PostId { get; init; }
|
|
public string Content { get; init; }
|
|
public DateTimeOffset PostedOn { get; init; }
|
|
public string AuthorName { get; init; }
|
|
}
|
|
}
|