mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Merge branch 'darkSasori-master'
This commit is contained in:
commit
8267c3aa93
8
Gopkg.lock
generated
8
Gopkg.lock
generated
@ -25,6 +25,12 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "6a9abf92e34f4de62ac671caee3143f10b98892d"
|
revision = "6a9abf92e34f4de62ac671caee3143f10b98892d"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/darkSasori/todoist"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ec6b38b374ab9c60cc9716d2083ae66eb9383d03"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
@ -201,6 +207,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "04892edb6b5f0be61b391ccead307ed15899532db05a17b9e28c00ee32a34861"
|
inputs-digest = "9eaa70ed639c832e3cde26a4270f4c7b9124960952aa76506f702c2c296d5019"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -85,6 +85,10 @@
|
|||||||
name = "github.com/adlio/trello"
|
name = "github.com/adlio/trello"
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/darkSasori/todoist"
|
||||||
|
|
||||||
[prune]
|
[prune]
|
||||||
go-tests = true
|
go-tests = true
|
||||||
unused-packages = true
|
unused-packages = true
|
||||||
|
88
_site/content/posts/modules/todoist.md
Normal file
88
_site/content/posts/modules/todoist.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
title: "Todoist"
|
||||||
|
date: 2018-07-05T22:55:55-03:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Displays all itens on specified project.
|
||||||
|
|
||||||
|
<img src="/imgs/modules/todoist.png" alt="todoist screenshot" />
|
||||||
|
|
||||||
|
## Source Code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wtf/todoist/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required ENV Variables
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `WTF_TODOIST_TOKEN` <br />
|
||||||
|
<span class="caption">Value:</span> Your Todoist API Token. <br />
|
||||||
|
|
||||||
|
_You can get your API Token at: todoist.com/prefs/integrations._
|
||||||
|
|
||||||
|
## Keyboard Commands
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `h` <br />
|
||||||
|
<span class="caption">Action:</span> Show the previous project.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `←` <br />
|
||||||
|
<span class="caption">Action:</span←> Show the previous project.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `l` <br />
|
||||||
|
<span class="caption">Action:</span←> Show the next project.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `→` <br />
|
||||||
|
<span class="caption">Action:</span> Show the next project.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `j` <br />
|
||||||
|
<span class="caption">Action:</span> Select the next item in the list.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `↓` <br />
|
||||||
|
<span class="caption">Action:</span> Select the next item in the list.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `k` <br />
|
||||||
|
<span class="caption">Action:</span> Select the previous item in the list.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `↑` <br />
|
||||||
|
<span class="caption">Action:</span> Select the previous item in the list.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `c` <br />
|
||||||
|
<span class="caption">Action:</span> Close current item.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `d` <br />
|
||||||
|
<span class="caption">Action:</span> Delete current item.
|
||||||
|
|
||||||
|
<span class="caption">Key:</span> `r` <br />
|
||||||
|
<span class="caption">Action:</span> Reload all projects.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
todoist:
|
||||||
|
projects:
|
||||||
|
- project_id
|
||||||
|
enabled: true
|
||||||
|
position:
|
||||||
|
height: 1
|
||||||
|
left: 2
|
||||||
|
top: 0
|
||||||
|
width: 1
|
||||||
|
refreshInterval: 3600
|
||||||
|
```
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
|
`enabled` <br />
|
||||||
|
Determines whether or not this module is executed and if its data displayed onscreen. <br />
|
||||||
|
Values: `true`, `false`.
|
||||||
|
|
||||||
|
`projects` <br />
|
||||||
|
The todoist projects to fetch items from. <br />
|
||||||
|
|
||||||
|
`refreshInterval` <br />
|
||||||
|
How often, in seconds, this module will update its data. <br />
|
||||||
|
Values: A positive integer, `0..n`.
|
||||||
|
|
||||||
|
`position` <br />
|
||||||
|
Where in the grid this module's widget will be displayed. <br />
|
BIN
_site/static/imgs/modules/todoist.png
Normal file
BIN
_site/static/imgs/modules/todoist.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
@ -47,6 +47,7 @@
|
|||||||
<li class="sidebar-list-item-2"><a href="/posts/modules/security/">Security</a></li>
|
<li class="sidebar-list-item-2"><a href="/posts/modules/security/">Security</a></li>
|
||||||
<li class="sidebar-list-item-2"><a href="/posts/modules/textfile/">Text File</a></li>
|
<li class="sidebar-list-item-2"><a href="/posts/modules/textfile/">Text File</a></li>
|
||||||
<li class="sidebar-list-item-2"><a href="/posts/modules/todo/">Todo</a></li>
|
<li class="sidebar-list-item-2"><a href="/posts/modules/todo/">Todo</a></li>
|
||||||
|
<li class="sidebar-list-item-2"><a href="/posts/modules/todoist/">Todoist</a></li>
|
||||||
<li class="sidebar-list-item-2"><a href="/posts/modules/trello/">Trello</a></li>
|
<li class="sidebar-list-item-2"><a href="/posts/modules/trello/">Trello</a></li>
|
||||||
<li class="sidebar-list-item-2"><a href="/posts/modules/weather/">Weather</a></li>
|
<li class="sidebar-list-item-2"><a href="/posts/modules/weather/">Weather</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
64
todoist/display.go
Normal file
64
todoist/display.go
Normal file
@ -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
|
||||||
|
}
|
79
todoist/list.go
Normal file
79
todoist/list.go
Normal file
@ -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()
|
||||||
|
}
|
113
todoist/widget.go
Normal file
113
todoist/widget.go
Normal file
@ -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()
|
||||||
|
}
|
2
vendor/github.com/darkSasori/todoist/.gitignore
generated
vendored
Normal file
2
vendor/github.com/darkSasori/todoist/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.out
|
||||||
|
vendor/*
|
19
vendor/github.com/darkSasori/todoist/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/darkSasori/todoist/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|
||||||
|
go:
|
||||||
|
- "1.10.3"
|
||||||
|
- master
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||||
|
- go get -u github.com/golang/lint/golint
|
||||||
|
|
||||||
|
install:
|
||||||
|
- dep ensure
|
||||||
|
|
||||||
|
script:
|
||||||
|
- golint -set_exit_status
|
||||||
|
- go test -v
|
21
vendor/github.com/darkSasori/todoist/Gopkg.lock
generated
vendored
Normal file
21
vendor/github.com/darkSasori/todoist/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/darkSasori/todoist"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ab3a9f0b9d551ab4e57f1caae9bb20d5aa51ec10"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "v1"
|
||||||
|
name = "gopkg.in/jarcoal/httpmock.v1"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "16f9a43967d613f0adc2000f0094a17b9f6c4c20"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "4d0d4d6dd7b4141be119cb48281ea7c10f05d3037c0e4ac49560cf4af21917a7"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
34
vendor/github.com/darkSasori/todoist/Gopkg.toml
generated
vendored
Normal file
34
vendor/github.com/darkSasori/todoist/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "v1"
|
||||||
|
name = "gopkg.in/jarcoal/httpmock.v1"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
21
vendor/github.com/darkSasori/todoist/LICENSE
generated
vendored
Normal file
21
vendor/github.com/darkSasori/todoist/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Lineu Felipe
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
6
vendor/github.com/darkSasori/todoist/README.md
generated
vendored
Normal file
6
vendor/github.com/darkSasori/todoist/README.md
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# todoist
|
||||||
|
[](https://godoc.org/github.com/darkSasori/todoist)
|
||||||
|
[](https://travis-ci.org/darkSasori/todoist)
|
||||||
|
[](https://goreportcard.com/report/github.com/darkSasori/todoist)
|
||||||
|
|
||||||
|
Unofficial todoist api implementation
|
149
vendor/github.com/darkSasori/todoist/projects.go
generated
vendored
Normal file
149
vendor/github.com/darkSasori/todoist/projects.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package todoist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Project is a model of todoist project entity
|
||||||
|
type Project struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CommentCount int `json:"comment_count"`
|
||||||
|
Order int `json:"order"`
|
||||||
|
Indent int `json:"indent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeProject(body io.ReadCloser) (Project, error) {
|
||||||
|
defer body.Close()
|
||||||
|
decoder := json.NewDecoder(body)
|
||||||
|
var project Project
|
||||||
|
|
||||||
|
if err := decoder.Decode(&project); err != nil {
|
||||||
|
return Project{}, err
|
||||||
|
}
|
||||||
|
return project, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListProject return all projects
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// todoist.Token = "your token"
|
||||||
|
// projects, err := todoist.ListProject()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// fmt.Println(projects)
|
||||||
|
func ListProject() ([]Project, error) {
|
||||||
|
res, err := makeRequest(http.MethodGet, "projects", nil)
|
||||||
|
if err != nil {
|
||||||
|
return []Project{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
decoder := json.NewDecoder(res.Body)
|
||||||
|
var projects []Project
|
||||||
|
|
||||||
|
if err := decoder.Decode(&projects); err != nil {
|
||||||
|
return []Project{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return projects, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProject return a project by id
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// todoist.Token = "your token"
|
||||||
|
// project, err := todoist.GetProject(1)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// fmt.Println(project)
|
||||||
|
func GetProject(id int) (Project, error) {
|
||||||
|
path := fmt.Sprintf("projects/%d", id)
|
||||||
|
res, err := makeRequest(http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Project{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeProject(res.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProject create a new project with a name
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// todoist.Token = "your token"
|
||||||
|
// project, err := todoist.CreateProject("New Project")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// fmt.Println(project)
|
||||||
|
func CreateProject(name string) (Project, error) {
|
||||||
|
project := struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}{
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := makeRequest(http.MethodPost, "projects", project)
|
||||||
|
if err != nil {
|
||||||
|
return Project{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeProject(res.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete project
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// todoist.Token = "your token"
|
||||||
|
// project, err := todoist.GetProject(1)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// err = project.Delete()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
func (p Project) Delete() error {
|
||||||
|
path := fmt.Sprintf("projects/%d", p.ID)
|
||||||
|
_, err := makeRequest(http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update project
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// todoist.Token = "your token"
|
||||||
|
// project, err := todoist.GetProject(1)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// project.Name = "updated"
|
||||||
|
// err = project.Update()
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// fmt.Println(project)
|
||||||
|
func (p Project) Update() error {
|
||||||
|
path := fmt.Sprintf("projects/%d", p.ID)
|
||||||
|
project := struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}{
|
||||||
|
p.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := makeRequest(http.MethodPost, path, project)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
158
vendor/github.com/darkSasori/todoist/task.go
generated
vendored
Normal file
158
vendor/github.com/darkSasori/todoist/task.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package todoist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Task is a model of todoist project entity
|
||||||
|
type Task struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
CommentCount int `json:"comment_count"`
|
||||||
|
Completed bool `json:"completed"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Indent int `json:"indent"`
|
||||||
|
LabelIDs []int `json:"label_ids"`
|
||||||
|
Order int `json:"order"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
ProjectID int `json:"project_id"`
|
||||||
|
Due Due `json:"due"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due is a model of todoist project entity
|
||||||
|
type Due struct {
|
||||||
|
String string `json:"string"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
Datetime CustomTime `json:"datetime"`
|
||||||
|
Timezone string `json:"timezone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Task) taskSave() taskSave {
|
||||||
|
return taskSave{
|
||||||
|
t.Content,
|
||||||
|
t.ProjectID,
|
||||||
|
t.Order,
|
||||||
|
t.LabelIDs,
|
||||||
|
t.Priority,
|
||||||
|
t.Due.String,
|
||||||
|
t.Due.Datetime,
|
||||||
|
"en",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTask(body io.ReadCloser) (Task, error) {
|
||||||
|
defer body.Close()
|
||||||
|
decoder := json.NewDecoder(body)
|
||||||
|
var task Task
|
||||||
|
|
||||||
|
if err := decoder.Decode(&task); err != nil {
|
||||||
|
return Task{}, err
|
||||||
|
}
|
||||||
|
return task, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryParam is a map[string]string to build http query
|
||||||
|
type QueryParam map[string]string
|
||||||
|
|
||||||
|
func (qp QueryParam) String() string {
|
||||||
|
if len(qp) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := "?"
|
||||||
|
for key, value := range qp {
|
||||||
|
if ret != "?" {
|
||||||
|
ret = ret + "&"
|
||||||
|
}
|
||||||
|
ret = ret + key + "=" + value
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTask return all task, you can filter using QueryParam
|
||||||
|
// See documentation: https://developer.todoist.com/rest/v8/#get-tasks
|
||||||
|
func ListTask(qp QueryParam) ([]Task, error) {
|
||||||
|
path := fmt.Sprintf("tasks%s", qp)
|
||||||
|
res, err := makeRequest(http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []Task{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
decoder := json.NewDecoder(res.Body)
|
||||||
|
var tasks []Task
|
||||||
|
|
||||||
|
if err := decoder.Decode(&tasks); err != nil {
|
||||||
|
return []Task{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTask return a task by id
|
||||||
|
func GetTask(id int) (Task, error) {
|
||||||
|
path := fmt.Sprintf("tasks/%d", id)
|
||||||
|
res, err := makeRequest(http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Task{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeTask(res.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTask create a new task
|
||||||
|
func CreateTask(task Task) (Task, error) {
|
||||||
|
res, err := makeRequest(http.MethodPost, "tasks", task.taskSave())
|
||||||
|
if err != nil {
|
||||||
|
return Task{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeTask(res.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete remove a task
|
||||||
|
func (t Task) Delete() error {
|
||||||
|
path := fmt.Sprintf("tasks/%d", t.ID)
|
||||||
|
_, err := makeRequest(http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update a task
|
||||||
|
func (t Task) Update() error {
|
||||||
|
path := fmt.Sprintf("tasks/%d", t.ID)
|
||||||
|
_, err := makeRequest(http.MethodPost, path, t.taskSave())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mask task as done
|
||||||
|
func (t Task) Close() error {
|
||||||
|
path := fmt.Sprintf("tasks/%d/close", t.ID)
|
||||||
|
_, err := makeRequest(http.MethodPost, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen a task
|
||||||
|
func (t Task) Reopen() error {
|
||||||
|
path := fmt.Sprintf("tasks/%d/reopen", t.ID)
|
||||||
|
_, err := makeRequest(http.MethodPost, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
145
vendor/github.com/darkSasori/todoist/todoist.go
generated
vendored
Normal file
145
vendor/github.com/darkSasori/todoist/todoist.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package todoist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token save the personal token from todoist
|
||||||
|
var Token string
|
||||||
|
var todoistURL = "https://beta.todoist.com/API/v8/"
|
||||||
|
|
||||||
|
func makeRequest(method, endpoint string, data interface{}) (*http.Response, error) {
|
||||||
|
url := todoistURL + endpoint
|
||||||
|
body := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
if data != nil {
|
||||||
|
json, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body = bytes.NewBuffer(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bearer := fmt.Sprintf("Bearer %s", Token)
|
||||||
|
req.Header.Add("Authorization", bearer)
|
||||||
|
|
||||||
|
if data != nil {
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode >= 400 {
|
||||||
|
defer res.Body.Close()
|
||||||
|
str, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(string(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctLayout = "2006-01-02T15:04:05+00:00"
|
||||||
|
|
||||||
|
// CustomTime had a custom json date format
|
||||||
|
type CustomTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON convert from []byte to CustomTime
|
||||||
|
func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
s := strings.Trim(string(b), "\"")
|
||||||
|
if s == "null" {
|
||||||
|
ct.Time = time.Time{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.Time, err = time.Parse(ctLayout, s)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON convert CustomTime to []byte
|
||||||
|
func (ct CustomTime) MarshalJSON() ([]byte, error) {
|
||||||
|
if ct.Time.IsZero() {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return []byte(`"` + ct.Time.Format(ctLayout) + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type taskSave struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
ProjectID int `json:"project_id,omitempty"`
|
||||||
|
Order int `json:"order,omitempty"`
|
||||||
|
LabelIDs []int `json:"label_ids,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty"`
|
||||||
|
DueString string `json:"due_string,omitempty"`
|
||||||
|
DueDateTime CustomTime `json:"due_datetime,omitempty"`
|
||||||
|
DueLang string `json:"due_lang,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts taskSave) MarshalJSON() ([]byte, error) {
|
||||||
|
buffer := bytes.NewBufferString("{")
|
||||||
|
|
||||||
|
if ts.Content == "" {
|
||||||
|
return nil, fmt.Errorf("Content is empty")
|
||||||
|
}
|
||||||
|
buffer.WriteString(fmt.Sprintf("\"content\":\"%s\"", ts.Content))
|
||||||
|
|
||||||
|
if ts.ProjectID != 0 {
|
||||||
|
buffer.WriteString(fmt.Sprintf(",\"project_id\":%d", ts.ProjectID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.Order != 0 {
|
||||||
|
buffer.WriteString(fmt.Sprintf(",\"order\":%d", ts.Order))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ts.DueDateTime.IsZero() {
|
||||||
|
buffer.WriteString(",\"due_datetime\":")
|
||||||
|
json, err := json.Marshal(ts.DueDateTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ts.LabelIDs) != 0 {
|
||||||
|
buffer.WriteString(",\"label_ids\":")
|
||||||
|
json, err := json.Marshal(ts.LabelIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.Priority != 0 {
|
||||||
|
buffer.WriteString(fmt.Sprintf(",\"priority\":%d", ts.Priority))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.DueString != "" {
|
||||||
|
buffer.WriteString(fmt.Sprintf(",\"due_string\":\"%s\"", ts.DueString))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ts.DueLang != "" {
|
||||||
|
buffer.WriteString(fmt.Sprintf(",\"due_lang\":\"%s\"", ts.DueLang))
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.WriteString("}")
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
4
wtf.go
4
wtf.go
@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/senorprogrammer/wtf/system"
|
"github.com/senorprogrammer/wtf/system"
|
||||||
"github.com/senorprogrammer/wtf/textfile"
|
"github.com/senorprogrammer/wtf/textfile"
|
||||||
"github.com/senorprogrammer/wtf/todo"
|
"github.com/senorprogrammer/wtf/todo"
|
||||||
|
"github.com/senorprogrammer/wtf/todoist"
|
||||||
"github.com/senorprogrammer/wtf/trello"
|
"github.com/senorprogrammer/wtf/trello"
|
||||||
"github.com/senorprogrammer/wtf/weatherservices/prettyweather"
|
"github.com/senorprogrammer/wtf/weatherservices/prettyweather"
|
||||||
"github.com/senorprogrammer/wtf/weatherservices/weather"
|
"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))
|
Widgets = append(Widgets, textfile.NewWidget(app, pages))
|
||||||
case "todo":
|
case "todo":
|
||||||
Widgets = append(Widgets, todo.NewWidget(app, pages))
|
Widgets = append(Widgets, todo.NewWidget(app, pages))
|
||||||
|
case "todoist":
|
||||||
|
Widgets = append(Widgets, todoist.NewWidget(app, pages))
|
||||||
case "trello":
|
case "trello":
|
||||||
Widgets = append(Widgets, trello.NewWidget())
|
Widgets = append(Widgets, trello.NewWidget())
|
||||||
case "weather":
|
case "weather":
|
||||||
@ -233,7 +236,6 @@ func makeWidgets(app *tview.Application, pages *tview.Pages) {
|
|||||||
if enabled := Config.UBool("wtf.mods."+mod+".enabled", false); enabled {
|
if enabled := Config.UBool("wtf.mods."+mod+".enabled", false); enabled {
|
||||||
addWidget(app, pages, mod)
|
addWidget(app, pages, mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user