Skip to content

Commit 6ed1996

Browse files
committed
feat: search using my jira filters functionality
1 parent d04bc70 commit 6ed1996

22 files changed

+829
-116
lines changed

README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,18 @@ Usage:
8383
Available Commands:
8484
[issueKey] Open a Jira issue directly from the CLI
8585
completion Generate the autocompletion script for the specified shell
86+
filters Search using Jira filters
8687
help Help about any command
8788
jql Search using custom JQL queries
8889
version Print the version number of fjira
8990
workspace Switch to a different workspace
9091
9192
Flags:
9293
-h, --help help for fjira
93-
-p, --project string Open project directly from cli
94+
-p, --project string Open a project directly from CLI
9495
9596
Additional help topics:
96-
fjira Open fuzzy-find for projects as a default action
97+
fjira Open a fuzzy finder for projects as a default action
9798
9899
Use "fjira [command] --help" for more information about a command.
99100
```
@@ -182,11 +183,24 @@ Fjira also offers a board-like view. After opening a project, press F4 to access
182183

183184
You can create and execute custom JQL queries with Fjira:
184185

186+
```shell
187+
fjira jql
188+
```
189+
185190
![Fjira Custom JQL](demo_custom_jql.png)
186191

192+
## My Jira Filters
193+
194+
You can search using your stored Jira Filters:
195+
196+
```shell
197+
fjira filters
198+
```
199+
187200
## Roadmap (TODO)
188201

189202
- Expand Documentation
203+
- Create&Delete Jira Filters
190204
- Support Additional Linux Package Managers (Apt, AUR, YUM)
191205
- Introduce More Jira Features
192206

cmd/fjira-cli/commands/filters.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package commands
2+
3+
import (
4+
"github.com/mk-5/fjira/internal/fjira"
5+
"github.com/mk-5/fjira/internal/workspaces"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func GetFiltersCmd() *cobra.Command {
10+
return &cobra.Command{
11+
Use: "filters",
12+
Short: "Search using Jira filters",
13+
Run: func(cmd *cobra.Command, args []string) {
14+
s := cmd.Context().Value(CtxWorkspaceSettings).(*workspaces.WorkspaceSettings)
15+
f := fjira.CreateNewFjira(s)
16+
defer f.Close()
17+
f.Run(&fjira.CliArgs{
18+
FiltersMode: true,
19+
})
20+
},
21+
}
22+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package commands
2+
3+
import (
4+
"context"
5+
"github.com/mk-5/fjira/internal/app"
6+
"github.com/mk-5/fjira/internal/workspaces"
7+
"github.com/stretchr/testify/assert"
8+
"testing"
9+
"time"
10+
)
11+
12+
func TestGetFiltersCmd(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
}{
16+
{"should create&execute FiltersCmd"},
17+
}
18+
for _, tt := range tests {
19+
t.Run(tt.name, func(t *testing.T) {
20+
// when
21+
cmd := GetFiltersCmd()
22+
23+
// then
24+
assert.NotNil(t, cmd)
25+
26+
// and when
27+
var err error
28+
go func() {
29+
err = cmd.ExecuteContext(context.WithValue(context.TODO(), CtxWorkspaceSettings, &workspaces.WorkspaceSettings{}))
30+
}() //nolint:errcheck
31+
for app.GetApp() == nil {
32+
<-time.After(50 * time.Millisecond)
33+
}
34+
35+
// then
36+
assert.Nil(t, err)
37+
})
38+
}
39+
}

cmd/fjira-cli/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func initCli() {
2020
rootCmd.AddCommand(commands.GetIssueCmd())
2121
rootCmd.AddCommand(commands.GetWorkspaceCmd())
2222
rootCmd.AddCommand(commands.GetJqlCmd())
23+
rootCmd.AddCommand(commands.GetFiltersCmd())
2324
rootCmd.AddCommand(commands.GetVersionCmd(version))
2425

2526
if err := rootCmd.Execute(); err != nil {

internal/filters/filters_search.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package filters
2+
3+
import (
4+
"github.com/gdamore/tcell/v2"
5+
"github.com/mk-5/fjira/internal/app"
6+
"github.com/mk-5/fjira/internal/jira"
7+
"github.com/mk-5/fjira/internal/ui"
8+
"strings"
9+
)
10+
11+
type filtersSearchView struct {
12+
app.View
13+
api jira.Api
14+
bottomBar *app.ActionBar
15+
fuzzyFind *app.FuzzyFind
16+
}
17+
18+
func NewFiltersView(api jira.Api) app.View {
19+
bottomBar := ui.CreateBottomLeftBar()
20+
bottomBar.AddItem(ui.NewCancelBarItem())
21+
return &filtersSearchView{
22+
api: api,
23+
bottomBar: bottomBar,
24+
}
25+
}
26+
27+
func (view *filtersSearchView) Init() {
28+
go view.startFiltersFuzzyFind()
29+
go view.handleBottomBarActions()
30+
}
31+
32+
func (view *filtersSearchView) Destroy() {
33+
view.bottomBar.Destroy()
34+
}
35+
36+
func (view *filtersSearchView) Draw(screen tcell.Screen) {
37+
if view.fuzzyFind != nil {
38+
view.fuzzyFind.Draw(screen)
39+
}
40+
view.bottomBar.Draw(screen)
41+
}
42+
43+
func (view *filtersSearchView) Update() {
44+
view.bottomBar.Update()
45+
if view.fuzzyFind != nil {
46+
view.fuzzyFind.Update()
47+
}
48+
}
49+
50+
func (view *filtersSearchView) Resize(screenX, screenY int) {
51+
if view.fuzzyFind != nil {
52+
view.fuzzyFind.Resize(screenX, screenY)
53+
}
54+
view.bottomBar.Resize(screenX, screenY)
55+
}
56+
57+
func (view *filtersSearchView) HandleKeyEvent(ev *tcell.EventKey) {
58+
view.bottomBar.HandleKeyEvent(ev)
59+
if view.fuzzyFind != nil {
60+
view.fuzzyFind.HandleKeyEvent(ev)
61+
}
62+
}
63+
64+
func (view *filtersSearchView) startFiltersFuzzyFind() {
65+
app.GetApp().ClearNow()
66+
app.GetApp().Loading(true)
67+
filters, err := view.api.GetMyFilters()
68+
if err != nil {
69+
app.Error(err.Error())
70+
return
71+
}
72+
view.fuzzyFind = app.NewFuzzyFind(ui.MessageSelectFilter, FormatFilters(filters))
73+
view.fuzzyFind.MarginBottom = 1
74+
app.GetApp().Loading(false)
75+
if chosen := <-view.fuzzyFind.Complete; true {
76+
app.GetApp().ClearNow()
77+
query := view.fuzzyFind.GetQuery()
78+
if chosen.Index < 0 && strings.TrimSpace(query) == "" {
79+
// do nothing
80+
return
81+
}
82+
if chosen.Index >= 0 {
83+
filter := filters[chosen.Index]
84+
app.GoTo("issues-search-jql", filter.JQL, view.reopen, view.api)
85+
}
86+
}
87+
}
88+
89+
func (view *filtersSearchView) handleBottomBarActions() {
90+
for {
91+
action := <-view.bottomBar.Action
92+
switch action {
93+
case ui.ActionCancel:
94+
view.cancel()
95+
return
96+
}
97+
}
98+
}
99+
100+
func (view *filtersSearchView) cancel() {
101+
app.GetApp().Quit()
102+
}
103+
104+
func (view *filtersSearchView) reopen() {
105+
app.GoTo("filters", view.api)
106+
}

0 commit comments

Comments
 (0)