From 2d0e1a46e27d8c627b3aa155493f5cb090dad893 Mon Sep 17 00:00:00 2001 From: Snowykami Date: Sat, 13 Sep 2025 16:04:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E7=9A=84=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=98=BE=E7=A4=BA=E9=80=89=E9=A1=B9=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E5=92=8C=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/dto/comment.go | 69 ++--- internal/model/comment.go | 27 +- internal/repo/comment.go | 1 + internal/service/comment.go | 303 ++++++++++--------- pkg/utils/oidc.go | 82 ++--- web/src/api/comment.ts | 7 +- web/src/components/comment/comment-input.tsx | 16 +- web/src/components/comment/comment-item.tsx | 3 +- web/src/components/comment/index.tsx | 4 +- web/src/locales/zh-CN.json | 1 + 10 files changed, 279 insertions(+), 234 deletions(-) diff --git a/internal/dto/comment.go b/internal/dto/comment.go index 783fb33..5af89f5 100644 --- a/internal/dto/comment.go +++ b/internal/dto/comment.go @@ -1,47 +1,48 @@ package dto type CommentDto struct { - ID uint `json:"id"` - TargetID uint `json:"target_id"` - TargetType string `json:"target_type"` // 目标类型,如 "post", "page" - Content string `json:"content"` - ReplyID uint `json:"reply_id"` // 回复的评论ID - Depth int `json:"depth"` // 评论的层级深度 - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - User UserDto `json:"user"` // 评论的 - ReplyCount uint64 `json:"reply_count"` // 回复数量 - LikeCount uint64 `json:"like_count"` // 点赞数量 - IsLiked bool `json:"is_liked"` // 当前用户是否点赞 - IsPrivate bool `json:"is_private"` - Location string `json:"location"` // 用户位置,基于IP - OS string `json:"os"` // 用户操作系统,基于User-Agent - Browser string `json:"browser"` // 用户浏览器,基于User-Agent + ID uint `json:"id"` + TargetID uint `json:"target_id"` + TargetType string `json:"target_type"` // 目标类型,如 "post", "page" + Content string `json:"content"` + ReplyID uint `json:"reply_id"` // 回复的评论ID + Depth int `json:"depth"` // 评论的层级深度 + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + User UserDto `json:"user"` // 评论的 + ReplyCount uint64 `json:"reply_count"` // 回复数量 + LikeCount uint64 `json:"like_count"` // 点赞数量 + IsLiked bool `json:"is_liked"` // 当前用户是否点赞 + IsPrivate bool `json:"is_private"` + Location string `json:"location"` // 用户位置,基于IP + OS string `json:"os"` // 用户操作系统,基于User-Agent + Browser string `json:"browser"` // 用户浏览器,基于User-Agent } type CreateCommentReq struct { - TargetID uint `json:"target_id" binding:"required"` // 目标ID - TargetType string `json:"target_type" binding:"required"` // 目标类型,如 "post", "page" - Content string `json:"content" binding:"required"` // 评论内容 - ReplyID uint `json:"reply_id"` // 回复的评论ID - IsPrivate bool `json:"is_private"` // 是否私密评论,默认false - RemoteAddr string `json:"remote_addr"` // 远程地址 - UserAgent string `json:"user_agent"` // 用户代理 + TargetID uint `json:"target_id" binding:"required"` // 目标ID + TargetType string `json:"target_type" binding:"required"` // 目标类型,如 "post", "page" + Content string `json:"content" binding:"required"` // 评论内容 + ReplyID uint `json:"reply_id"` // 回复的评论ID + 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 { - CommentID uint `json:"comment_id" binding:"required"` // 评论ID - Content string `json:"content" binding:"required"` // 评论内容 - IsPrivate bool `json:"is_private"` // 是否私密 + CommentID uint `json:"comment_id" binding:"required"` // 评论ID + Content string `json:"content" binding:"required"` // 评论内容 + IsPrivate bool `json:"is_private"` // 是否私密 } type GetCommentListReq struct { - TargetID uint `json:"target_id" binding:"required"` - TargetType string `json:"target_type" binding:"required"` - CommentID uint `json:"comment_id"` // 获取某条评论的所有子评论 - OrderBy string `json:"order_by"` // 排序方式 - Page uint64 `json:"page"` // 页码 - Size uint64 `json:"size"` - Desc bool `json:"desc"` - Depth int `json:"depth"` // 评论的层级深度 + TargetID uint `json:"target_id" binding:"required"` + TargetType string `json:"target_type" binding:"required"` + CommentID uint `json:"comment_id"` // 获取某条评论的所有子评论 + OrderBy string `json:"order_by"` // 排序方式 + Page uint64 `json:"page"` // 页码 + Size uint64 `json:"size"` + Desc bool `json:"desc"` + Depth int `json:"depth"` // 评论的层级深度 } diff --git a/internal/model/comment.go b/internal/model/comment.go index 83901d6..31af712 100644 --- a/internal/model/comment.go +++ b/internal/model/comment.go @@ -7,19 +7,20 @@ import ( type Comment struct { gorm.Model - UserID uint `gorm:"index"` // 评论的用户ID - User User `gorm:"foreignKey:UserID;references:ID"` // 关联的用户 - TargetID uint `gorm:"index"` // 目标ID - TargetType string `gorm:"index"` // 目标类型,如 "post", "page" - ReplyID uint `gorm:"index"` // 回复的评论ID - Content string `gorm:"type:text"` // 评论内容 - Depth int `gorm:"default:0"` // 评论的层级深度,从0开始计数 - IsPrivate bool `gorm:"default:false"` // 是否为私密评论,私密评论只有评论者和被评论对象所有者可见 - RemoteAddr string `gorm:"type:text"` // 远程地址 - UserAgent string `gorm:"type:text"` - Location string `gorm:"type:text"` // 用户位置,基于IP - LikeCount uint64 - CommentCount uint64 + UserID uint `gorm:"index"` // 评论的用户ID + User User `gorm:"foreignKey:UserID;references:ID"` // 关联的用户 + TargetID uint `gorm:"index"` // 目标ID + TargetType string `gorm:"index"` // 目标类型,如 "post", "page" + ReplyID uint `gorm:"index"` // 回复的评论ID + Content string `gorm:"type:text"` // 评论内容 + Depth int `gorm:"default:0"` // 评论的层级深度,从0开始计数 + IsPrivate bool `gorm:"default:false"` // 是否为私密评论,私密评论只有评论者和被评论对象所有者可见 + RemoteAddr string `gorm:"type:text"` // 远程地址 + UserAgent string `gorm:"type:text"` + Location string `gorm:"type:text"` // 用户位置,基于IP + LikeCount uint64 + CommentCount uint64 + ShowClientInfo bool `gorm:"default:false"` // 是否显示客户端信息 } func (c *Comment) AfterCreate(tx *gorm.DB) (err error) { diff --git a/internal/repo/comment.go b/internal/repo/comment.go index b18ec94..41246ef 100644 --- a/internal/repo/comment.go +++ b/internal/repo/comment.go @@ -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) diff --git a/internal/service/comment.go b/internal/service/comment.go index 5b39575..714c62a 100644 --- a/internal/service/comment.go +++ b/internal/service/comment.go @@ -1,184 +1,213 @@ package service import ( - "context" - "strconv" + "context" + "strconv" - "github.com/snowykami/neo-blog/pkg/constant" - "github.com/snowykami/neo-blog/pkg/utils" + "github.com/snowykami/neo-blog/pkg/constant" + "github.com/snowykami/neo-blog/pkg/utils" - "github.com/snowykami/neo-blog/internal/ctxutils" - "github.com/snowykami/neo-blog/internal/dto" - "github.com/snowykami/neo-blog/internal/model" - "github.com/snowykami/neo-blog/internal/repo" - "github.com/snowykami/neo-blog/pkg/errs" + "github.com/snowykami/neo-blog/internal/ctxutils" + "github.com/snowykami/neo-blog/internal/dto" + "github.com/snowykami/neo-blog/internal/model" + "github.com/snowykami/neo-blog/internal/repo" + "github.com/snowykami/neo-blog/pkg/errs" ) type CommentService struct{} func NewCommentService() *CommentService { - return &CommentService{} + return &CommentService{} } func (cs *CommentService) CreateComment(ctx context.Context, req *dto.CreateCommentReq) (uint, error) { - currentUser, ok := ctxutils.GetCurrentUser(ctx) - if !ok { - return 0, errs.ErrUnauthorized - } + currentUser, ok := ctxutils.GetCurrentUser(ctx) + if !ok { + return 0, errs.ErrUnauthorized + } - if ok, err := cs.checkTargetExists(req.TargetID, req.TargetType); !ok { - if err != nil { - return 0, errs.New(errs.ErrBadRequest.Code, "target not found", err) - } - return 0, errs.ErrBadRequest - } + if ok, err := cs.checkTargetExists(req.TargetID, req.TargetType); !ok { + if err != nil { + return 0, errs.New(errs.ErrBadRequest.Code, "target not found", err) + } + return 0, errs.ErrBadRequest + } - comment := &model.Comment{ - Content: req.Content, - ReplyID: req.ReplyID, - TargetID: req.TargetID, - TargetType: req.TargetType, - UserID: currentUser.ID, - IsPrivate: req.IsPrivate, - RemoteAddr: req.RemoteAddr, - UserAgent: req.UserAgent, - } + comment := &model.Comment{ + Content: req.Content, + ReplyID: req.ReplyID, + TargetID: req.TargetID, + TargetType: req.TargetType, + UserID: currentUser.ID, + IsPrivate: req.IsPrivate, + RemoteAddr: req.RemoteAddr, + UserAgent: req.UserAgent, + ShowClientInfo: req.ShowClientInfo, + } - commentID, err := repo.Comment.CreateComment(comment) + commentID, err := repo.Comment.CreateComment(comment) - if err != nil { - return 0, err - } + if err != nil { + return 0, err + } - return commentID, nil + return commentID, nil } func (cs *CommentService) UpdateComment(ctx context.Context, req *dto.UpdateCommentReq) error { - currentUser, ok := ctxutils.GetCurrentUser(ctx) - if !ok { - return errs.ErrUnauthorized - } + currentUser, ok := ctxutils.GetCurrentUser(ctx) + if !ok { + return errs.ErrUnauthorized + } - comment, err := repo.Comment.GetComment(strconv.Itoa(int(req.CommentID))) - if err != nil { - return err - } + comment, err := repo.Comment.GetComment(strconv.Itoa(int(req.CommentID))) + if err != nil { + return err + } - if currentUser.ID != comment.UserID { - return errs.ErrForbidden - } + if currentUser.ID != comment.UserID { + return errs.ErrForbidden + } - comment.Content = req.Content - comment.IsPrivate = req.IsPrivate + comment.Content = req.Content + comment.IsPrivate = req.IsPrivate - err = repo.Comment.UpdateComment(comment) + err = repo.Comment.UpdateComment(comment) - if err != nil { - return err - } + if err != nil { + return err + } - return nil + return nil } func (cs *CommentService) DeleteComment(ctx context.Context, commentID string) error { - currentUser, ok := ctxutils.GetCurrentUser(ctx) - if !ok { - return errs.ErrUnauthorized - } - if commentID == "" { - return errs.ErrBadRequest - } + currentUser, ok := ctxutils.GetCurrentUser(ctx) + if !ok { + return errs.ErrUnauthorized + } + if commentID == "" { + return errs.ErrBadRequest + } - comment, err := repo.Comment.GetComment(commentID) - if err != nil { - return errs.New(errs.ErrNotFound.Code, "comment not found", err) - } + comment, err := repo.Comment.GetComment(commentID) + if err != nil { + return errs.New(errs.ErrNotFound.Code, "comment not found", err) + } - if comment.UserID != currentUser.ID { - return errs.ErrForbidden - } + if comment.UserID != currentUser.ID { + return errs.ErrForbidden + } - if err := repo.Comment.DeleteComment(commentID); err != nil { - return err - } + if err := repo.Comment.DeleteComment(commentID); err != nil { + return err + } - return nil + return nil } func (cs *CommentService) GetComment(ctx context.Context, commentID string) (*dto.CommentDto, error) { - comment, err := repo.Comment.GetComment(commentID) + comment, err := repo.Comment.GetComment(commentID) - if err != nil { - return nil, errs.New(errs.ErrNotFound.Code, "comment not found", err) - } + if err != nil { + return nil, errs.New(errs.ErrNotFound.Code, "comment not found", err) + } - commentDto := dto.CommentDto{ - ID: comment.ID, - TargetID: comment.TargetID, - TargetType: comment.TargetType, - Content: comment.Content, - ReplyID: comment.ReplyID, - Depth: comment.Depth, - CreatedAt: comment.CreatedAt.String(), - UpdatedAt: comment.UpdatedAt.String(), - User: comment.User.ToDto(), - } - // TODO: 返回更多字段 + 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 = "" + } - return &commentDto, err + commentDto := dto.CommentDto{ + ID: comment.ID, + Content: comment.Content, + TargetID: comment.TargetID, + TargetType: comment.TargetType, + ReplyID: comment.ReplyID, + 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, + } + + return &commentDto, err } func (cs *CommentService) GetCommentList(ctx context.Context, req *dto.GetCommentListReq) ([]dto.CommentDto, error) { - currentUserID := uint(0) - 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 - if currentUserID != 0 { - isLiked, _ = repo.Like.IsLiked(currentUserID, comment.ID, constant.TargetTypeComment) - } - ua := utils.ParseUA(comment.UserAgent) - commentDto := dto.CommentDto{ - ID: comment.ID, - Content: comment.Content, - TargetID: comment.TargetID, - TargetType: comment.TargetType, - ReplyID: comment.ReplyID, - 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, - } - commentDtos = append(commentDtos, commentDto) - } - return commentDtos, nil + currentUserID := uint(0) + 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 + if currentUserID != 0 { + isLiked, _ = repo.Like.IsLiked(currentUserID, comment.ID, constant.TargetTypeComment) + } + ua := utils.ParseUA(comment.UserAgent) + commentDto := dto.CommentDto{ + ID: comment.ID, + Content: comment.Content, + TargetID: comment.TargetID, + TargetType: comment.TargetType, + ReplyID: comment.ReplyID, + 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, + } + if !comment.ShowClientInfo { + commentDto.Location = "" + commentDto.OS = "" + commentDto.Browser = "" + } + commentDtos = append(commentDtos, commentDto) + } + return commentDtos, nil } func (cs *CommentService) checkTargetExists(targetID uint, targetType string) (bool, error) { - switch targetType { - case constant.TargetTypePost: - if _, err := repo.Post.GetPostByID(strconv.Itoa(int(targetID))); err != nil { - return false, errs.New(errs.ErrNotFound.Code, "post not found", err) - } - default: - return false, errs.New(errs.ErrBadRequest.Code, "invalid target type", nil) - } - return true, nil + switch targetType { + case constant.TargetTypePost: + if _, err := repo.Post.GetPostByID(strconv.Itoa(int(targetID))); err != nil { + return false, errs.New(errs.ErrNotFound.Code, "post not found", err) + } + default: + return false, errs.New(errs.ErrBadRequest.Code, "invalid target type", nil) + } + return true, nil } diff --git a/pkg/utils/oidc.go b/pkg/utils/oidc.go index 9b0f026..c571764 100644 --- a/pkg/utils/oidc.go +++ b/pkg/utils/oidc.go @@ -1,7 +1,7 @@ package utils import ( - "fmt" + "fmt" ) type oidcUtils struct{} @@ -10,59 +10,59 @@ var Oidc = oidcUtils{} // RequestToken 请求访问令牌 func (u *oidcUtils) RequestToken(tokenEndpoint, clientID, clientSecret, code, redirectURI string) (*TokenResponse, error) { - tokenResp, err := client.R(). - SetFormData(map[string]string{ - "grant_type": "authorization_code", - "client_id": clientID, - "client_secret": clientSecret, - "code": code, - "redirect_uri": redirectURI, - }). - SetHeader("Accept", "application/json"). - SetResult(&TokenResponse{}). - Post(tokenEndpoint) + tokenResp, err := client.R(). + SetFormData(map[string]string{ + "grant_type": "authorization_code", + "client_id": clientID, + "client_secret": clientSecret, + "code": code, + "redirect_uri": redirectURI, + }). + SetHeader("Accept", "application/json"). + SetResult(&TokenResponse{}). + Post(tokenEndpoint) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - if tokenResp.StatusCode() != 200 { - return nil, fmt.Errorf("状态码: %d,响应: %s", tokenResp.StatusCode(), tokenResp.String()) - } - return tokenResp.Result().(*TokenResponse), nil + if tokenResp.StatusCode() != 200 { + return nil, fmt.Errorf("状态码: %d,响应: %s", tokenResp.StatusCode(), tokenResp.String()) + } + return tokenResp.Result().(*TokenResponse), nil } // RequestUserInfo 请求用户信息 func (u *oidcUtils) RequestUserInfo(userInfoEndpoint, accessToken string) (*UserInfo, error) { - userInfoResp, err := client.R(). - SetHeader("Authorization", "Bearer "+accessToken). - SetHeader("Accept", "application/json"). - SetResult(&UserInfo{}). - Get(userInfoEndpoint) - if err != nil { - return nil, err - } + userInfoResp, err := client.R(). + SetHeader("Authorization", "Bearer "+accessToken). + SetHeader("Accept", "application/json"). + SetResult(&UserInfo{}). + Get(userInfoEndpoint) + if err != nil { + return nil, err + } - if userInfoResp.StatusCode() != 200 { - return nil, fmt.Errorf("状态码: %d,响应: %s", userInfoResp.StatusCode(), userInfoResp.String()) - } + if userInfoResp.StatusCode() != 200 { + return nil, fmt.Errorf("状态码: %d,响应: %s", userInfoResp.StatusCode(), userInfoResp.String()) + } - return userInfoResp.Result().(*UserInfo), nil + return userInfoResp.Result().(*UserInfo), nil } type TokenResponse struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` - IDToken string `json:"id_token,omitempty"` - RefreshToken string `json:"refresh_token,omitempty"` + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + IDToken string `json:"id_token,omitempty"` + RefreshToken string `json:"refresh_token,omitempty"` } // UserInfo 定义用户信息结构 type UserInfo struct { - Sub string `json:"sub"` - Name string `json:"name"` - Email string `json:"email"` - Picture string `json:"picture,omitempty"` - Groups []string `json:"groups,omitempty"` // 可选字段,OIDC提供的用户组信息 + Sub string `json:"sub"` + Name string `json:"name"` + Email string `json:"email"` + Picture string `json:"picture,omitempty"` + Groups []string `json:"groups,omitempty"` // 可选字段,OIDC提供的用户组信息 } diff --git a/web/src/api/comment.ts b/web/src/api/comment.ts index 0d5fdaf..32029b0 100644 --- a/web/src/api/comment.ts +++ b/web/src/api/comment.ts @@ -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> { const res = await axiosClient.post>('/comment/c', { @@ -26,7 +28,8 @@ export async function createComment( targetId, content, replyId, - isPrivate + isPrivate, + showClientInfo, }) return res.data } diff --git a/web/src/components/comment/comment-input.tsx b/web/src/components/comment/comment-input.tsx index 617018e..cf97341 100644 --- a/web/src/components/comment/comment-input.tsx +++ b/web/src/components/comment/comment-input.tsx @@ -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(""); }; @@ -62,7 +63,7 @@ export function CommentInput(
clickToUserProfile(user.username) : clickToLogin} className="cursor-pointer flex-shrink-0 w-10 h-10 fade-in"> - {user && } + {user && } {!user && }
@@ -75,6 +76,13 @@ export function CommentInput(
+
+ setShowClientInfo(checked === true)} + /> + +
{ + 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(); diff --git a/web/src/components/comment/index.tsx b/web/src/components/comment/index.tsx index ee0b7ea..a6464d8 100644 --- a/web/src/components/comment/index.tsx +++ b/web/src/components/comment/index.tsx @@ -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); diff --git a/web/src/locales/zh-CN.json b/web/src/locales/zh-CN.json index 3dd7090..83e090f 100644 --- a/web/src/locales/zh-CN.json +++ b/web/src/locales/zh-CN.json @@ -47,6 +47,7 @@ "private": "私密评论", "private_placeholder": "悄悄地说一句...", "reply": "回复", + "show_client_info": "展示客户端信息", "submit": "提交", "unlike": "取消点赞", "unlike_success": "已取消点赞",