0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-12-10 11:05:19 +01:00

Merge 42bcea7b783aaa17dd02eaa9be5b958894636d8a into c287a8cdb589172bbba8969357a671dabc6596bd

This commit is contained in:
Lunny Xiao 2025-12-06 10:38:38 -05:00 committed by GitHub
commit a19daf48ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 114 additions and 18 deletions

View File

@ -102,3 +102,21 @@
review_id: 22 review_id: 22
assignee_id: 5 assignee_id: 5
created_unix: 946684817 created_unix: 946684817
-
id: 12
type: 22 # review comment
poster_id: 1
issue_id: 3
content: ""
review_id: 5
created_unix: 946684810
-
id: 13
type: 21 # code comment
poster_id: 1
issue_id: 3
content: "Some codes need to be changed"
review_id: 5
created_unix: 946684810

View File

@ -1099,21 +1099,21 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us
} }
// DeleteComment deletes the comment // DeleteComment deletes the comment
func DeleteComment(ctx context.Context, comment *Comment) error { func DeleteComment(ctx context.Context, comment *Comment) (*Comment, error) {
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil { if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil {
return err return nil, err
} }
if _, err := db.DeleteByBean(ctx, &ContentHistory{ if _, err := db.DeleteByBean(ctx, &ContentHistory{
CommentID: comment.ID, CommentID: comment.ID,
}); err != nil { }); err != nil {
return err return nil, err
} }
if comment.Type.CountedAsConversation() { if comment.Type.CountedAsConversation() {
if err := UpdateIssueNumComments(ctx, comment.IssueID); err != nil { if err := UpdateIssueNumComments(ctx, comment.IssueID); err != nil {
return err return nil, err
} }
} }
if _, err := e.Table("action"). if _, err := e.Table("action").
@ -1121,14 +1121,48 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
Update(map[string]any{ Update(map[string]any{
"is_deleted": true, "is_deleted": true,
}); err != nil { }); err != nil {
return err return nil, err
}
var deletedReviewComment *Comment
// delete review & review comment if the code comment is the last comment of the review
if comment.Type == CommentTypeCode && comment.ReviewID > 0 {
if err := comment.LoadReview(ctx); err != nil {
return nil, err
}
if comment.Review != nil && comment.Review.Type == ReviewTypeComment {
res, err := db.GetEngine(ctx).ID(comment.ReviewID).
Where("NOT EXISTS (SELECT 1 FROM comment WHERE review_id = ? AND `type` = ?)", comment.ReviewID, CommentTypeCode).
Delete(new(Review))
if err != nil {
return nil, err
}
if res > 0 {
var reviewComment Comment
has, err := db.GetEngine(ctx).Where("review_id = ?", comment.ReviewID).
And("`type` = ?", CommentTypeReview).Get(&reviewComment)
if err != nil {
return nil, err
}
if has && reviewComment.Content == "" {
if _, err := db.GetEngine(ctx).ID(reviewComment.ID).Delete(new(Comment)); err != nil {
return nil, err
}
deletedReviewComment = &reviewComment
}
comment.ReviewID = 0 // reset review ID to 0 for the notification
}
}
} }
if err := comment.neuterCrossReferences(ctx); err != nil { if err := comment.neuterCrossReferences(ctx); err != nil {
return err return nil, err
} }
return DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID}) if err := DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID}); err != nil {
return nil, err
}
return deletedReviewComment, nil
} }
// UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id // UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id

View File

@ -38,9 +38,8 @@ func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) {
reviewComments, err := prs.LoadReviewCommentsCounts(t.Context()) reviewComments, err := prs.LoadReviewCommentsCounts(t.Context())
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, reviewComments, 2) assert.Len(t, reviewComments, 2)
for _, pr := range prs { assert.Equal(t, 1, reviewComments[prs[0].IssueID])
assert.Equal(t, 1, reviewComments[pr.IssueID]) assert.Equal(t, 2, reviewComments[prs[1].IssueID])
}
} }
func TestPullRequestList_LoadReviews(t *testing.T) { func TestPullRequestList_LoadReviews(t *testing.T) {

View File

@ -726,7 +726,7 @@ func deleteIssueComment(ctx *context.APIContext) {
return return
} }
if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil { if _, err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }

View File

@ -328,12 +328,18 @@ func DeleteComment(ctx *context.Context) {
return return
} }
if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil { deletedReviewComment, err := issue_service.DeleteComment(ctx, ctx.Doer, comment)
if err != nil {
ctx.ServerError("DeleteComment", err) ctx.ServerError("DeleteComment", err)
return return
} }
ctx.Status(http.StatusOK) res := map[string]any{}
if deletedReviewComment != nil {
res["deletedReviewCommentHashTag"] = deletedReviewComment.HashTag()
}
ctx.JSON(http.StatusOK, res)
} }
// ChangeCommentReaction create a reaction for comment // ChangeCommentReaction create a reaction for comment

View File

@ -138,17 +138,17 @@ func UpdateComment(ctx context.Context, c *issues_model.Comment, contentVersion
} }
// DeleteComment deletes the comment // DeleteComment deletes the comment
func DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) error { func DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) (*issues_model.Comment, error) {
err := db.WithTx(ctx, func(ctx context.Context) error { deletedReviewComment, err := db.WithTx2(ctx, func(ctx context.Context) (*issues_model.Comment, error) {
return issues_model.DeleteComment(ctx, comment) return issues_model.DeleteComment(ctx, comment)
}) })
if err != nil { if err != nil {
return err return nil, err
} }
notify_service.DeleteComment(ctx, doer, comment) notify_service.DeleteComment(ctx, doer, comment)
return nil return deletedReviewComment, nil
} }
// LoadCommentPushCommits Load push commits // LoadCommentPushCommits Load push commits

View File

@ -0,0 +1,32 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issue
import (
"testing"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
func Test_DeleteCommentWithReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 13})
assert.Equal(t, int64(5), comment.ReviewID)
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: comment.ReviewID})
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
// since this is the last comment of the review, it should be deleted when the comment is deleted
deletedReviewComment, err := DeleteComment(t.Context(), user1, comment)
assert.NoError(t, err)
assert.NotNil(t, deletedReviewComment)
// the review should be deleted as well
unittest.AssertNotExistsBean(t, &issues_model.Review{ID: review.ID})
unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: deletedReviewComment.ID})
}

View File

@ -117,7 +117,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
} }
for _, comment := range comments { for _, comment := range comments {
if err = issues_model.DeleteComment(ctx, comment); err != nil { if _, err = issues_model.DeleteComment(ctx, comment); err != nil {
return err return err
} }
} }

View File

@ -149,6 +149,13 @@ export function initRepoIssueCommentDelete() {
counter.textContent = String(num); counter.textContent = String(num);
} }
const json: Record<string, any> = await response.json();
if (json.errorMessage) throw new Error(json.errorMessage);
if (json.deletedReviewCommentHashTag) {
document.querySelector(`#${json.deletedReviewCommentHashTag}`)?.remove();
}
document.querySelector(`#${deleteButton.getAttribute('data-comment-id')}`)?.remove(); document.querySelector(`#${deleteButton.getAttribute('data-comment-id')}`)?.remove();
if (conversationHolder && !conversationHolder.querySelector('.comment')) { if (conversationHolder && !conversationHolder.querySelector('.comment')) {