Implemented update and delete for posts

This commit is contained in:
Marcel Baumgartner 2023-10-28 19:33:59 +02:00
parent d98e8ef0f8
commit 6d83c31f42
3 changed files with 111 additions and 11 deletions

View file

@ -45,6 +45,7 @@ public class PostService
{
post.Title = title;
post.Content = content;
post.UpdatedAt = DateTime.UtcNow;
PostRepository.Update(post);
@ -53,6 +54,30 @@ public class PostService
public async Task Delete(Post post)
{
var postWithData = PostRepository
.Get()
.Include(x => x.Comments)
.Include(x => x.Likes)
.First(x => x.Id == post.Id);
// Cache relational data to delete later on
var likes = postWithData.Likes.ToArray();
var comments = postWithData.Comments.ToArray();
// Clear relations
postWithData.Comments.Clear();
postWithData.Likes.Clear();
PostRepository.Update(postWithData);
// Delete relational data
foreach (var like in likes)
PostLikeRepository.Delete(like);
foreach (var comment in comments)
PostCommentRepository.Delete(comment);
// Now delete the post itself
PostRepository.Delete(post);
await Events.OnPostDeleted.InvokeAsync(post);
}
@ -67,7 +92,7 @@ public class PostService
if (content.Length > 1024)
throw new DisplayException("Comment content cannot be longer than 1024 characters");
if (!Regex.IsMatch(content, "^[a-zA-Z0-9äöüßÄÖÜẞ,.;_\\n\\t-]+$"))
if (!Regex.IsMatch(content, "^[ a-zA-Z0-9äöüßÄÖÜẞ,.;_\\n\\t-]+$"))
throw new DisplayException("Illegal characters in comment content");
//TODO: Swear word filter

View file

@ -1,6 +1,7 @@
@using Moonlight.App.Database.Entities.Community
@using Ganss.Xss
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.Models.Enums
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Moonlight.App.Services.Community
@ -8,6 +9,7 @@
@inject Repository<Post> PostRepository
@inject IdentityService IdentityService
@inject PostService PostService
@inject ToastService ToastService
<div class="card card-flush">
<div class="card-header pt-9">
@ -16,19 +18,29 @@
<img src="/api/bucket/avatars/@(Post.Author.Avatar)" class="" alt="">
</div>
<div class="flex-grow-1">
<a href="#" class="text-gray-800 text-hover-primary fs-4 fw-bold">@(Post.Author.Username)</a>
<span class="text-gray-800 text-hover-primary fs-4 fw-bold">@(Post.Author.Username)</span>
<span class="text-gray-500 fw-semibold d-block">@(Formatter.FormatAgoFromDateTime(Post.CreatedAt))</span>
</div>
</div>
<div class="card-toolbar">
</div>
@if (Post.Author.Id == IdentityService.CurrentUser.Id || IdentityService.Permissions[Permission.AdminCommunity])
{
<div class="card-toolbar">
<a @onclick="DeletePost" @onclick:preventDefault href="#" class="text-danger fw-semibold d-block">Remove post</a>
</div>
}
</div>
<div class="card-body">
<div class="fs-6 fw-normal text-gray-700">
@{
@if (IsEditing)
{
<TextEditor @bind-Value="EditContent" InitialContent="@Post.Content" />
}
else
{
var sanitizer = new HtmlSanitizer();
var content = sanitizer.Sanitize(Post.Content);
@((MarkupString)content)
}
</div>
@ -49,6 +61,25 @@
@(LikesCount) Like(s)
</a>
</li>
@if (Post.Author.Id == IdentityService.CurrentUser.Id || IdentityService.Permissions[Permission.AdminCommunity])
{
<li class="nav-item">
<div class="nav-link pt-0">
<a @onclick="() => ToggleEdit()" @onclick:preventDefault href="#" class="btn btn-sm btn-color-gray-600 btn-active-color-warning fw-bold px-4 pe-0 @(IsEditing ? "active" : "")">
<i class="bx bx-edit fs-2"></i>
@(IsEditing ? "Save" : "Edit")
</a>
@if (IsEditing)
{
<a @onclick="() => ToggleEdit(true)" @onclick:preventDefault href="#" class="btn btn-sm btn-color-gray-600 btn-active-color-warning fw-bold px-4 ps-2 @(IsEditing ? "active" : "")">
<i class="bx bx-x fs-2"></i>
Cancel
</a>
}
</div>
</li>
}
</ul>
<div class="separator separator-solid mb-1"></div>
@if (ShowComments)
@ -66,6 +97,11 @@
<div class="d-flex align-items-center flex-wrap mb-0">
<a href="#" class="text-gray-800 text-hover-primary fw-bold me-6">@(comment.Author.Username)</a>
<span class="text-gray-500 fw-semibold fs-7 me-5">@(Formatter.FormatAgoFromDateTime(comment.CreatedAt))</span>
@if (comment.Author.Id == IdentityService.CurrentUser.Id || IdentityService.Permissions[Permission.AdminCommunity])
{
<a @onclick="() => DeleteComment(comment)" @onclick:preventDefault href="#" class="text-danger fw-semibold fs-7">Remove comment</a>
}
</div>
<span class="text-gray-800 fs-6 fw-normal pt-1">
@(Formatter.FormatLineBreaks(comment.Content))
@ -90,8 +126,8 @@
</div>
<div class="position-relative w-100">
<div class="input-group">
<textarea @bind="Comment" type="text" class="form-control form-control-solid border ps-5" placeholder="Write your comment.."></textarea>
<WButton OnClick="CreateComment" Text="Comment" CssClasses="btn btn-primary" />
<textarea @bind="Comment" type="text" class="form-control form-control-solid border ps-5" placeholder="Write your comment.." style="height: 1vh"></textarea>
<WButton OnClick="CreateComment" Text="Comment" CssClasses="btn btn-primary"/>
</div>
</div>
</div>
@ -103,6 +139,9 @@
[Parameter]
public Post Post { get; set; }
[Parameter]
public Func<Task>? OnUpdate { get; set; }
private int CommentsCount = -1;
private int LikesCount = -1;
private bool HasLiked = false;
@ -111,6 +150,10 @@
private PostComment[] Comments = Array.Empty<PostComment>();
private string Comment = "";
private bool IsEditing = false;
private string EditTitle = "";
private string EditContent = "";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
@ -118,7 +161,7 @@
await UpdateCounts();
}
}
private Task LoadComments(LazyLoader _)
{
Comments = PostRepository
@ -161,6 +204,7 @@
Comment = "";
ShowComments = true;
await LoadComments(null!);
await InvokeAsync(StateHasChanged);
await UpdateCounts();
}
@ -168,9 +212,12 @@
private async Task DeleteComment(PostComment comment)
{
await PostService.DeleteComment(Post, comment);
await LoadComments(null!);
await InvokeAsync(StateHasChanged);
await UpdateCounts();
await ToastService.Success("Successfully deleted comment");
}
private async Task ToggleComments()
@ -187,4 +234,31 @@
await PostService.ToggleLike(Post, IdentityService.CurrentUser);
await UpdateCounts();
}
private async Task ToggleEdit(bool preventSaving = false)
{
IsEditing = !IsEditing;
if (IsEditing)
{
EditTitle = Post.Title;
EditContent = Post.Content;
}
else if (!preventSaving)
{
await PostService.Update(Post, EditTitle, EditContent);
await ToastService.Success("Successfully saved post");
}
await InvokeAsync(StateHasChanged);
}
private async Task DeletePost()
{
await PostService.Delete(Post);
await ToastService.Success("Successfully deleted post");
if (OnUpdate != null)
await OnUpdate.Invoke();
}
}

View file

@ -8,10 +8,10 @@
@inject Repository<Post> PostRepository
<div class="row">
<LazyLoader Load="Load">
<LazyLoader @ref="LazyLoader" Load="Load">
@foreach (var post in Posts)
{
<PostView Post="post" />
<PostView Post="post" OnUpdate="() => LazyLoader.Reload()" />
<div class="mb-10"></div>
}
</LazyLoader>
@ -19,6 +19,7 @@
@code
{
private LazyLoader LazyLoader;
private Post[] Posts;
private Task Load(LazyLoader _)