Skip to content

Commit 3f0651d

Browse files
authored
Improve milestone filter on issues page (#22423)
Now we have `All milestones`, `No milestones`, `Open milestones` and `Closed milestones`. Fix #11924 Fix #22411 <img width="1166" alt="image" src="https://user-images.githubusercontent.com/81045/212243375-95eea035-a972-44b8-8088-53db614cb07e.png">
1 parent e375037 commit 3f0651d

File tree

8 files changed

+99
-91
lines changed

8 files changed

+99
-91
lines changed

models/db/search.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ const (
3131
const (
3232
// Which means a condition to filter the records which don't match any id.
3333
// It's different from zero which means the condition could be ignored.
34-
NoneID = -1
34+
NoConditionID = -1
3535
)

models/issues/issue.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,9 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
12731273
applySubscribedCondition(sess, opts.SubscriberID)
12741274
}
12751275

1276-
if len(opts.MilestoneIDs) > 0 {
1276+
if len(opts.MilestoneIDs) == 1 && opts.MilestoneIDs[0] == db.NoConditionID {
1277+
sess.And("issue.milestone_id = 0")
1278+
} else if len(opts.MilestoneIDs) > 0 {
12771279
sess.In("issue.milestone_id", opts.MilestoneIDs)
12781280
}
12791281

@@ -1287,7 +1289,7 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
12871289
if opts.ProjectID > 0 {
12881290
sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id").
12891291
And("project_issue.project_id=?", opts.ProjectID)
1290-
} else if opts.ProjectID == db.NoneID { // show those that are in no project
1292+
} else if opts.ProjectID == db.NoConditionID { // show those that are in no project
12911293
sess.And(builder.NotIn("issue.id", builder.Select("issue_id").From("project_issue")))
12921294
}
12931295

@@ -1721,6 +1723,8 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
17211723

17221724
if opts.MilestoneID > 0 {
17231725
sess.And("issue.milestone_id = ?", opts.MilestoneID)
1726+
} else if opts.MilestoneID == db.NoConditionID {
1727+
sess.And("issue.milestone_id = 0")
17241728
}
17251729

17261730
if opts.ProjectID > 0 {

options/locale/locale_en-US.ini

+4-1
Original file line numberDiff line numberDiff line change
@@ -1352,7 +1352,10 @@ issues.filter_label = Label
13521352
issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> to exclude labels`
13531353
issues.filter_label_no_select = All labels
13541354
issues.filter_milestone = Milestone
1355-
issues.filter_milestone_no_select = All milestones
1355+
issues.filter_milestone_all = All milestones
1356+
issues.filter_milestone_none = No milestones
1357+
issues.filter_milestone_open = Open milestones
1358+
issues.filter_milestone_closed = Closed milestones
13561359
issues.filter_project = Project
13571360
issues.filter_project_all = All projects
13581361
issues.filter_project_none = No project

routers/web/repo/issue.go

+24-7
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
241241
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5)
242242

243243
var mileIDs []int64
244-
if milestoneID > 0 {
244+
if milestoneID > 0 || milestoneID == db.NoConditionID { // -1 to get those issues which have no any milestone assigned
245245
mileIDs = []int64{milestoneID}
246246
}
247247

@@ -438,20 +438,37 @@ func Issues(ctx *context.Context) {
438438
return
439439
}
440440

441-
var err error
441+
renderMilestones(ctx)
442+
if ctx.Written() {
443+
return
444+
}
445+
446+
ctx.Data["CanWriteIssuesOrPulls"] = ctx.Repo.CanWriteIssuesOrPulls(isPullList)
447+
448+
ctx.HTML(http.StatusOK, tplIssues)
449+
}
450+
451+
func renderMilestones(ctx *context.Context) {
442452
// Get milestones
443-
ctx.Data["Milestones"], _, err = issues_model.GetMilestones(issues_model.GetMilestonesOption{
453+
milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
444454
RepoID: ctx.Repo.Repository.ID,
445-
State: api.StateType(ctx.FormString("state")),
455+
State: api.StateAll,
446456
})
447457
if err != nil {
448458
ctx.ServerError("GetAllRepoMilestones", err)
449459
return
450460
}
451461

452-
ctx.Data["CanWriteIssuesOrPulls"] = ctx.Repo.CanWriteIssuesOrPulls(isPullList)
453-
454-
ctx.HTML(http.StatusOK, tplIssues)
462+
openMilestones, closedMilestones := issues_model.MilestoneList{}, issues_model.MilestoneList{}
463+
for _, milestone := range milestones {
464+
if milestone.IsClosed {
465+
closedMilestones = append(closedMilestones, milestone)
466+
} else {
467+
openMilestones = append(openMilestones, milestone)
468+
}
469+
}
470+
ctx.Data["OpenMilestones"] = openMilestones
471+
ctx.Data["ClosedMilestones"] = closedMilestones
455472
}
456473

457474
// RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository

templates/repo/issue/list.tmpl

+23-4
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
</div>
5454

5555
<!-- Milestone -->
56-
<div class="ui {{if not .Milestones}}disabled{{end}} dropdown jump item">
56+
<div class="ui {{if not (or .OpenMilestones .ClosedMilestones)}}disabled{{end}} dropdown jump item">
5757
<span class="text">
5858
{{.locale.Tr "repo.issues.filter_milestone"}}
5959
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
@@ -63,9 +63,28 @@
6363
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
6464
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestone"}}">
6565
</div>
66-
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_milestone_no_select"}}</a>
67-
{{range .Milestones}}
68-
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.Name}}</a>
66+
<div class="divider"></div>
67+
<a class="{{if not $.MilestoneID}}active selected {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=0&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_milestone_all"}}</a>
68+
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID -1}}active selected {{end}}{{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone=-1&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_milestone_none"}}</a>
69+
{{if .OpenMilestones}}
70+
<div class="divider"></div>
71+
<div class="header">{{.locale.Tr "repo.issues.filter_milestone_open"}}</div>
72+
{{range .OpenMilestones}}
73+
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">
74+
{{svg "octicon-milestone" 16 "mr-2"}}
75+
{{.Name}}
76+
</a>
77+
{{end}}
78+
{{end}}
79+
{{if .ClosedMilestones}}
80+
<div class="divider"></div>
81+
<div class="header">{{.locale.Tr "repo.issues.filter_milestone_closed"}}</div>
82+
{{range .ClosedMilestones}}
83+
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected {{end}}{{end}}item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">
84+
{{svg "octicon-milestone" 16 "mr-2"}}
85+
{{.Name}}
86+
</a>
87+
{{end}}
6988
{{end}}
7089
</div>
7190
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_milestone_title"}}</div>
2+
{{if or .OpenMilestones .ClosedMilestones}}
3+
<div class="ui icon search input">
4+
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
5+
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestones"}}">
6+
</div>
7+
<div class="divider"></div>
8+
{{end}}
9+
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_milestone"}}</div>
10+
{{if and (not .OpenMilestones) (not .ClosedMilestones)}}
11+
<div class="header" style="text-transform: none;font-size:14px;">
12+
{{.locale.Tr "repo.issues.new.no_items"}}
13+
</div>
14+
{{else}}
15+
{{if .OpenMilestones}}
16+
<div class="divider"></div>
17+
<div class="header">
18+
{{.locale.Tr "repo.issues.new.open_milestone"}}
19+
</div>
20+
{{range .OpenMilestones}}
21+
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
22+
{{svg "octicon-milestone" 16 "gt-mr-2"}}
23+
{{.Name}}
24+
</a>
25+
{{end}}
26+
{{end}}
27+
{{if .ClosedMilestones}}
28+
<div class="divider"></div>
29+
<div class="header">
30+
{{.locale.Tr "repo.issues.new.closed_milestone"}}
31+
</div>
32+
{{range .ClosedMilestones}}
33+
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
34+
{{svg "octicon-milestone" 16 "gt-mr-2"}}
35+
{{.Name}}
36+
</a>
37+
{{end}}
38+
{{end}}
39+
{{end}}

templates/repo/issue/new_form.tmpl

+1-38
Original file line numberDiff line numberDiff line change
@@ -72,44 +72,7 @@
7272
{{end}}
7373
</span>
7474
<div class="menu">
75-
<div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_milestone_title"}}</div>
76-
{{if or .OpenMilestones .ClosedMilestones}}
77-
<div class="ui icon search input">
78-
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
79-
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestones"}}">
80-
</div>
81-
{{end}}
82-
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_milestone"}}</div>
83-
{{if and (not .OpenMilestones) (not .ClosedMilestones)}}
84-
<div class="header" style="text-transform: none;font-size:14px;">
85-
{{.locale.Tr "repo.issues.new.no_items"}}
86-
</div>
87-
{{else}}
88-
{{if .OpenMilestones}}
89-
<div class="divider"></div>
90-
<div class="header">
91-
{{.locale.Tr "repo.issues.new.open_milestone"}}
92-
</div>
93-
{{range .OpenMilestones}}
94-
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
95-
{{svg "octicon-milestone" 16 "gt-mr-2"}}
96-
{{.Name}}
97-
</a>
98-
{{end}}
99-
{{end}}
100-
{{if .ClosedMilestones}}
101-
<div class="divider"></div>
102-
<div class="header">
103-
{{.locale.Tr "repo.issues.new.closed_milestone"}}
104-
</div>
105-
{{range .ClosedMilestones}}
106-
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
107-
{{svg "octicon-milestone" 16 "gt-mr-2"}}
108-
{{.Name}}
109-
</a>
110-
{{end}}
111-
{{end}}
112-
{{end}}
75+
{{template "repo/issue/milestone/select_menu" .}}
11376
</div>
11477
</div>
11578
<div class="ui select-milestone list">

templates/repo/issue/view_content/sidebar.tmpl

+1-38
Original file line numberDiff line numberDiff line change
@@ -118,44 +118,7 @@
118118
{{end}}
119119
</a>
120120
<div class="menu" data-action="update" data-issue-id="{{$.Issue.ID}}" data-update-url="{{$.RepoLink}}/issues/milestone">
121-
<div class="header" style="text-transform: none;font-size:16px;">{{.locale.Tr "repo.issues.new.add_milestone_title"}}</div>
122-
{{if or .OpenMilestones .ClosedMilestones}}
123-
<div class="ui icon search input">
124-
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
125-
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestones"}}">
126-
</div>
127-
{{end}}
128-
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_milestone"}}</div>
129-
{{if and (not .OpenMilestones) (not .ClosedMilestones)}}
130-
<div class="header" style="text-transform: none;font-size:14px;">
131-
{{.locale.Tr "repo.issues.new.no_items"}}
132-
</div>
133-
{{else}}
134-
{{if .OpenMilestones}}
135-
<div class="divider"></div>
136-
<div class="header">
137-
{{.locale.Tr "repo.issues.new.open_milestone"}}
138-
</div>
139-
{{range .OpenMilestones}}
140-
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
141-
{{svg "octicon-milestone" 16 "gt-mr-2"}}
142-
{{.Name}}
143-
</a>
144-
{{end}}
145-
{{end}}
146-
{{if .ClosedMilestones}}
147-
<div class="divider"></div>
148-
<div class="header">
149-
{{.locale.Tr "repo.issues.new.closed_milestone"}}
150-
</div>
151-
{{range .ClosedMilestones}}
152-
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}">
153-
{{svg "octicon-milestone" 16 "gt-mr-2"}}
154-
{{.Name}}
155-
</a>
156-
{{end}}
157-
{{end}}
158-
{{end}}
121+
{{template "repo/issue/milestone/select_menu" .}}
159122
</div>
160123
</div>
161124
<div class="ui select-milestone list">

0 commit comments

Comments
 (0)