From 2c3da59e275b69ebf984bb70954f42a7bcb0b49d Mon Sep 17 00:00:00 2001 From: Gwyneth Morgan Date: Mon, 15 Jan 2024 07:07:22 -0800 Subject: [PATCH] Add ability to see open and closed issues at the same time (#28757) By clicking the currently active "Open" or "Closed" filter button in the issue list, the user can toggle that filter off in order to see all issues regardless of state. The URL "state" parameter will be set to "all" and the "Open"/"Closed" button will not show as active. --- models/issues/tracked_time.go | 12 ++++++---- models/issues/tracked_time_test.go | 9 ++++++-- routers/web/repo/issue.go | 36 +++++++++++++++++++++-------- templates/repo/issue/openclose.tmpl | 4 ++-- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index 884a445d2..91c4832e4 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -340,7 +340,7 @@ func GetTrackedTimeByID(ctx context.Context, id int64) (*TrackedTime, error) { } // GetIssueTotalTrackedTime returns the total tracked time for issues by given conditions. -func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed bool) (int64, error) { +func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool) (int64, error) { if len(opts.IssueIDs) <= MaxQueryParameters { return getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs) } @@ -363,7 +363,7 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed return accum, nil } -func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed bool, issueIDs []int64) (int64, error) { +func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool, issueIDs []int64) (int64, error) { sumSession := func(opts *IssuesOptions, issueIDs []int64) *xorm.Session { sess := db.GetEngine(ctx). Table("tracked_time"). @@ -377,7 +377,9 @@ func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isC Time int64 } - return sumSession(opts, issueIDs). - And("issue.is_closed = ?", isClosed). - SumInt(new(trackedTime), "tracked_time.time") + session := sumSession(opts, issueIDs) + if !isClosed.IsNone() { + session = session.And("issue.is_closed = ?", isClosed.IsTrue()) + } + return session.SumInt(new(trackedTime), "tracked_time.time") } diff --git a/models/issues/tracked_time_test.go b/models/issues/tracked_time_test.go index 2774234e7..9beb862ff 100644 --- a/models/issues/tracked_time_test.go +++ b/models/issues/tracked_time_test.go @@ -11,6 +11,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -119,11 +120,15 @@ func TestTotalTimesForEachUser(t *testing.T) { func TestGetIssueTotalTrackedTime(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, false) + ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolFalse) assert.NoError(t, err) assert.EqualValues(t, 3682, ttt) - ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, true) + ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolTrue) assert.NoError(t, err) assert.EqualValues(t, 0, ttt) + + ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolNone) + assert.NoError(t, err) + assert.EqualValues(t, 3682, ttt) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 0d660e3b8..c8c9924a9 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -237,10 +237,18 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } } - isShowClosed := ctx.FormString("state") == "closed" - // if open issues are zero and close don't, use closed as default + var isShowClosed util.OptionalBool + switch ctx.FormString("state") { + case "closed": + isShowClosed = util.OptionalBoolTrue + case "all": + isShowClosed = util.OptionalBoolNone + default: + isShowClosed = util.OptionalBoolFalse + } + // if there are closed issues and no open issues, default to showing all issues if len(ctx.FormString("state")) == 0 && issueStats.OpenCount == 0 && issueStats.ClosedCount != 0 { - isShowClosed = true + isShowClosed = util.OptionalBoolNone } if repo.IsTimetrackerEnabled(ctx) { @@ -260,10 +268,13 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } var total int - if !isShowClosed { - total = int(issueStats.OpenCount) - } else { + switch isShowClosed { + case util.OptionalBoolTrue: total = int(issueStats.ClosedCount) + case util.OptionalBoolNone: + total = int(issueStats.OpenCount + issueStats.ClosedCount) + default: + total = int(issueStats.OpenCount) } pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) @@ -282,7 +293,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ReviewedID: reviewedID, MilestoneIDs: mileIDs, ProjectID: projectID, - IsClosed: util.OptionalBoolOf(isShowClosed), + IsClosed: isShowClosed, IsPull: isPullOption, LabelIDs: labelIDs, SortType: sortType, @@ -428,6 +439,9 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ctx.Data["OpenCount"] = issueStats.OpenCount ctx.Data["ClosedCount"] = issueStats.ClosedCount linkStr := "%s?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&archived=%t" + ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr, ctx.Link, + url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "all", url.QueryEscape(selectLabels), + mentionedID, projectID, assigneeID, posterID, archived) ctx.Data["OpenLink"] = fmt.Sprintf(linkStr, ctx.Link, url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "open", url.QueryEscape(selectLabels), mentionedID, projectID, assigneeID, posterID, archived) @@ -442,11 +456,13 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ctx.Data["ProjectID"] = projectID ctx.Data["AssigneeID"] = assigneeID ctx.Data["PosterID"] = posterID - ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["Keyword"] = keyword - if isShowClosed { + switch isShowClosed { + case util.OptionalBoolTrue: ctx.Data["State"] = "closed" - } else { + case util.OptionalBoolNone: + ctx.Data["State"] = "all" + default: ctx.Data["State"] = "open" } ctx.Data["ShowArchivedLabels"] = archived diff --git a/templates/repo/issue/openclose.tmpl b/templates/repo/issue/openclose.tmpl index ff5ec3c5a..38848c51a 100644 --- a/templates/repo/issue/openclose.tmpl +++ b/templates/repo/issue/openclose.tmpl @@ -1,5 +1,5 @@