post reactions
This commit is contained in:
parent
d1687f276b
commit
8e8e4e2258
10 changed files with 124 additions and 8 deletions
|
@ -0,0 +1,3 @@
|
||||||
|
namespace Femto.Api.Controllers.Posts.Dto;
|
||||||
|
|
||||||
|
public record AddPostReactionRequest(string Emoji);
|
|
@ -0,0 +1,3 @@
|
||||||
|
namespace Femto.Api.Controllers.Posts.Dto;
|
||||||
|
|
||||||
|
public record DeletePostReactionRequest(string Emoji);
|
|
@ -1,6 +1,8 @@
|
||||||
using Femto.Api.Controllers.Posts.Dto;
|
using Femto.Api.Controllers.Posts.Dto;
|
||||||
using Femto.Common;
|
using Femto.Common;
|
||||||
using Femto.Modules.Blog.Application;
|
using Femto.Modules.Blog.Application;
|
||||||
|
using Femto.Modules.Blog.Application.Commands.AddPostReaction;
|
||||||
|
using Femto.Modules.Blog.Application.Commands.ClearPostReaction;
|
||||||
using Femto.Modules.Blog.Application.Commands.CreatePost;
|
using Femto.Modules.Blog.Application.Commands.CreatePost;
|
||||||
using Femto.Modules.Blog.Application.Commands.DeletePost;
|
using Femto.Modules.Blog.Application.Commands.DeletePost;
|
||||||
using Femto.Modules.Blog.Application.Queries.GetPosts;
|
using Femto.Modules.Blog.Application.Queries.GetPosts;
|
||||||
|
@ -95,4 +97,27 @@ public class PostsController(IBlogModule blogModule, ICurrentUserContext current
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{postId}/reactions")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult> AddPostReaction(Guid postId, [FromBody] AddPostReactionRequest request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var currentUser = currentUserContext.CurrentUser!;
|
||||||
|
|
||||||
|
await blogModule.Command(new AddPostReactionCommand(postId, request.Emoji, currentUser.Id), cancellationToken);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{postId}/reactions")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult> DeletePostReaction(Guid postId, [FromBody] DeletePostReactionRequest request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var currentUser = currentUserContext.CurrentUser!;
|
||||||
|
|
||||||
|
await blogModule.Command(new ClearPostReactionCommand(postId, request.Emoji, currentUser.Id), cancellationToken);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
using Femto.Common;
|
||||||
|
using Femto.Common.Domain;
|
||||||
|
|
||||||
|
namespace Femto.Modules.Blog.Application.Commands.AddPostReaction;
|
||||||
|
|
||||||
|
public record AddPostReactionCommand(Guid PostId, string Emoji, Guid ReactorId) : ICommand;
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Femto.Common.Domain;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Femto.Modules.Blog.Application.Commands.AddPostReaction;
|
||||||
|
|
||||||
|
internal class AddPostReactionCommandHandler(BlogContext context)
|
||||||
|
: ICommandHandler<AddPostReactionCommand>
|
||||||
|
{
|
||||||
|
public async Task Handle(AddPostReactionCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var post = await context.Posts.SingleOrDefaultAsync(
|
||||||
|
p => p.Id == request.PostId,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
if (post is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
post.AddReaction(request.ReactorId, request.Emoji);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
using Femto.Common;
|
||||||
|
using Femto.Common.Domain;
|
||||||
|
|
||||||
|
namespace Femto.Modules.Blog.Application.Commands.ClearPostReaction;
|
||||||
|
|
||||||
|
public record ClearPostReactionCommand(Guid PostId, string Emoji, Guid ReactorId): ICommand;
|
|
@ -0,0 +1,22 @@
|
||||||
|
using Femto.Common.Domain;
|
||||||
|
using Femto.Modules.Blog.Application.Commands.AddPostReaction;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Femto.Modules.Blog.Application.Commands.ClearPostReaction;
|
||||||
|
|
||||||
|
internal class ClearPostReactionCommandHandler(BlogContext context)
|
||||||
|
: ICommandHandler<ClearPostReactionCommand>
|
||||||
|
{
|
||||||
|
public async Task Handle(ClearPostReactionCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var post = await context.Posts.SingleOrDefaultAsync(
|
||||||
|
p => p.Id == request.PostId,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
if (post is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
post.RemoveReaction(request.ReactorId, request.Emoji);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,9 +25,10 @@ internal class CreatePostCommandHandler(BlogContext context)
|
||||||
media.Height
|
media.Height
|
||||||
))
|
))
|
||||||
.ToList()
|
.ToList()
|
||||||
);
|
)
|
||||||
|
{
|
||||||
post.IsPublic = request.IsPublic is true;
|
IsPublic = request.IsPublic is true
|
||||||
|
};
|
||||||
|
|
||||||
await context.AddAsync(post, cancellationToken);
|
await context.AddAsync(post, cancellationToken);
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,21 @@ internal class PostConfiguration : IEntityTypeConfiguration<Post>
|
||||||
{
|
{
|
||||||
table.ToTable("post");
|
table.ToTable("post");
|
||||||
table.OwnsMany(post => post.Media).WithOwner();
|
table.OwnsMany(post => post.Media).WithOwner();
|
||||||
table.OwnsMany(post => post.Reactions).WithOwner();
|
table.OwnsMany(
|
||||||
|
post => post.Reactions,
|
||||||
table.Property<string>("PossibleReactionsJson")
|
reactions =>
|
||||||
.HasColumnName("possible_reactions");
|
{
|
||||||
|
reactions.WithOwner().HasForeignKey(r => r.PostId);
|
||||||
|
reactions.HasKey(r => new
|
||||||
|
{
|
||||||
|
r.PostId,
|
||||||
|
r.AuthorId,
|
||||||
|
r.Emoji,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
table.Property<string>("PossibleReactionsJson").HasColumnName("possible_reactions");
|
||||||
|
|
||||||
table.Ignore(e => e.PossibleReactions);
|
table.Ignore(e => e.PossibleReactions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ internal class Post : Entity
|
||||||
|
|
||||||
public IList<PostReaction> Reactions { get; private set; } = [];
|
public IList<PostReaction> Reactions { get; private set; } = [];
|
||||||
public bool IsPublic { get; set; }
|
public bool IsPublic { get; set; }
|
||||||
|
|
||||||
public DateTimeOffset PostedOn { get; private set; }
|
public DateTimeOffset PostedOn { get; private set; }
|
||||||
|
|
||||||
private string PossibleReactionsJson { get; set; } = null!;
|
private string PossibleReactionsJson { get; set; } = null!;
|
||||||
|
@ -38,4 +38,22 @@ internal class Post : Entity
|
||||||
|
|
||||||
this.AddDomainEvent(new PostCreated(this));
|
this.AddDomainEvent(new PostCreated(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddReaction(Guid reactorId, string emoji)
|
||||||
|
{
|
||||||
|
if (!this.PossibleReactions.Contains(emoji))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.Reactions.Any(r => r.AuthorId == reactorId && r.Emoji == emoji))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.Reactions.Add(new PostReaction(reactorId, this.Id, emoji));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveReaction(Guid reactorId, string emoji)
|
||||||
|
{
|
||||||
|
this.Reactions = this
|
||||||
|
.Reactions.Where(r => r.AuthorId != reactorId || r.Emoji != emoji)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue