Skip to content

Display badge on blocked & blocking issues #35004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions models/issues/issue_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,74 @@ func (issues IssueList) LoadDiscussComments(ctx context.Context) error {
return issues.loadComments(ctx, builder.Eq{"comment.type": CommentTypeComment})
}

// GetBlockedByCounts returns a map of issue ID to number of open issues that are blocking it
func (issues IssueList) GetBlockedByCount(ctx context.Context) (map[int64]int64, error) {
type BlockedByCount struct {
IssueID int64
Count int64
}

bCounts := make([]*BlockedByCount, len(issues))
ids := make([]int64, len(issues))
for i, issue := range issues {
ids[i] = issue.ID
}

sess := db.GetEngine(ctx).In("issue_id", ids)
err := sess.Select("issue_id, count(issue_dependency.id) as `count`").
Join("INNER", "issue", "issue.id = issue_dependency.dependency_id").
Where("is_closed = ?", false).
GroupBy("issue_id").
OrderBy("issue_id").
Table("issue_dependency").
Find(&bCounts)
if err != nil {
return nil, err
}

blockedByCountMap := make(map[int64]int64, len(issues))
for _, c := range bCounts {
if c != nil {
blockedByCountMap[c.IssueID] = c.Count
}
}

return blockedByCountMap, nil
}

// GetBlockingCounts returns a map of issue ID to number of issues that are blocked by it
func (issues IssueList) GetBlockingCount(ctx context.Context) (map[int64]int64, error) {
type BlockingCount struct {
IssueID int64
Count int64
}

bCounts := make([]*BlockingCount, 0, len(issues))
ids := make([]int64, len(issues))
for i, issue := range issues {
ids[i] = issue.ID
}

sess := db.GetEngine(ctx).In("dependency_id", ids)
err := sess.Select("dependency_id as `issue_id`, count(id) as `count`").
GroupBy("dependency_id").
OrderBy("dependency_id").
Table("issue_dependency").
Find(&bCounts)
if err != nil {
return nil, err
}

blockingCountMap := make(map[int64]int64, len(issues))
for _, c := range bCounts {
if c != nil {
blockingCountMap[c.IssueID] = c.Count
}
}

return blockingCountMap, nil
}

// GetApprovalCounts returns a map of issue ID to slice of approval counts
// FIXME: only returns official counts due to double counting of non-official approvals
func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*ReviewCount, error) {
Expand Down
4 changes: 4 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,10 @@ issues.dependency.add_error_dep_not_exist = Dependency does not exist.
issues.dependency.add_error_dep_exists = Dependency already exists.
issues.dependency.add_error_cannot_create_circular = You cannot create a dependency with two issues blocking each other.
issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository.
issues.dependency.blocking_count_1 = "This issue is blocking %d other issue."
issues.dependency.blocking_count_n = "This issue is blocking %d other issues."
issues.dependency.blocked_by_count_1 = "This issue is blocked by %d issue."
issues.dependency.blocked_by_count_n = "This issue is blocked by %d issues."
issues.review.self.approval = You cannot approve your own pull request.
issues.review.self.rejection = You cannot request changes on your own pull request.
issues.review.approve = "approved these changes %s"
Expand Down
27 changes: 27 additions & 0 deletions routers/web/repo/issue_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,18 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
return
}

blockingCounts, err := issues.GetBlockingCount(ctx)
if err != nil {
ctx.ServerError("BlockingCounts", err)
return
}

blockedByCounts, err := issues.GetBlockedByCount(ctx)
if err != nil {
ctx.ServerError("BlockedByCounts", err)
return
}

if ctx.IsSigned {
if err := issues.LoadIsRead(ctx, ctx.Doer.ID); err != nil {
ctx.ServerError("LoadIsRead", err)
Expand Down Expand Up @@ -718,6 +730,21 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
return 0
}

ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
counts, ok := blockingCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
counts, ok := blockedByCounts[issueID]
if !ok {
return 0
}
return counts
}

retrieveProjectsForIssueList(ctx, repo)
if ctx.Written() {
return
Expand Down
27 changes: 27 additions & 0 deletions routers/web/user/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,33 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
}
return 0
}

blockingCounts, err := issues.GetBlockingCount(ctx)
if err != nil {
ctx.ServerError("BlockingCounts", err)
return
}

blockedByCounts, err := issues.GetBlockedByCount(ctx)
if err != nil {
ctx.ServerError("BlockedByCounts", err)
return
}
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
counts, ok := blockingCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
counts, ok := blockedByCounts[issueID]
if !ok {
return 0
}
return counts
}

ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["IssueStats"] = issueStats
Expand Down
26 changes: 26 additions & 0 deletions routers/web/user/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,32 @@ func NotificationSubscriptions(ctx *context.Context) {
return 0
}

blockingCounts, err := issues.GetBlockingCount(ctx)
if err != nil {
ctx.ServerError("BlockingCounts", err)
return
}

blockedByCounts, err := issues.GetBlockedByCount(ctx)
if err != nil {
ctx.ServerError("BlockedByCounts", err)
return
}
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
counts, ok := blockingCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
counts, ok := blockedByCounts[issueID]
if !ok {
return 0
}
return counts
}

ctx.Data["Status"] = 1
ctx.Data["Title"] = ctx.Tr("notification.subscriptions")

Expand Down
20 changes: 20 additions & 0 deletions templates/shared/issuelist.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<div id="issue-list" class="flex-list">
{{$approvalCounts := .ApprovalCounts}}
{{$blockedByCounts := .BlockedByCounts}}
{{$blockingCounts := .BlockingCounts}}
{{range .Issues}}
{{$blockedByCount := call $blockedByCounts .ID}}
{{$blockingCount := call $blockingCounts .ID}}
<div class="flex-item">

<div class="flex-item-leading">
Expand All @@ -22,6 +26,22 @@
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
{{end}}
{{end}}
{{if gt $blockedByCount 0}}
<div class="ui label label-blocking">
<span data-tooltip-content="{{ctx.Locale.TrN $blockedByCount "repo.issues.dependency.blocked_by_count_1" "repo.issues.dependency.blocked_by_count_n" $blockedByCount}}" class="text red flex-text-block">
{{svg "octicon-blocked" 16}}
{{$blockedByCount}}
</span>
</div>
{{end}}
{{if and (gt $blockingCount 0) (not .IsClosed)}}
<div class="ui label label-blocking">
<span data-tooltip-content="{{ctx.Locale.TrN $blockingCount "repo.issues.dependency.blocking_count_1" "repo.issues.dependency.blocking_count_n" $blockingCount}}" class="text red flex-text-block">
{{svg "octicon-report" 16}}
{{$blockingCount}}
</span>
</div>
{{end}}
<span class="labels-list">
{{range .Labels}}
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a>
Expand Down
7 changes: 7 additions & 0 deletions web_src/css/repo/issue-label.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,10 @@
top: 10px;
right: 5px;
}

.label-blocking {
border: 1px solid var(--color-secondary) !important;
background: none transparent !important;
margin-left: 1px;
margin-right: 1px;
}