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.
+
+
+
+## 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)
}
-
}
}