-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Enhance gh issue view to conditionally handle v1 projects being available #9530
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
Changes from all commits
0b4b99c
71f951d
d1b0817
17d01d4
cca04cf
ef60f0f
4ef4fd3
6170466
a7a4859
3cc291b
eebe25e
b9b6e62
62f1ebf
787cdba
e35ba83
bf9c8fb
5f9d4dc
e6abdf8
e17c217
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| package view | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "slices" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/cli/cli/v2/api" | ||
| prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared" | ||
| "github.com/cli/cli/v2/pkg/iostreams" | ||
| ) | ||
|
|
||
| type PresentationIssue struct { | ||
| Title string | ||
| Number int | ||
| CreatedAt time.Time | ||
| Comments api.Comments | ||
| Author string | ||
| State string | ||
| StateReason string | ||
| Reactions string | ||
| AssigneesList string | ||
| LabelsList string | ||
| ProjectsList string | ||
| MilestoneTitle string | ||
| Body string | ||
| URL string | ||
| } | ||
|
|
||
| // Creates a new PresentationIssue from an api.Issue. | ||
| // The PresentationIssue is what the issue printers need to display an | ||
| // issue to the user. | ||
| func MapApiIssueToPresentationIssue(issue *api.Issue, colorScheme *iostreams.ColorScheme) (PresentationIssue, error) { | ||
| presentationIssue := PresentationIssue{ | ||
| Title: issue.Title, | ||
| Number: issue.Number, | ||
| CreatedAt: issue.CreatedAt, | ||
| Comments: issue.Comments, | ||
| Author: issue.Author.Login, | ||
| State: issue.State, | ||
| StateReason: issue.StateReason, | ||
| Reactions: prShared.ReactionGroupList(issue.ReactionGroups), | ||
| AssigneesList: stringifyAssignees(issue.Assignees), | ||
| LabelsList: stringifyAndColorizeLabels(sortAlphabeticallyIgnoreCase(issue.Labels), colorScheme), | ||
| ProjectsList: stringifyProjects(issue.ProjectCards, issue.ProjectItems), | ||
| Body: issue.Body, | ||
| URL: issue.URL, | ||
| } | ||
|
|
||
| if issue.Milestone != nil { | ||
| presentationIssue.MilestoneTitle = issue.Milestone.Title | ||
| } | ||
|
|
||
| return presentationIssue, nil | ||
| } | ||
|
|
||
| func stringifyProjects(projectCards api.ProjectCards, projectItems api.ProjectItems) string { | ||
| if len(projectCards.Nodes) == 0 && len(projectItems.Nodes) == 0 { | ||
| return "" | ||
| } | ||
|
|
||
| projectNames := make([]string, len(projectCards.Nodes)+len(projectItems.Nodes)) | ||
| for i, project := range projectCards.Nodes { | ||
| colName := project.Column.Name | ||
| if colName == "" { | ||
| colName = "Awaiting triage" | ||
| } | ||
| projectNames[i] = fmt.Sprintf("%s (%s)", project.Project.Name, colName) | ||
| } | ||
|
|
||
| for i, project := range projectItems.Nodes { | ||
| statusName := project.Status.Name | ||
| if statusName == "" { | ||
| statusName = "Backlog" | ||
| } | ||
| projectNames[i+len(projectCards.Nodes)] = fmt.Sprintf("%s (%s)", project.Project.Title, statusName) | ||
| } | ||
|
|
||
| list := strings.Join(projectNames, ", ") | ||
| if projectCards.TotalCount > len(projectCards.Nodes) { | ||
| list += ", …" | ||
| } | ||
| return list | ||
| } | ||
|
|
||
| func stringifyAssignees(issueAssignees api.Assignees) string { | ||
| if len(issueAssignees.Nodes) == 0 { | ||
| return "" | ||
| } | ||
|
|
||
| AssigneeNames := make([]string, 0, len(issueAssignees.Nodes)) | ||
| for _, assignee := range issueAssignees.Nodes { | ||
| AssigneeNames = append(AssigneeNames, assignee.Login) | ||
| } | ||
|
|
||
| list := strings.Join(AssigneeNames, ", ") | ||
| if issueAssignees.TotalCount > len(issueAssignees.Nodes) { | ||
| list += ", …" | ||
| } | ||
| return list | ||
| } | ||
|
|
||
| func stringifyAndColorizeLabels(issueLabels api.Labels, colorScheme *iostreams.ColorScheme) string { | ||
| labelNames := make([]string, len(issueLabels.Nodes)) | ||
| for j, label := range issueLabels.Nodes { | ||
| if colorScheme == nil { | ||
| labelNames[j] = label.Name | ||
| } else { | ||
| labelNames[j] = colorScheme.HexToRGB(label.Color, label.Name) | ||
| } | ||
| } | ||
|
|
||
| return strings.Join(labelNames, ", ") | ||
| } | ||
|
|
||
| func sortAlphabeticallyIgnoreCase(l api.Labels) api.Labels { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this function signature is a bit confusing given the implementation. This indicates that I give it a labels and it gives me back sorted labels. This is true but it's kind of hiding the fact that the slice of Nodes is being mutated in place. This is also surprising in the way we use it because it's mutating the original api.Issue as well. |
||
| slices.SortStableFunc(l.Nodes, func(a, b api.IssueLabel) int { | ||
| return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) | ||
| }) | ||
| return l | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a bit leaky that we've gone to all this work the separate our
PresentationIssuefrom ourapidomain and we're holding on toapi.Commentsin here. I'd need to take some time to investigate a bit more before offering any kind of suggestion though.