mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-26 11:06:23 +00:00
feat: 添加评论功能的客户端信息显示选项,更新相关接口和组件
This commit is contained in:
@ -27,6 +27,7 @@ type CreateCommentReq struct {
|
||||
IsPrivate bool `json:"is_private"` // 是否私密评论,默认false
|
||||
RemoteAddr string `json:"remote_addr"` // 远程地址
|
||||
UserAgent string `json:"user_agent"` // 用户代理
|
||||
ShowClientInfo bool `json:"show_client_info"` // 是否显示客户端信息
|
||||
}
|
||||
|
||||
type UpdateCommentReq struct {
|
||||
|
@ -20,6 +20,7 @@ type Comment struct {
|
||||
Location string `gorm:"type:text"` // 用户位置,基于IP
|
||||
LikeCount uint64
|
||||
CommentCount uint64
|
||||
ShowClientInfo bool `gorm:"default:false"` // 是否显示客户端信息
|
||||
}
|
||||
|
||||
func (c *Comment) AfterCreate(tx *gorm.DB) (err error) {
|
||||
|
@ -121,6 +121,7 @@ func (cr *CommentRepo) CreateComment(comment *model.Comment) (uint, error) {
|
||||
})
|
||||
return commentID, err
|
||||
}
|
||||
|
||||
func (cr *CommentRepo) UpdateComment(comment *model.Comment) error {
|
||||
if comment.ID == 0 {
|
||||
return errs.New(http.StatusBadRequest, "invalid comment ID", nil)
|
||||
|
@ -42,6 +42,7 @@ func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateComm
|
||||
IsPrivate: req.IsPrivate,
|
||||
RemoteAddr: req.RemoteAddr,
|
||||
UserAgent: req.UserAgent,
|
||||
ShowClientInfo: req.ShowClientInfo,
|
||||
}
|
||||
|
||||
commentID, err := repo.Comment.CreateComment(comment)
|
||||
@ -112,18 +113,44 @@ func (cs *CommentService) GetComment(ctx context.Context, commentID string) (*dt
|
||||
return nil, errs.New(errs.ErrNotFound.Code, "comment not found", err)
|
||||
}
|
||||
|
||||
currentUserID := uint(0)
|
||||
if currentUser, ok := ctxutils.GetCurrentUser(ctx); ok {
|
||||
currentUserID = currentUser.ID
|
||||
}
|
||||
if comment.IsPrivate && currentUserID != comment.UserID {
|
||||
return nil, errs.ErrForbidden
|
||||
}
|
||||
isLiked := false
|
||||
if currentUserID != 0 {
|
||||
isLiked, _ = repo.Like.IsLiked(currentUserID, comment.ID, constant.TargetTypeComment)
|
||||
}
|
||||
ua := utils.ParseUA(comment.UserAgent)
|
||||
if !comment.ShowClientInfo {
|
||||
comment.Location = ""
|
||||
ua.OS = ""
|
||||
ua.OSVersion = ""
|
||||
ua.Browser = ""
|
||||
ua.BrowserVer = ""
|
||||
}
|
||||
|
||||
commentDto := dto.CommentDto{
|
||||
ID: comment.ID,
|
||||
Content: comment.Content,
|
||||
TargetID: comment.TargetID,
|
||||
TargetType: comment.TargetType,
|
||||
Content: comment.Content,
|
||||
ReplyID: comment.ReplyID,
|
||||
Depth: comment.Depth,
|
||||
CreatedAt: comment.CreatedAt.String(),
|
||||
UpdatedAt: comment.UpdatedAt.String(),
|
||||
Depth: comment.Depth,
|
||||
User: comment.User.ToDto(),
|
||||
ReplyCount: comment.CommentCount,
|
||||
LikeCount: comment.LikeCount,
|
||||
IsLiked: isLiked,
|
||||
IsPrivate: comment.IsPrivate,
|
||||
OS: ua.OS + " " + ua.OSVersion,
|
||||
Browser: ua.Browser + " " + ua.BrowserVer,
|
||||
Location: comment.Location,
|
||||
}
|
||||
// TODO: 返回更多字段
|
||||
|
||||
return &commentDto, err
|
||||
}
|
||||
@ -133,14 +160,11 @@ func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommen
|
||||
if currentUser, ok := ctxutils.GetCurrentUser(ctx); ok {
|
||||
currentUserID = currentUser.ID
|
||||
}
|
||||
|
||||
comments, err := repo.Comment.ListComments(currentUserID, req.TargetID, req.CommentID, req.TargetType, req.Page, req.Size, req.OrderBy, req.Desc, req.Depth)
|
||||
if err != nil {
|
||||
return nil, errs.New(errs.ErrInternalServer.Code, "failed to list comments", err)
|
||||
}
|
||||
|
||||
commentDtos := make([]dto.CommentDto, 0)
|
||||
|
||||
for _, comment := range comments {
|
||||
//replyCount, _ := repo.Comment.CountReplyComments(currentUserID, comment.ID)
|
||||
isLiked := false
|
||||
@ -166,6 +190,11 @@ func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommen
|
||||
Browser: ua.Browser + " " + ua.BrowserVer,
|
||||
Location: comment.Location,
|
||||
}
|
||||
if !comment.ShowClientInfo {
|
||||
commentDto.Location = ""
|
||||
commentDto.OS = ""
|
||||
commentDto.Browser = ""
|
||||
}
|
||||
commentDtos = append(commentDtos, commentDto)
|
||||
}
|
||||
return commentDtos, nil
|
||||
|
@ -12,13 +12,15 @@ export async function createComment(
|
||||
targetId,
|
||||
content,
|
||||
replyId = null,
|
||||
isPrivate = false
|
||||
isPrivate = false,
|
||||
showClientInfo = true,
|
||||
}: {
|
||||
targetType: TargetType
|
||||
targetId: number
|
||||
content: string
|
||||
replyId: number | null
|
||||
isPrivate: boolean
|
||||
showClientInfo: boolean
|
||||
}
|
||||
): Promise<BaseResponse<{ id: number }>> {
|
||||
const res = await axiosClient.post<BaseResponse<{ id: number }>>('/comment/c', {
|
||||
@ -26,7 +28,8 @@ export async function createComment(
|
||||
targetId,
|
||||
content,
|
||||
replyId,
|
||||
isPrivate
|
||||
isPrivate,
|
||||
showClientInfo,
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { User } from "@/models/user";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import GravatarAvatar, { getGravatarByUser } from "@/components/common/gravatar";
|
||||
import GravatarAvatar from "@/components/common/gravatar";
|
||||
import { CircleUser } from "lucide-react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
@ -20,7 +20,7 @@ export function CommentInput(
|
||||
isUpdate = false
|
||||
}: {
|
||||
user: User | null,
|
||||
onCommentSubmitted: ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => void,
|
||||
onCommentSubmitted: ({ commentContent, isPrivate, showClientInfo }: { commentContent: string, isPrivate: boolean, showClientInfo: boolean }) => void,
|
||||
initContent?: string,
|
||||
initIsPrivate?: boolean,
|
||||
placeholder?: string,
|
||||
@ -33,6 +33,7 @@ export function CommentInput(
|
||||
const clickToUserProfile = useToUserProfile();
|
||||
|
||||
const [isPrivate, setIsPrivate] = useState(initIsPrivate);
|
||||
const [showClientInfo, setShowClientInfo] = useState(true);
|
||||
const [commentContent, setCommentContent] = useState(initContent);
|
||||
|
||||
const handleCommentSubmit = async () => {
|
||||
@ -54,7 +55,7 @@ export function CommentInput(
|
||||
toast.warning(t("comment_unchanged"));
|
||||
return;
|
||||
}
|
||||
onCommentSubmitted({ commentContent, isPrivate });
|
||||
onCommentSubmitted({ commentContent, isPrivate, showClientInfo });
|
||||
setCommentContent("");
|
||||
};
|
||||
|
||||
@ -75,6 +76,13 @@ export function CommentInput(
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end fade-in-up space-x-4 items-center">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={showClientInfo}
|
||||
onCheckedChange={checked => setShowClientInfo(checked === true)}
|
||||
/>
|
||||
<Label onClick={() => setShowClientInfo(prev => !prev)}>{t("show_client_info")}</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={isPrivate}
|
||||
|
@ -123,13 +123,14 @@ export function CommentItem(
|
||||
});
|
||||
}
|
||||
|
||||
const onReply = ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => {
|
||||
const onReply = ({ commentContent, isPrivate, showClientInfo }: { commentContent: string, isPrivate: boolean, showClientInfo: boolean }) => {
|
||||
createComment({
|
||||
targetType: comment.targetType,
|
||||
targetId: comment.targetId,
|
||||
content: commentContent,
|
||||
replyId: comment.id,
|
||||
isPrivate,
|
||||
showClientInfo
|
||||
}).then(() => {
|
||||
toast.success(t("comment_success"));
|
||||
reloadReplies();
|
||||
|
@ -61,18 +61,18 @@ export function CommentSection(
|
||||
});
|
||||
}, [])
|
||||
|
||||
const onCommentSubmitted = ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => {
|
||||
const onCommentSubmitted = ({ commentContent, isPrivate, showClientInfo }: { commentContent: string, isPrivate: boolean, showClientInfo: boolean }) => {
|
||||
createComment({
|
||||
targetType,
|
||||
targetId,
|
||||
content: commentContent,
|
||||
replyId: null,
|
||||
isPrivate,
|
||||
showClientInfo
|
||||
}).then(res => {
|
||||
toast.success(t("comment_success"));
|
||||
setTotalCommentCount(c => c + 1);
|
||||
getComment({ id: res.data.id }).then(response => {
|
||||
console.log("New comment fetched:", response.data);
|
||||
setComments(prevComments => [response.data, ...prevComments]);
|
||||
});
|
||||
setActiveInput(null);
|
||||
|
@ -47,6 +47,7 @@
|
||||
"private": "私密评论",
|
||||
"private_placeholder": "悄悄地说一句...",
|
||||
"reply": "回复",
|
||||
"show_client_info": "展示客户端信息",
|
||||
"submit": "提交",
|
||||
"unlike": "取消点赞",
|
||||
"unlike_success": "已取消点赞",
|
||||
|
Reference in New Issue
Block a user