Implemented update and delete for posts
This commit is contained in:
parent
d98e8ef0f8
commit
6d83c31f42
3 changed files with 111 additions and 11 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 _)
|
||||
|
|
Loading…
Reference in a new issue