From e619384098419569e570796a57ee6af4948067ae Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 11 Dec 2024 14:33:24 +0800 Subject: Add label/author/assignee filters to the user/org home issue list (#32779) Replace #26661, fix #25979 Not perfect, but usable and much better than before. Since it is quite complex, I am not quite sure whether there would be any regression, if any, I will fix in first time. I have tested the related pages many times: issue list, milestone issue list, project view, user issue list, org issue list. --- models/db/search.go | 12 +++++++----- models/issues/issue_search.go | 39 +++++++++++++++++++++++---------------- models/issues/issue_stats.go | 10 ++-------- models/issues/issue_test.go | 3 ++- 4 files changed, 34 insertions(+), 30 deletions(-) (limited to 'models') diff --git a/models/db/search.go b/models/db/search.go index 37565f45e1..e0a1b6bde9 100644 --- a/models/db/search.go +++ b/models/db/search.go @@ -26,8 +26,10 @@ const ( SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" ) -const ( - // Which means a condition to filter the records which don't match any id. - // It's different from zero which means the condition could be ignored. - NoConditionID = -1 -) +// NoConditionID means a condition to filter the records which don't match any id. +// eg: "milestone_id=-1" means "find the items without any milestone. +const NoConditionID int64 = -1 + +// NonExistingID means a condition to match no result (eg: a non-existing user) +// It doesn't use -1 or -2 because they are used as builtin users. +const NonExistingID int64 = -1000000 diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 5948a67d4e..f1cd125d49 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -27,8 +27,8 @@ type IssuesOptions struct { //nolint RepoIDs []int64 // overwrites RepoCond if the length is not 0 AllPublic bool // include also all public repositories RepoCond builder.Cond - AssigneeID int64 - PosterID int64 + AssigneeID optional.Option[int64] + PosterID optional.Option[int64] MentionedID int64 ReviewRequestedID int64 ReviewedID int64 @@ -231,15 +231,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) { sess.And("issue.is_closed=?", opts.IsClosed.Value()) } - if opts.AssigneeID > 0 { - applyAssigneeCondition(sess, opts.AssigneeID) - } else if opts.AssigneeID == db.NoConditionID { - sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)") - } - - if opts.PosterID > 0 { - applyPosterCondition(sess, opts.PosterID) - } + applyAssigneeCondition(sess, opts.AssigneeID) + applyPosterCondition(sess, opts.PosterID) if opts.MentionedID > 0 { applyMentionedCondition(sess, opts.MentionedID) @@ -359,13 +352,27 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati return cond } -func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) { - sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). - And("issue_assignees.assignee_id = ?", assigneeID) +func applyAssigneeCondition(sess *xorm.Session, assigneeID optional.Option[int64]) { + // old logic: 0 is also treated as "not filtering assignee", because the "assignee" was read as FormInt64 + if !assigneeID.Has() || assigneeID.Value() == 0 { + return + } + if assigneeID.Value() == db.NoConditionID { + sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)") + } else { + sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). + And("issue_assignees.assignee_id = ?", assigneeID.Value()) + } } -func applyPosterCondition(sess *xorm.Session, posterID int64) { - sess.And("issue.poster_id=?", posterID) +func applyPosterCondition(sess *xorm.Session, posterID optional.Option[int64]) { + if !posterID.Has() { + return + } + // poster doesn't need to support db.NoConditionID(-1), so just use the value as-is + if posterID.Has() { + sess.And("issue.poster_id=?", posterID.Value()) + } } func applyMentionedCondition(sess *xorm.Session, mentionedID int64) { diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 39326616f8..9ef9347a16 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -151,15 +151,9 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6 applyProjectCondition(sess, opts) - if opts.AssigneeID > 0 { - applyAssigneeCondition(sess, opts.AssigneeID) - } else if opts.AssigneeID == db.NoConditionID { - sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)") - } + applyAssigneeCondition(sess, opts.AssigneeID) - if opts.PosterID > 0 { - applyPosterCondition(sess, opts.PosterID) - } + applyPosterCondition(sess, opts.PosterID) if opts.MentionedID > 0 { applyMentionedCondition(sess, opts.MentionedID) diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 1bbc0eee56..548f137f39 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -16,6 +16,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -155,7 +156,7 @@ func TestIssues(t *testing.T) { }{ { issues_model.IssuesOptions{ - AssigneeID: 1, + AssigneeID: optional.Some(int64(1)), SortType: "oldest", }, []int64{1, 6}, -- cgit v1.2.3