diff --git a/Gopkg.lock b/Gopkg.lock index 9211154d..8a0e37d8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -25,6 +25,12 @@ packages = ["."] revision = "6a9abf92e34f4de62ac671caee3143f10b98892d" +[[projects]] + branch = "master" + name = "github.com/darkSasori/todoist" + packages = ["."] + revision = "ec6b38b374ab9c60cc9716d2083ae66eb9383d03" + [[projects]] name = "github.com/davecgh/go-spew" packages = ["spew"] @@ -201,6 +207,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a7a00554f9040d7617458773eafa64b82f9502eace145152cb50eb082800e936" + inputs-digest = "b2141b5945354e95e2f3e8f1f6eb182de11e21f0fe1188862c6dc57983c8cbc4" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 6221a4d7..9261787d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -85,6 +85,10 @@ branch = "master" name = "github.com/adlio/trello" +[[constraint]] + branch = "master" + name = "github.com/darkSasori/todoist" + [prune] go-tests = true unused-packages = true diff --git a/_site/content/posts/modules/todoist.md b/_site/content/posts/modules/todoist.md new file mode 100644 index 00000000..bb3beb11 --- /dev/null +++ b/_site/content/posts/modules/todoist.md @@ -0,0 +1,88 @@ +--- +title: "Todoist" +date: 2018-07-05T22:55:55-03:00 +draft: false +--- + +Displays all itens on specified project. + +todoist screenshot + +## Source Code + +```bash +wtf/todoist/ +``` + +## Required ENV Variables + +Key: `WTF_TODOIST_TOKEN`
+Value: Your Todoist API Token.
+ +_You can get your API Token at: todoist.com/prefs/integrations._ + +## Keyboard Commands + +Key: `h`
+Action: Show the previous project. + +Key: `←`
+Action: Show the previous project. + +Key: `l`
+Action: Show the next project. + +Key: `→`
+Action: Show the next project. + +Key: `j`
+Action: Select the next item in the list. + +Key: `↓`
+Action: Select the next item in the list. + +Key: `k`
+Action: Select the previous item in the list. + +Key: `↑`
+Action: Select the previous item in the list. + +Key: `c`
+Action: Close current item. + +Key: `d`
+Action: Delete current item. + +Key: `r`
+Action: Reload all projects. + +## Configuration + +```yaml +todoist: + projects: + - project_id + enabled: true + position: + height: 1 + left: 2 + top: 0 + width: 1 + refreshInterval: 3600 +``` + +### Attributes + +`enabled`
+Determines whether or not this module is executed and if its data displayed onscreen.
+Values: `true`, `false`. + +`projects`
+The todoist projects to fetch items from.
+ +`refreshInterval`
+How often, in seconds, this module will update its data.
+Values: A positive integer, `0..n`. + +`position`
+Where in the grid this module's widget will be displayed.
diff --git a/_site/static/imgs/modules/todoist.png b/_site/static/imgs/modules/todoist.png new file mode 100644 index 00000000..086d0ec8 Binary files /dev/null and b/_site/static/imgs/modules/todoist.png differ diff --git a/_site/themes/hyde-hyde/layouts/partials/sidebar.html b/_site/themes/hyde-hyde/layouts/partials/sidebar.html index 3bb31b75..2da8d975 100644 --- a/_site/themes/hyde-hyde/layouts/partials/sidebar.html +++ b/_site/themes/hyde-hyde/layouts/partials/sidebar.html @@ -47,6 +47,7 @@ + diff --git a/todoist/display.go b/todoist/display.go new file mode 100644 index 00000000..8cbfa858 --- /dev/null +++ b/todoist/display.go @@ -0,0 +1,64 @@ +package todoist + +import ( + "fmt" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" + "github.com/senorprogrammer/wtf/wtf" +) + +func (w *Widget) display() { + if len(w.list) == 0 { + return + } + list := w.list[w.idx] + + w.View.SetTitle(fmt.Sprintf("%s- [green]%s[white] ", w.Name, list.Project.Name)) + str := wtf.SigilStr(len(w.list), w.idx, w.View) + "\n" + + for index, item := range list.items { + if index == list.index { + str = str + fmt.Sprintf("[%s]", wtf.Config.UString("wtf.colors.border.focused", "grey")) + } + str = str + fmt.Sprintf("| | %s[white]\n", tview.Escape(item.Content)) + } + + w.View.Clear() + w.View.SetText(str) +} + +func (w *Widget) keyboardIntercept(event *tcell.EventKey) *tcell.EventKey { + if len(w.list) == 0 { + return event + } + + switch string(event.Rune()) { + case "r": + w.Refresh() + return nil + case "d": + w.Delete() + return nil + case "c": + w.Close() + return nil + } + + switch fromVim(event) { + case tcell.KeyLeft: + w.Prev() + return nil + case tcell.KeyRight: + w.Next() + return nil + case tcell.KeyUp: + w.UP() + return nil + case tcell.KeyDown: + w.Down() + return nil + } + + return event +} diff --git a/todoist/list.go b/todoist/list.go new file mode 100644 index 00000000..81b9d55e --- /dev/null +++ b/todoist/list.go @@ -0,0 +1,79 @@ +package todoist + +import ( + "fmt" + + "github.com/darkSasori/todoist" +) + +type List struct { + todoist.Project + items []todoist.Task + index int +} + +func NewList(id int) *List { + project, err := todoist.GetProject(id) + if err != nil { + panic(err) + } + + list := &List{ + Project: project, + index: -1, + } + list.loadItems() + return list +} + +func (l List) isFirst() bool { + return l.index == 0 +} + +func (l List) isLast() bool { + return l.index >= len(l.items)-1 +} + +func (l *List) up() { + l.index = l.index - 1 + if l.index < 0 { + l.index = len(l.items) - 1 + } +} + +func (l *List) down() { + if l.index == -1 { + l.index = 0 + return + } + + l.index = l.index + 1 + if l.index >= len(l.items) { + l.index = 0 + } +} + +func (l *List) loadItems() { + tasks, err := todoist.ListTask(todoist.QueryParam{"project_id": fmt.Sprintf("%d", l.ID)}) + if err != nil { + panic(err) + } + + l.items = tasks +} + +func (l *List) close() { + if err := l.items[l.index].Close(); err != nil { + panic(err) + } + + l.loadItems() +} + +func (l *List) delete() { + if err := l.items[l.index].Delete(); err != nil { + panic(err) + } + + l.loadItems() +} diff --git a/todoist/widget.go b/todoist/widget.go new file mode 100644 index 00000000..e40089fa --- /dev/null +++ b/todoist/widget.go @@ -0,0 +1,113 @@ +package todoist + +import ( + "os" + + "github.com/darkSasori/todoist" + "github.com/gdamore/tcell" + "github.com/rivo/tview" + "github.com/senorprogrammer/wtf/wtf" +) + +type Widget struct { + wtf.TextWidget + + app *tview.Application + pages *tview.Pages + list []*List + idx int +} + +func NewWidget(app *tview.Application, pages *tview.Pages) *Widget { + widget := Widget{ + TextWidget: wtf.NewTextWidget(" Todoist ", "todoist", true), + + app: app, + pages: pages, + } + + todoist.Token = os.Getenv("WTF_TODOIST_TOKEN") + widget.list = loadProjects() + widget.View.SetInputCapture(widget.keyboardIntercept) + + return &widget +} + +func (w *Widget) Refresh() { + if w.Disabled() || len(w.list) == 0 { + return + } + + w.UpdateRefreshedAt() + w.display() +} + +func (w *Widget) Next() { + w.idx = w.idx + 1 + if w.idx == len(w.list) { + w.idx = 0 + } + + w.display() +} + +func (w *Widget) Prev() { + w.idx = w.idx - 1 + if w.idx < 0 { + w.idx = len(w.list) - 1 + } + + w.display() +} + +func (w *Widget) Down() { + w.list[w.idx].down() + w.display() +} + +func (w *Widget) UP() { + w.list[w.idx].up() + w.display() +} + +func (w *Widget) Close() { + w.list[w.idx].close() + if w.list[w.idx].isLast() { + w.UP() + return + } + w.Down() +} + +func (w *Widget) Delete() { + w.list[w.idx].close() + if w.list[w.idx].isLast() { + w.UP() + return + } + w.Down() +} + +func loadProjects() []*List { + lists := []*List{} + for _, id := range wtf.Config.UList("wtf.mods.todoist.projects") { + list := NewList(id.(int)) + lists = append(lists, list) + } + + return lists +} + +func fromVim(event *tcell.EventKey) tcell.Key { + switch string(event.Rune()) { + case "h": + return tcell.KeyLeft + case "l": + return tcell.KeyRight + case "k": + return tcell.KeyUp + case "j": + return tcell.KeyDown + } + return event.Key() +} diff --git a/wtf.go b/wtf.go index 39a8b99e..ad536aaa 100644 --- a/wtf.go +++ b/wtf.go @@ -39,6 +39,7 @@ import ( "github.com/senorprogrammer/wtf/system" "github.com/senorprogrammer/wtf/textfile" "github.com/senorprogrammer/wtf/todo" + "github.com/senorprogrammer/wtf/todoist" "github.com/senorprogrammer/wtf/trello" "github.com/senorprogrammer/wtf/weatherservices/prettyweather" "github.com/senorprogrammer/wtf/weatherservices/weather" @@ -218,6 +219,8 @@ func addWidget(app *tview.Application, pages *tview.Pages, widgetName string) { Widgets = append(Widgets, textfile.NewWidget(app, pages)) case "todo": Widgets = append(Widgets, todo.NewWidget(app, pages)) + case "todoist": + Widgets = append(Widgets, todoist.NewWidget(app, pages)) case "trello": Widgets = append(Widgets, trello.NewWidget()) case "weather": @@ -233,7 +236,6 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) { if enabled := Config.UBool("wtf.mods."+mod+".enabled", false); enabled { addWidget(app, pages, mod) } - } }