aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2023-02-25 03:55:50 +0100
committerGitHub <noreply@github.com>2023-02-24 21:55:50 -0500
commit10cdcb9ea8077098921d72720f9f36fcfd950452 (patch)
treee966411cf1bbc62bf7059deaa9238c14c0a9a04d
parent843f81113ebe71fd725210c5a382268333865cc7 (diff)
downloadgitea-10cdcb9ea8077098921d72720f9f36fcfd950452.tar.gz
gitea-10cdcb9ea8077098921d72720f9f36fcfd950452.zip
Add "Reviewed by you" filter for pull requests (#22927)
This includes pull requests that you approved, requested changes or commented on. Currently such pull requests are not visible in any of the filters on /pulls, while they may need further action like merging, or prodding the author or reviewers. Especially when working with a large team on a repository it's helpful to get a full overview of pull requests that may need your attention, without having to sift through the complete list.
-rw-r--r--models/issues/issue.go61
-rw-r--r--options/locale/locale_en-US.ini1
-rw-r--r--routers/api/v1/repo/issue.go7
-rw-r--r--routers/web/repo/issue.go10
-rw-r--r--routers/web/user/home.go4
-rw-r--r--templates/repo/issue/list.tmpl3
-rw-r--r--templates/repo/issue/milestone_issues.tmpl3
-rw-r--r--templates/swagger/v1_json.tmpl6
-rw-r--r--templates/user/dashboard/issues.tmpl12
9 files changed, 100 insertions, 7 deletions
diff --git a/models/issues/issue.go b/models/issues/issue.go
index c59e9d14e5..edd74261ec 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -1148,6 +1148,7 @@ type IssuesOptions struct { //nolint
PosterID int64
MentionedID int64
ReviewRequestedID int64
+ ReviewedID int64
SubscriberID int64
MilestoneIDs []int64
ProjectID int64
@@ -1262,6 +1263,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}
+ if opts.ReviewedID > 0 {
+ applyReviewedCondition(sess, opts.ReviewedID)
+ }
+
if opts.SubscriberID > 0 {
applySubscribedCondition(sess, opts.SubscriberID)
}
@@ -1432,6 +1437,36 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
}
+func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session {
+ // Query for pull requests where you are a reviewer or commenter, excluding
+ // any pull requests already returned by the the review requested filter.
+ notPoster := builder.Neq{"issue.poster_id": reviewedID}
+ reviewed := builder.In("issue.id", builder.
+ Select("issue_id").
+ From("review").
+ Where(builder.And(
+ builder.Neq{"type": ReviewTypeRequest},
+ builder.Or(
+ builder.Eq{"reviewer_id": reviewedID},
+ builder.In("reviewer_team_id", builder.
+ Select("team_id").
+ From("team_user").
+ Where(builder.Eq{"uid": reviewedID}),
+ ),
+ ),
+ )),
+ )
+ commented := builder.In("issue.id", builder.
+ Select("issue_id").
+ From("comment").
+ Where(builder.And(
+ builder.Eq{"poster_id": reviewedID},
+ builder.In("type", CommentTypeComment, CommentTypeCode, CommentTypeReview),
+ )),
+ )
+ return sess.And(notPoster, builder.Or(reviewed, commented))
+}
+
func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
return sess.And(
builder.
@@ -1586,6 +1621,7 @@ type IssueStats struct {
CreateCount int64
MentionCount int64
ReviewRequestedCount int64
+ ReviewedCount int64
}
// Filter modes.
@@ -1595,6 +1631,7 @@ const (
FilterModeCreate
FilterModeMention
FilterModeReviewRequested
+ FilterModeReviewed
FilterModeYourRepositories
)
@@ -1608,6 +1645,7 @@ type IssueStatsOptions struct {
MentionedID int64
PosterID int64
ReviewRequestedID int64
+ ReviewedID int64
IsPull util.OptionalBool
IssueIDs []int64
}
@@ -1646,6 +1684,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
accum.CreateCount += stats.CreateCount
accum.OpenCount += stats.MentionCount
accum.ReviewRequestedCount += stats.ReviewRequestedCount
+ accum.ReviewedCount += stats.ReviewedCount
i = chunk
}
return accum, nil
@@ -1703,6 +1742,10 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}
+ if opts.ReviewedID > 0 {
+ applyReviewedCondition(sess, opts.ReviewedID)
+ }
+
switch opts.IsPull {
case util.OptionalBoolTrue:
sess.And("issue.is_pull=?", true)
@@ -1843,6 +1886,19 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
if err != nil {
return nil, err
}
+ case FilterModeReviewed:
+ stats.OpenCount, err = applyReviewedCondition(sess(cond), opts.UserID).
+ And("issue.is_closed = ?", false).
+ Count(new(Issue))
+ if err != nil {
+ return nil, err
+ }
+ stats.ClosedCount, err = applyReviewedCondition(sess(cond), opts.UserID).
+ And("issue.is_closed = ?", true).
+ Count(new(Issue))
+ if err != nil {
+ return nil, err
+ }
}
cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
@@ -1871,6 +1927,11 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
return nil, err
}
+ stats.ReviewedCount, err = applyReviewedCondition(sess(cond), opts.UserID).Count(new(Issue))
+ if err != nil {
+ return nil, err
+ }
+
return stats, nil
}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index c818b0dcc5..2109950ca8 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1323,6 +1323,7 @@ issues.filter_type.assigned_to_you = Assigned to you
issues.filter_type.created_by_you = Created by you
issues.filter_type.mentioning_you = Mentioning you
issues.filter_type.review_requested = Review requested
+issues.filter_type.reviewed_by_you = Reviewed by you
issues.filter_sort = Sort
issues.filter_sort.latest = Newest
issues.filter_sort.oldest = Oldest
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 458838b935..06bf06b4e8 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -92,6 +92,10 @@ func SearchIssues(ctx *context.APIContext) {
// in: query
// description: filter pulls requesting your review, default is false
// type: boolean
+ // - name: reviewed
+ // in: query
+ // description: filter pulls reviewed by you, default is false
+ // type: boolean
// - name: owner
// in: query
// description: filter by owner
@@ -266,6 +270,9 @@ func SearchIssues(ctx *context.APIContext) {
if ctx.FormBool("review_requested") {
issuesOpt.ReviewRequestedID = ctxUserID
}
+ if ctx.FormBool("reviewed") {
+ issuesOpt.ReviewedID = ctxUserID
+ }
if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil {
ctx.Error(http.StatusInternalServerError, "Issues", err)
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 05ba26a70c..745d6e70a0 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -138,7 +138,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
var err error
viewType := ctx.FormString("type")
sortType := ctx.FormString("sort")
- types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested"}
+ types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested", "reviewed_by"}
if !util.SliceContainsString(types, viewType, true) {
viewType = "all"
}
@@ -148,6 +148,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
posterID = ctx.FormInt64("poster")
mentionedID int64
reviewRequestedID int64
+ reviewedID int64
forceEmpty bool
)
@@ -161,6 +162,8 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
assigneeID = ctx.Doer.ID
case "review_requested":
reviewRequestedID = ctx.Doer.ID
+ case "reviewed_by":
+ reviewedID = ctx.Doer.ID
}
}
@@ -208,6 +211,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
MentionedID: mentionedID,
PosterID: posterID,
ReviewRequestedID: reviewRequestedID,
+ ReviewedID: reviewedID,
IsPull: isPullOption,
IssueIDs: issueIDs,
})
@@ -255,6 +259,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
PosterID: posterID,
MentionedID: mentionedID,
ReviewRequestedID: reviewRequestedID,
+ ReviewedID: reviewedID,
MilestoneIDs: mileIDs,
ProjectID: projectID,
IsClosed: util.OptionalBoolOf(isShowClosed),
@@ -2425,6 +2430,9 @@ func SearchIssues(ctx *context.Context) {
if ctx.FormBool("review_requested") {
issuesOpt.ReviewRequestedID = ctxUserID
}
+ if ctx.FormBool("reviewed") {
+ issuesOpt.ReviewedID = ctxUserID
+ }
if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil {
ctx.Error(http.StatusInternalServerError, "Issues", err.Error())
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 2593ab148c..a0a5dc3c4b 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -385,6 +385,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
filterMode = issues_model.FilterModeMention
case "review_requested":
filterMode = issues_model.FilterModeReviewRequested
+ case "reviewed_by":
+ filterMode = issues_model.FilterModeReviewed
case "your_repositories":
fallthrough
default:
@@ -453,6 +455,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
opts.MentionedID = ctx.Doer.ID
case issues_model.FilterModeReviewRequested:
opts.ReviewRequestedID = ctx.Doer.ID
+ case issues_model.FilterModeReviewed:
+ opts.ReviewedID = ctx.Doer.ID
}
// keyword holds the search term entered into the search field.
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl
index 2b1bea822f..23a8a1d0e1 100644
--- a/templates/repo/issue/list.tmpl
+++ b/templates/repo/issue/list.tmpl
@@ -171,10 +171,11 @@
<a class="{{if eq .ViewType "all"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.all_issues"}}</a>
<a class="{{if eq .ViewType "assigned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
- <a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
{{if .PageIsPullList}}
<a class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.review_requested"}}</a>
+ <a class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
{{end}}
+ <a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
</div>
</div>
{{end}}
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index fca9597446..d73fb56fbc 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -111,8 +111,9 @@
<a class="{{if eq .ViewType "all"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.all_issues"}}</a>
<a class="{{if eq .ViewType "assigned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
- <a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
<a class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.review_requested"}}</a>
+ <a class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
+ <a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
</div>
</div>
{{end}}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index de774deaed..0605937599 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -2367,6 +2367,12 @@
"in": "query"
},
{
+ "type": "boolean",
+ "description": "filter pulls reviewed by you, default is false",
+ "name": "reviewed",
+ "in": "query"
+ },
+ {
"type": "string",
"description": "filter by owner",
"name": "owner",
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index 049b6a1681..29023d921a 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -17,16 +17,20 @@
{{.locale.Tr "repo.issues.filter_type.created_by_you"}}
<strong class="ui right">{{CountFmt .IssueStats.CreateCount}}</strong>
</a>
- <a class="{{if eq .ViewType "mentioned"}}ui basic primary button{{end}} item" href="{{.Link}}?type=mentioned&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
- {{.locale.Tr "repo.issues.filter_type.mentioning_you"}}
- <strong class="ui right">{{CountFmt .IssueStats.MentionCount}}</strong>
- </a>
{{if .PageIsPulls}}
<a class="{{if eq .ViewType "review_requested"}}ui basic primary button{{end}} item" href="{{.Link}}?type=review_requested&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
{{.locale.Tr "repo.issues.filter_type.review_requested"}}
<strong class="ui right">{{CountFmt .IssueStats.ReviewRequestedCount}}</strong>
</a>
+ <a class="{{if eq .ViewType "reviewed_by"}}ui basic primary button{{end}} item" href="{{.Link}}?type=reviewed_by&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
+ {{.locale.Tr "repo.issues.filter_type.reviewed_by_you"}}
+ <strong class="ui right">{{CountFmt .IssueStats.ReviewedCount}}</strong>
+ </a>
{{end}}
+ <a class="{{if eq .ViewType "mentioned"}}ui basic primary button{{end}} item" href="{{.Link}}?type=mentioned&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
+ {{.locale.Tr "repo.issues.filter_type.mentioning_you"}}
+ <strong class="ui right">{{CountFmt .IssueStats.MentionCount}}</strong>
+ </a>
<div class="ui divider"></div>
<a class="{{if not $.RepoIDs}}ui basic primary button{{end}} repo name item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&q={{$.Keyword}}">
<span class="text truncate">All</span>