Skip to content

Commit 6af4d03

Browse files
pabsan-0seflue
andauthored
Feat/task status icons (#64)
* boilerplate ported and adjusted off PR #38 * fix: proper padding for fields with no statusIcon * @seflue review: column alignment compacted from if-else to max() && typo Co-authored-by: Sebastian Flügge <[email protected]> * add typeIcon and statusIcon info to docs --------- Co-authored-by: Sebastian Flügge <[email protected]>
1 parent 5516244 commit 6af4d03

4 files changed

Lines changed: 84 additions & 22 deletions

File tree

docs/Config.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ gui:
5151
- status
5252
- summary
5353
selectCreatedIssue: true
54+
typeIcons:
55+
Bug: "🐞"
56+
Story: "📖"
57+
Sub-task: "📎"
58+
statusIcons:
59+
To do: "📋"
60+
On hold: "⏸️"
61+
Future: "🔜"
62+
5463
keybinding:
5564
universal:
5665
quit: q
@@ -184,6 +193,26 @@ gui:
184193
selectCreatedIssue: true
185194
```
186195

196+
`typeIcons` will have issue `type` names replaced by the emojis you set. Mappings are case-sensitive, and support not only emojis but also plaintext. Enable the field `type` under `issueListFields` to profit from this option.
197+
198+
```
199+
gui:
200+
typeIcons:
201+
Bug: "🐞"
202+
Story: "📖"
203+
Sub-task: "📎"
204+
```
205+
206+
`statusIcons` will have issue `status` indicators replaced by the emojis you set. Default indicators may be shared for similar states (e.g. To do / Future), so one may get more granularity from this option. Mappings are case-sensitive, and support not only emojis but also plaintext. Enable the field `status` under `issueListFields` to profit from this option.
207+
208+
```
209+
gui:
210+
statusIcons:
211+
To do: "📋"
212+
On hold: "⏸️"
213+
Future: "🔜"
214+
```
215+
187216
### Issue list fields
188217
189218
Controls which columns appear in the issue list. Available fields.

pkg/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ type GUIConfig struct {
189189
PrefillFromTab *bool `yaml:"prefillFromTab"`
190190
SelectCreatedIssue *bool `yaml:"selectCreatedIssue"`
191191
TypeIcons map[string]string `yaml:"typeIcons"`
192+
StatusIcons map[string]string `yaml:"statusIcons"`
192193
}
193194

194195
// ShouldPrefillFromTab returns true when the creation form should prefill from tab JQL

pkg/tui/app.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ func NewAppWithAuth(cfg *config.Config, client jira.ClientInterface, authMethod
229229
if len(cfg.GUI.TypeIcons) > 0 {
230230
issuesList.SetTypeIcons(cfg.GUI.TypeIcons)
231231
}
232+
if len(cfg.GUI.StatusIcons) > 0 {
233+
issuesList.SetStatusIcons(cfg.GUI.StatusIcons)
234+
}
232235
issuesList.SetTabs(cfg.IssueTabs)
233236
issuesList.SetFocused(true)
234237
issuesList.SetUserEmail(cfg.Jira.Email)

pkg/tui/views/issues.go

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,22 @@ const statusOpen = "○"
2727

2828
type IssuesList struct {
2929
components.ListBase
30-
issues []jira.Issue
31-
allIssues []jira.Issue
32-
filter string
33-
tabs []config.IssueTabConfig
34-
tab int
35-
tabCache map[int][]jira.Issue
36-
userEmail string
37-
keyColWidth int
38-
fields []string
39-
theme *theme.Theme
40-
typeIcons map[string]string
41-
typeIconCols int
42-
jqlQuery string
43-
jqlTabIdx int
30+
issues []jira.Issue
31+
allIssues []jira.Issue
32+
filter string
33+
tabs []config.IssueTabConfig
34+
tab int
35+
tabCache map[int][]jira.Issue
36+
userEmail string
37+
keyColWidth int
38+
fields []string
39+
theme *theme.Theme
40+
typeIcons map[string]string
41+
typeIconCols int
42+
statusIcons map[string]string
43+
statusIconCols int
44+
jqlQuery string
45+
jqlTabIdx int
4446
}
4547

4648
func NewIssuesList() *IssuesList {
@@ -58,6 +60,16 @@ func (m *IssuesList) SetTypeIcons(icons map[string]string) {
5860
}
5961
m.typeIconCols = max
6062
}
63+
func (m *IssuesList) SetStatusIcons(icons map[string]string) {
64+
m.statusIcons = icons
65+
max := 0
66+
for _, icon := range icons {
67+
if w := lipgloss.Width(icon); w > max {
68+
max = w
69+
}
70+
}
71+
m.statusIconCols = max
72+
}
6173
func (m *IssuesList) SetTabs(tabs []config.IssueTabConfig) { m.tabs = tabs }
6274
func (m *IssuesList) SetUserEmail(email string) { m.userEmail = email }
6375
func (m *IssuesList) ActiveTab() config.IssueTabConfig {
@@ -432,7 +444,8 @@ func (m *IssuesList) renderIssueRow(issue jira.Issue, width int, selected bool)
432444
fields = []string{"key", fieldStatus, "summary"}
433445
}
434446

435-
icon := typeIcon(m.typeIcons, issue.IssueType)
447+
currTypeIcon := typeIcon(m.typeIcons, issue.IssueType)
448+
currStatusIcon := statusIcon(m.statusIcons, issue.Status)
436449

437450
fixedWidth := 1
438451
if len(fields) > 1 {
@@ -443,13 +456,13 @@ func (m *IssuesList) renderIssueRow(issue jira.Issue, width int, selected bool)
443456
case "key":
444457
fixedWidth += m.keyColWidth
445458
case fieldStatus:
446-
fixedWidth += 1
459+
fixedWidth += max(1, m.statusIconCols)
447460
case "priority":
448461
fixedWidth += 8
449462
case "assignee":
450463
fixedWidth += 12
451464
case "type":
452-
if icon != "" {
465+
if currTypeIcon != "" {
453466
fixedWidth += m.typeIconCols
454467
} else {
455468
fixedWidth += 10
@@ -469,10 +482,14 @@ func (m *IssuesList) renderIssueRow(issue jira.Issue, width int, selected bool)
469482
case "summary":
470483
parts = append(parts, padRight(components.TruncateEnd(issue.Summary, summaryWidth), summaryWidth))
471484
case fieldStatus:
472-
if selected {
473-
parts = append(parts, statusEmojiPlain(issue.Status))
485+
if currStatusIcon != "" {
486+
parts = append(parts, padRight(currStatusIcon, m.statusIconCols))
474487
} else {
475-
parts = append(parts, statusEmoji(issue.Status))
488+
if selected {
489+
parts = append(parts, padRight(statusEmojiPlain(issue.Status), m.statusIconCols))
490+
} else {
491+
parts = append(parts, padRight(statusEmoji(issue.Status), m.statusIconCols))
492+
}
476493
}
477494
case "priority":
478495
name := ""
@@ -487,8 +504,8 @@ func (m *IssuesList) renderIssueRow(issue jira.Issue, width int, selected bool)
487504
}
488505
parts = append(parts, padRight(components.TruncateEnd(name, 12), 12))
489506
case "type":
490-
if icon != "" {
491-
parts = append(parts, padRight(icon, m.typeIconCols))
507+
if currTypeIcon != "" {
508+
parts = append(parts, padRight(currTypeIcon, m.typeIconCols))
492509
} else {
493510
name := ""
494511
if issue.IssueType != nil {
@@ -550,6 +567,18 @@ func typeIcon(icons map[string]string, issueType *jira.IssueType) string {
550567
return icon
551568
}
552569

570+
// statusIcon returns the configured icon for the given status, or empty string if none.
571+
func statusIcon(icons map[string]string, status *jira.Status) string {
572+
if status == nil {
573+
return ""
574+
}
575+
icon, ok := icons[status.Name]
576+
if !ok || icon == "" {
577+
return ""
578+
}
579+
return icon
580+
}
581+
553582
// statusEmojiPlain returns uncolored status char for selected rows
554583
func statusEmojiPlain(status *jira.Status) string {
555584
if status == nil {

0 commit comments

Comments
 (0)