enhance: add permalinks to pull request reviews (#38849) (#39036)

Backport #38849 by @silverwind

1. Make review threads linkable via `#pullrequestreview-<reviewID>`
2. Improve CSS so username and timestamp go colored on hover.
3. CSS cleanup, remove dead rules, nonexistant class name, make
`.suppressed` actually do what it says in the doc above.

<img width="351" height="84" alt="image"
src="https://github.com/user-attachments/assets/6ddf15d4-3d6e-4846-ab82-0ad0b19b372f"
/>

Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
Giteabot
2026-08-22 00:48:11 -07:00
committed by GitHub
parent f64ee9d1ed
commit 1350cf8a38
10 changed files with 45 additions and 26 deletions
+13 -9
View File
@@ -1065,19 +1065,23 @@ func (r *Review) GetCodeCommentsCount(ctx context.Context) int {
return int(count)
}
// HTMLURL formats a URL-string to the related review issue-comment
// HashTag returns unique hash tag for review.
func (r *Review) HashTag() string {
return fmt.Sprintf("pullrequestreview-%d", r.ID)
}
// HTMLURL formats a URL-string to the review on the pull request page
func (r *Review) HTMLURL(ctx context.Context) string {
opts := FindCommentsOptions{
Type: CommentTypeReview,
IssueID: r.IssueID,
ReviewID: r.ID,
if r.Type != ReviewTypeApprove && r.Type != ReviewTypeComment && r.Type != ReviewTypeReject {
return "" // only submitted reviews get a timeline block carrying the anchor
}
comment := new(Comment)
has, err := db.GetEngine(ctx).Where(opts.ToConds()).Get(comment)
if err != nil || !has {
if err := r.LoadIssue(ctx); err != nil {
return ""
}
return comment.HTMLURL(ctx)
if err := r.Issue.LoadRepo(ctx); err != nil {
return ""
}
return r.Issue.HTMLURL(ctx) + "#" + r.HashTag()
}
// RemapExternalUser ExternalUserRemappable interface
+10
View File
@@ -12,6 +12,7 @@ import (
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"github.com/stretchr/testify/assert"
)
@@ -28,6 +29,15 @@ func TestGetReviewByID(t *testing.T) {
assert.True(t, issues_model.IsErrReviewNotExist(err), "IsErrReviewNotExist")
}
func TestReview_HTMLURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1})
assert.Equal(t, setting.AppURL+"user2/repo1/pulls/2#pullrequestreview-1", review.HTMLURL(t.Context()))
pendingReview := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 4})
assert.Empty(t, pendingReview.HTMLURL(t.Context()))
}
func TestReview_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1})