From 1350cf8a388ee7ab4370f2c476bdaf065840ca99 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Sat, 22 Aug 2026 00:48:11 -0700 Subject: [PATCH] enhance: add permalinks to pull request reviews (#38849) (#39036) Backport #38849 by @silverwind 1. Make review threads linkable via `#pullrequestreview-` 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. image Co-authored-by: silverwind --- models/issues/review.go | 22 +++++++++++-------- models/issues/review_test.go | 10 +++++++++ .../issue/sidebar/stopwatch_timetracker.tmpl | 2 +- .../repo/issue/view_content/comments.tmpl | 14 +++++++----- templates/shared/secrets/add_list.tmpl | 2 +- templates/shared/user/authorlink.tmpl | 2 +- templates/shared/variables/variable_list.tmpl | 2 +- web_src/css/base.css | 2 -- web_src/css/repo.css | 10 ++++++--- web_src/js/features/repo-issue.ts | 5 +++-- 10 files changed, 45 insertions(+), 26 deletions(-) diff --git a/models/issues/review.go b/models/issues/review.go index a051440290e..58ad532da78 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -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 diff --git a/models/issues/review_test.go b/models/issues/review_test.go index 6010f74c415..653081c8ad0 100644 --- a/models/issues/review_test.go +++ b/models/issues/review_test.go @@ -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}) diff --git a/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl b/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl index b23024e62bc..853820d5490 100644 --- a/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl +++ b/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl @@ -72,7 +72,7 @@ {{range $user, $trackedtime := .WorkingUsers}}
{{template "shared/user/avatarlink" dict "user" $user}} -
+ diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 2171b3616ff..c55de5e9743 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -361,9 +361,10 @@
{{else if eq .Type 22}}
-
- {{$reviewType := 2}}{{/* default to "comment" type if the review record is missing */}} - {{if .Review}}{{$reviewType = .Review.Type}}{{end}} + {{/* default to the "comment" type and the comment anchor if the review record is missing */}} + {{$reviewType := 2}}{{$reviewHashTag := .HashTag}} + {{if .Review}}{{$reviewType = .Review.Type}}{{$reviewHashTag = .Review.HashTag}}{{end}} +
{{if not .OriginalAuthor}} {{/* Some timeline avatars need a offset to correctly align with their speech bubble. The condition depends on whether the comment has contents/attachments, @@ -381,12 +382,13 @@ {{template "repo/issue/view_content/comments_authorlink" dict "comment" .}} + {{$createdLink := HTMLFormat `%s` $reviewHashTag $createdStr}} {{if eq $reviewType 1}} - {{ctx.Locale.Tr "repo.issues.review.approve" $createdStr}} + {{ctx.Locale.Tr "repo.issues.review.approve" $createdLink}} {{else if eq $reviewType 3}} - {{ctx.Locale.Tr "repo.issues.review.reject" $createdStr}} + {{ctx.Locale.Tr "repo.issues.review.reject" $createdLink}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.comment" $createdStr}} + {{ctx.Locale.Tr "repo.issues.review.comment" $createdLink}} {{end}} {{if and .Review .Review.Dismissed}}
{{ctx.Locale.Tr "repo.issues.review.dismissed_label"}}
diff --git a/templates/shared/secrets/add_list.tmpl b/templates/shared/secrets/add_list.tmpl index f16bd5e9aa5..3e4a86a1183 100644 --- a/templates/shared/secrets/add_list.tmpl +++ b/templates/shared/secrets/add_list.tmpl @@ -34,7 +34,7 @@
- + {{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
- + {{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}