Add delete comment functionality with confirmation dialog.

This commit is contained in:
2025-06-23 19:55:43 +08:00
parent 46186e95df
commit dcd23054b2
6 changed files with 145 additions and 1 deletions

View File

@@ -131,6 +131,12 @@ export const i18nData = {
"Are you sure you want to delete the file": "Are you sure you want to delete the file":
"Are you sure you want to delete the file", "Are you sure you want to delete the file",
// 评论删除相关
"Delete Comment": "Delete Comment",
"Are you sure you want to delete this comment? This action cannot be undone.":
"Are you sure you want to delete this comment? This action cannot be undone.",
"Comment deleted successfully": "Comment deleted successfully",
// New translations // New translations
"Change Avatar": "Change Avatar", "Change Avatar": "Change Avatar",
"Change Username": "Change Username", "Change Username": "Change Username",
@@ -344,6 +350,11 @@ export const i18nData = {
"Delete File": "删除文件", "Delete File": "删除文件",
"Are you sure you want to delete the file": "您确定要删除此文件吗", "Are you sure you want to delete the file": "您确定要删除此文件吗",
"Delete Comment": "删除评论",
"Are you sure you want to delete this comment? This action cannot be undone.":
"您确定要删除此评论吗?此操作不可撤销。",
"Comment deleted successfully": "评论删除成功",
// New translations // New translations
"Change Avatar": "更改头像", "Change Avatar": "更改头像",
"Change Username": "更改用户名", "Change Username": "更改用户名",
@@ -556,6 +567,11 @@ export const i18nData = {
"Delete File": "刪除檔案", "Delete File": "刪除檔案",
"Are you sure you want to delete the file": "您確定要刪除此檔案嗎", "Are you sure you want to delete the file": "您確定要刪除此檔案嗎",
"Delete Comment": "刪除評論",
"Are you sure you want to delete this comment? This action cannot be undone.":
"您確定要刪除此評論嗎?此操作不可撤銷。",
"Comment deleted successfully": "評論刪除成功",
// New translations // New translations
"Change Avatar": "更改頭像", "Change Avatar": "更改頭像",
"Change Username": "更改用戶名", "Change Username": "更改用戶名",

View File

@@ -986,6 +986,18 @@ class Network {
} }
} }
async deleteComment(commentID: number): Promise<Response<void>> {
try {
const response = await axios.delete(
`${this.apiBaseUrl}/comments/${commentID}`,
);
return response.data;
} catch (e: any) {
console.error(e);
return { success: false, message: e.toString() };
}
}
async getServerConfig(): Promise<Response<ServerConfig>> { async getServerConfig(): Promise<Response<ServerConfig>> {
try { try {
const response = await axios.get(`${this.apiBaseUrl}/config`); const response = await axios.get(`${this.apiBaseUrl}/config`);

View File

@@ -1311,6 +1311,7 @@ function CommentTile({ comment }: { comment: Comment }) {
</div> </div>
{app.user?.id === comment.user.id && ( {app.user?.id === comment.user.id && (
<div className={"flex flex-row-reverse"}> <div className={"flex flex-row-reverse"}>
<DeleteCommentDialog commentId={comment.id} />
<EditCommentDialog comment={comment} /> <EditCommentDialog comment={comment} />
</div> </div>
)} )}
@@ -1456,4 +1457,62 @@ function EditCommentDialog({
</dialog> </dialog>
</> </>
); );
} }
// 新增:删除评论弹窗组件
function DeleteCommentDialog({ commentId }: { commentId: number }) {
const [isLoading, setLoading] = useState(false);
const reload = useContext(context);
const { t } = useTranslation();
const id = `delete_comment_dialog_${commentId}`;
const handleDelete = async () => {
if (isLoading) return;
setLoading(true);
const res = await network.deleteComment(commentId);
const dialog = document.getElementById(id) as HTMLDialogElement;
dialog.close();
if (res.success) {
showToast({ message: t("Comment deleted successfully"), type: "success" });
reload();
} else {
showToast({ message: res.message, type: "error" });
}
setLoading(false);
};
return (
<>
<button
className={"btn btn-error btn-sm btn-ghost ml-1"}
onClick={() => {
const dialog = document.getElementById(id) as HTMLDialogElement;
dialog.showModal();
}}
>
<MdOutlineDelete size={16} className={"inline-block"} />
{t("Delete")}
</button>
<dialog id={id} className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">{t("Delete Comment")}</h3>
<p className="py-4">
{t("Are you sure you want to delete this comment? This action cannot be undone.")}
</p>
<div className="modal-action">
<form method="dialog">
<button className="btn btn-ghost">{t("Close")}</button>
</form>
<button className="btn btn-error" onClick={handleDelete}>
{isLoading ? (
<span className={"loading loading-spinner loading-sm"}></span>
) : null}
{t("Delete")}
</button>
</div>
</div>
</dialog>
</>
);
}

View File

@@ -15,6 +15,7 @@ func AddCommentRoutes(router fiber.Router) {
api.Get("/:resourceID", listComments) api.Get("/:resourceID", listComments)
api.Get("/user/:username", listCommentsWithUser) api.Get("/user/:username", listCommentsWithUser)
api.Put("/:commentID", updateComment) api.Put("/:commentID", updateComment)
api.Delete("/:commentID", deleteComment)
} }
func createComment(c fiber.Ctx) error { func createComment(c fiber.Ctx) error {
@@ -115,3 +116,23 @@ func updateComment(c fiber.Ctx) error {
Message: "Comment updated successfully", Message: "Comment updated successfully",
}) })
} }
func deleteComment(c fiber.Ctx) error {
userID, ok := c.Locals("uid").(uint)
if !ok {
return model.NewRequestError("You must be logged in to delete comment")
}
commentIDStr := c.Params("commentID")
commentID, err := strconv.Atoi(commentIDStr)
if err != nil {
return model.NewRequestError("Invalid comment ID")
}
err = service.DeleteComment(uint(commentID), userID)
if err != nil {
return err
}
return c.JSON(model.Response[any]{
Success: true,
Message: "Comment deleted successfully",
})
}

View File

@@ -83,3 +83,25 @@ func UpdateCommentContent(commentID uint, content string) (*model.Comment, error
db.Preload("User").First(&comment, commentID) db.Preload("User").First(&comment, commentID)
return &comment, nil return &comment, nil
} }
func DeleteCommentByID(commentID uint) error {
return db.Transaction(func(tx *gorm.DB) error {
var comment model.Comment
if err := tx.First(&comment, commentID).Error; err != nil {
return err
}
if err := tx.Delete(&comment).Error; err != nil {
return err
}
if err := tx.Model(&model.User{}).Where("id = ?", comment.UserID).Update("comments_count", gorm.Expr("comments_count - 1")).Error; err != nil {
return err
}
if err := tx.
Where("type = ? and ref_id = ?", model.ActivityTypeNewComment, commentID).
Delete(&model.Activity{}).
Error; err != nil {
return err
}
return nil
})
}

View File

@@ -84,3 +84,17 @@ func UpdateComment(commentID, userID uint, content string) (*model.CommentView,
} }
return updated.ToView(), nil return updated.ToView(), nil
} }
func DeleteComment(commentID, userID uint) error {
comment, err := dao.GetCommentByID(commentID)
if err != nil {
return model.NewNotFoundError("Comment not found")
}
if comment.UserID != userID {
return model.NewRequestError("You can only delete your own comments")
}
if err := dao.DeleteCommentByID(commentID); err != nil {
return model.NewInternalServerError("Error deleting comment")
}
return nil
}