diff --git a/internal/controller/comment_controller.go b/internal/controller/comment_controller.go index b9beead94..73a8f9980 100644 --- a/internal/controller/comment_controller.go +++ b/internal/controller/comment_controller.go @@ -195,6 +195,8 @@ func (cc *CommentController) UpdateComment(ctx *gin.Context) { req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.IsAdmin = middleware.GetIsAdminFromContext(ctx) + req.IP = ctx.ClientIP() + req.UserAgent = ctx.GetHeader("User-Agent") canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{ permission.CommentEdit, permission.LinkUrlLimit, diff --git a/internal/schema/comment_schema.go b/internal/schema/comment_schema.go index a9c8a21a3..9538649b6 100644 --- a/internal/schema/comment_schema.go +++ b/internal/schema/comment_schema.go @@ -88,6 +88,9 @@ type UpdateCommentReq struct { // user id UserID string `json:"-"` IsAdmin bool `json:"-"` + // for the reviewer plugin (edits go through review like new comments) + IP string `json:"-"` + UserAgent string `json:"-"` // whether user can edit it CanEdit bool `json:"-"` @@ -111,6 +114,8 @@ func (req *UpdateCommentReq) Check() (errFields []*validator.FormErrorField, err type UpdateCommentResp struct { // comment id CommentID string `json:"comment_id"` + // status after the edit was reviewed (11 = pending, hidden until a moderator approves it) + Status int `json:"status"` // original comment content OriginalText string `json:"original_text"` // parsed comment content @@ -175,6 +180,8 @@ type GetCommentReq struct { type GetCommentResp struct { // comment id CommentID string `json:"comment_id"` + // comment status (11 = pending review) so the UI can tell the author + Status int `json:"status"` // create time CreatedAt int64 `json:"created_at"` diff --git a/internal/service/comment/comment_service.go b/internal/service/comment/comment_service.go index 0decd5847..f6d4ecb99 100644 --- a/internal/service/comment/comment_service.go +++ b/internal/service/comment/comment_service.go @@ -323,10 +323,24 @@ func (cs *CommentService) UpdateComment(ctx context.Context, req *schema.UpdateC if err = cs.commentRepo.UpdateCommentContent(ctx, old.ID, req.OriginalText, req.ParsedText); err != nil { return nil, err } + // an edit is reviewed like a new comment (upstream only reviews on creation, so a benign + // comment could be edited into anything). Staff are skipped by the reviewer plugin itself. + newStatus := old.Status + if old.Status == entity.CommentStatusAvailable { + edited := *old + edited.OriginalText, edited.ParsedText = req.OriginalText, req.ParsedText + if status := cs.reviewService.AddCommentReview(ctx, &edited, req.IP, req.UserAgent); status != entity.CommentStatusAvailable { + if err = cs.commentCommonRepo.UpdateCommentStatus(ctx, old.ID, status); err != nil { + return nil, err + } + newStatus = status + } + } resp = &schema.UpdateCommentResp{ CommentID: old.ID, OriginalText: req.OriginalText, ParsedText: req.ParsedText, + Status: newStatus, } cs.eventQueueService.Send(ctx, schema.NewEvent(constant.EventCommentUpdate, req.UserID).TID(old.ID). CID(old.ID, old.UserID)) diff --git a/ui/src/components/Comment/index.tsx b/ui/src/components/Comment/index.tsx index af2ddbd4f..c9e83dbb8 100644 --- a/ui/src/components/Comment/index.tsx +++ b/ui/src/components/Comment/index.tsx @@ -27,7 +27,12 @@ import unionBy from 'lodash/unionBy'; import * as Types from '@/common/interface'; import { Modal } from '@/components'; -import { usePageUsers, useReportModal, useCaptchaModal } from '@/hooks'; +import { + usePageUsers, + useReportModal, + useCaptchaModal, + useToast, +} from '@/hooks'; import { matchedUsers, parseUserInfo, @@ -78,6 +83,7 @@ const Comment: FC = ({ objectId, mode, commentId, children }) => { const editCaptcha = useCaptchaPlugin('edit'); const dCaptcha = useCaptchaPlugin('delete'); const vCaptcha = useCaptchaPlugin('vote'); + const toast = useToast(); const { t } = useTranslation('translation', { keyPrefix: 'comment' }); @@ -157,6 +163,15 @@ const Comment: FC = ({ objectId, mode, commentId, children }) => { return updateComment(up) .then(async (res) => { await editCaptcha?.close(); + // the edit went to the review queue: hide it and tell the author + if (res.status === 11) { + toast.onShow({ + msg: t('post_pending', { keyPrefix: 'messages' }), + variant: 'warning', + }); + setComments(comments.filter((c) => c.comment_id !== item.comment_id)); + return; + } setComments( comments.map((comment) => { if (comment.comment_id === item.comment_id) { @@ -191,6 +206,16 @@ const Comment: FC = ({ objectId, mode, commentId, children }) => { return addComment(req) .then(async (res) => { await addCaptcha?.close(); + // a new comment sent to the review queue is not shown, the author gets a notice + if (res.status === 11) { + toast.onShow({ + msg: t('post_pending', { keyPrefix: 'messages' }), + variant: 'warning', + }); + updateCurrentReplyId(''); + setVisibleComment(false); + return; + } if (item.type === 'reply') { const index = comments.findIndex( (comment) => comment.comment_id === item.comment_id,