mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Merge branch 'master' into dependabot/go_modules/github.com/nicklaw5/helix-0.7.0
This commit is contained in:
commit
589af9917a
@ -959,6 +959,41 @@
|
|||||||
"name": "Tim Hwang",
|
"name": "Tim Hwang",
|
||||||
"avatar_url": "https://avatars3.githubusercontent.com/u/5831434?v=4",
|
"avatar_url": "https://avatars3.githubusercontent.com/u/5831434?v=4",
|
||||||
"profile": "http://timhwang21.gitbook.io",
|
"profile": "http://timhwang21.gitbook.io",
|
||||||
|
"contributions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "cyingfan",
|
||||||
|
"name": "Ying Fan Chong",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/10404961?v=4",
|
||||||
|
"profile": "http://about.me/yingfan",
|
||||||
|
"contributions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "MartinJohns",
|
||||||
|
"name": "Martin Johns",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/5269069?v=4",
|
||||||
|
"profile": "https://github.com/MartinJohns",
|
||||||
|
"contributions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "jamietanna",
|
||||||
|
"name": "Jamie Tanna",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/3315059?v=4",
|
||||||
|
"profile": "https://www.jvt.me",
|
||||||
|
"contributions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "trimble",
|
||||||
|
"name": "Todd Trimble",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/371317?v=4",
|
||||||
|
"profile": "https://github.com/trimble",
|
||||||
|
"contributions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "mhanberg",
|
||||||
|
"name": "Mitchell Hanberg",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/5523984?v=4",
|
||||||
|
"profile": "https://www.mitchellhanberg.com",
|
||||||
"contributions": [
|
"contributions": [
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- "1.14.x"
|
- "1.15.x"
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
# Make sure travis builds work for forks
|
# Make sure travis builds work for forks
|
||||||
|
26
CHANGELOG.md
26
CHANGELOG.md
@ -4,6 +4,32 @@
|
|||||||
|
|
||||||
### ⚡️ Added
|
### ⚡️ Added
|
||||||
|
|
||||||
|
* FeedReader module supports [display mode switching](https://github.com/wtfutil/wtf/pull/976) via the `t` key, by [@cyingfan](https://github.com/cyingfan)
|
||||||
|
* Clocks module now supports `natural` ordering option, [#896](https://github.com/wtfutil/wtf/issues/896) by [@seanstoppable](https://github.com/Seanstoppable)
|
||||||
|
|
||||||
|
### 🐞 Fixed
|
||||||
|
|
||||||
|
* Exchange Rates module now displays rates in alphabetical order, sorted by the base rate, by [@senorprogrammer](https://github.com/senorprogrammer)
|
||||||
|
* Feed Reader module no longer crashes on feeds that don't have a published date, [#958](https://github.com/wtfutil/wtf/issues/958) by [@cyingfan](https://github.com/cyingfan)
|
||||||
|
* Stray blank lines no longer appear at the end of highlightable lists, [#977](https://github.com/wtfutil/wtf/pull/977) by [@cyingfan](https://github.com/cyingfan)
|
||||||
|
* `config.yml` now properly set to `0600` instead of `0666`, by [@cyingfan](https://github.com/cyingfan)
|
||||||
|
|
||||||
|
### 👍 Updated
|
||||||
|
|
||||||
|
* Updated `github.com/gdamore/tcell` from 1.3.0 to 1.4.0
|
||||||
|
* Updated `github.com/shirou/gopsutil` from 2.20.7+incompatible to 2.20.8+incompatible
|
||||||
|
* Updated `github.com/mmcdole/gofeed` from 1.0.0 to 1.1.0
|
||||||
|
* Updated `github.com/digitalocean/godo` from 1.42.1 to 1.44.0
|
||||||
|
* Updated `github.com/xanzy/go-gitlab` from 0.33.0 to 0.38.1
|
||||||
|
* Updated `github.com/adlio/trello` from 1.7.0 to 1.8.0
|
||||||
|
* Updated `github.com/alecthomas/chroma` from 0.8.0 to 0.8.1
|
||||||
|
|
||||||
|
* Now requires Go 1.15 for compilation in Dockerfiles and go.mod, by [@seanstoppable](https://github.com/Seanstoppable)
|
||||||
|
|
||||||
|
## v0.32.0
|
||||||
|
|
||||||
|
### ⚡️ Added
|
||||||
|
|
||||||
* PagerDuty module now supports `showOncallEnd` setting, which determines whether or not to display the oncall rotation end date, by [@senorprogrammer](https://github.com/senorprogrammer)
|
* PagerDuty module now supports `showOncallEnd` setting, which determines whether or not to display the oncall rotation end date, by [@senorprogrammer](https://github.com/senorprogrammer)
|
||||||
* Exchange Rates module now supports `precision` setting, which determines how many decimal places to display, [#897](https://github.com/wtfutil/wtf/issues/897) by [@senorprogrammer](https://github.com/senorprogrammer)
|
* Exchange Rates module now supports `precision` setting, which determines how many decimal places to display, [#897](https://github.com/wtfutil/wtf/issues/897) by [@senorprogrammer](https://github.com/senorprogrammer)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.13-alpine as build
|
FROM golang:1.15-alpine as build
|
||||||
|
|
||||||
ARG version=master
|
ARG version=master
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.13 as build
|
FROM golang:1.15 as build
|
||||||
|
|
||||||
ARG version=master
|
ARG version=master
|
||||||
|
|
||||||
|
11
README.md
11
README.md
@ -130,6 +130,10 @@ docker rm wtf_build
|
|||||||
|
|
||||||
**Note:** WTF is _only_ compatible with Go versions **1.12.0** or later (due to the use of Go modules and newer standard library functions). If you would like to use `gccgo` to compile, you _must_ use `gccgo-9` or later which introduces support for Go modules.
|
**Note:** WTF is _only_ compatible with Go versions **1.12.0** or later (due to the use of Go modules and newer standard library functions). If you would like to use `gccgo` to compile, you _must_ use `gccgo-9` or later which introduces support for Go modules.
|
||||||
|
|
||||||
|
### Installing via Arch User Repository
|
||||||
|
|
||||||
|
Arch Linux users can utilise the [wtfutil](https://aur.archlinux.org/packages/wtfutil) package to build it from source, or [wtfutil-bin](https://aur.archlinux.org/packages/wtfutil-bin/) to install pre-built binaries.
|
||||||
|
|
||||||
## Running via Docker
|
## Running via Docker
|
||||||
|
|
||||||
You can run `wtf` inside a docker container:
|
You can run `wtf` inside a docker container:
|
||||||
@ -396,6 +400,13 @@ Dependency management in WTF is handled by [Go modules](https://github.com/golan
|
|||||||
<td align="center"><a href="https://github.com/fmotrifork"><img src="https://avatars3.githubusercontent.com/u/18327738?v=4" width="48px;" alt=""/><br /><sub><b>Frederik Mogensen</b></sub></a><br /></td>
|
<td align="center"><a href="https://github.com/fmotrifork"><img src="https://avatars3.githubusercontent.com/u/18327738?v=4" width="48px;" alt=""/><br /><sub><b>Frederik Mogensen</b></sub></a><br /></td>
|
||||||
<td align="center"><a href="https://github.com/aeter"><img src="https://avatars0.githubusercontent.com/u/238607?v=4" width="48px;" alt=""/><br /><sub><b>aeter</b></sub></a><br /></td>
|
<td align="center"><a href="https://github.com/aeter"><img src="https://avatars0.githubusercontent.com/u/238607?v=4" width="48px;" alt=""/><br /><sub><b>aeter</b></sub></a><br /></td>
|
||||||
<td align="center"><a href="http://timhwang21.gitbook.io"><img src="https://avatars3.githubusercontent.com/u/5831434?v=4" width="48px;" alt=""/><br /><sub><b>Tim Hwang</b></sub></a><br /></td>
|
<td align="center"><a href="http://timhwang21.gitbook.io"><img src="https://avatars3.githubusercontent.com/u/5831434?v=4" width="48px;" alt=""/><br /><sub><b>Tim Hwang</b></sub></a><br /></td>
|
||||||
|
<td align="center"><a href="http://about.me/yingfan"><img src="https://avatars1.githubusercontent.com/u/10404961?v=4" width="48px;" alt=""/><br /><sub><b>Ying Fan Chong</b></sub></a><br /></td>
|
||||||
|
<td align="center"><a href="https://github.com/MartinJohns"><img src="https://avatars1.githubusercontent.com/u/5269069?v=4" width="48px;" alt=""/><br /><sub><b>Martin Johns</b></sub></a><br /></td>
|
||||||
|
<td align="center"><a href="https://www.jvt.me"><img src="https://avatars0.githubusercontent.com/u/3315059?v=4" width="48px;" alt=""/><br /><sub><b>Jamie Tanna</b></sub></a><br /></td>
|
||||||
|
<td align="center"><a href="https://github.com/trimble"><img src="https://avatars3.githubusercontent.com/u/371317?v=4" width="48px;" alt=""/><br /><sub><b>Todd Trimble</b></sub></a><br /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://www.mitchellhanberg.com"><img src="https://avatars2.githubusercontent.com/u/5523984?v=4" width="48px;" alt=""/><br /><sub><b>Mitchell Hanberg</b></sub></a><br /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -59,10 +59,9 @@ import (
|
|||||||
"github.com/wtfutil/wtf/modules/subreddit"
|
"github.com/wtfutil/wtf/modules/subreddit"
|
||||||
"github.com/wtfutil/wtf/modules/textfile"
|
"github.com/wtfutil/wtf/modules/textfile"
|
||||||
"github.com/wtfutil/wtf/modules/todo"
|
"github.com/wtfutil/wtf/modules/todo"
|
||||||
"github.com/wtfutil/wtf/modules/todoist"
|
"github.com/wtfutil/wtf/modules/todo_plus"
|
||||||
"github.com/wtfutil/wtf/modules/transmission"
|
"github.com/wtfutil/wtf/modules/transmission"
|
||||||
"github.com/wtfutil/wtf/modules/travisci"
|
"github.com/wtfutil/wtf/modules/travisci"
|
||||||
"github.com/wtfutil/wtf/modules/trello"
|
|
||||||
"github.com/wtfutil/wtf/modules/twitch"
|
"github.com/wtfutil/wtf/modules/twitch"
|
||||||
"github.com/wtfutil/wtf/modules/twitter"
|
"github.com/wtfutil/wtf/modules/twitter"
|
||||||
"github.com/wtfutil/wtf/modules/twitterstats"
|
"github.com/wtfutil/wtf/modules/twitterstats"
|
||||||
@ -269,9 +268,12 @@ func MakeWidget(
|
|||||||
case "todo":
|
case "todo":
|
||||||
settings := todo.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
settings := todo.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||||
widget = todo.NewWidget(app, pages, settings)
|
widget = todo.NewWidget(app, pages, settings)
|
||||||
|
case "todo_plus":
|
||||||
|
settings := todo_plus.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||||
|
widget = todo_plus.NewWidget(app, pages, settings)
|
||||||
case "todoist":
|
case "todoist":
|
||||||
settings := todoist.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
settings := todo_plus.FromTodoist(moduleName, moduleConfig, config)
|
||||||
widget = todoist.NewWidget(app, pages, settings)
|
widget = todo_plus.NewWidget(app, pages, settings)
|
||||||
case "transmission":
|
case "transmission":
|
||||||
settings := transmission.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
settings := transmission.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||||
widget = transmission.NewWidget(app, pages, settings)
|
widget = transmission.NewWidget(app, pages, settings)
|
||||||
@ -279,8 +281,8 @@ func MakeWidget(
|
|||||||
settings := travisci.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
settings := travisci.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||||
widget = travisci.NewWidget(app, pages, settings)
|
widget = travisci.NewWidget(app, pages, settings)
|
||||||
case "trello":
|
case "trello":
|
||||||
settings := trello.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
settings := todo_plus.FromTrello(moduleName, moduleConfig, config)
|
||||||
widget = trello.NewWidget(app, settings)
|
widget = todo_plus.NewWidget(app, pages, settings)
|
||||||
case "twitch":
|
case "twitch":
|
||||||
settings := twitch.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
settings := twitch.NewSettingsFromYAML(moduleName, moduleConfig, config)
|
||||||
widget = twitch.NewWidget(app, pages, settings)
|
widget = twitch.NewWidget(app, pages, settings)
|
||||||
|
@ -37,7 +37,7 @@ func CreateFile(fileName string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath := fmt.Sprintf("%s/%s", configDir, fileName)
|
filePath := filepath.Join(configDir, fileName)
|
||||||
|
|
||||||
// Check if the file already exists; if it does not, create it
|
// Check if the file already exists; if it does not, create it
|
||||||
_, err = os.Stat(filePath)
|
_, err = os.Stat(filePath)
|
||||||
@ -106,7 +106,7 @@ func LoadWtfConfigFile(filePath string) *config.Config {
|
|||||||
// chmodConfigFile sets the mode of the config file to r+w for the owner only
|
// chmodConfigFile sets the mode of the config file to r+w for the owner only
|
||||||
func chmodConfigFile() {
|
func chmodConfigFile() {
|
||||||
configDir, _ := WtfConfigDir()
|
configDir, _ := WtfConfigDir()
|
||||||
relPath := fmt.Sprintf("%s%s", configDir, WtfConfigFile)
|
relPath := filepath.Join(configDir, WtfConfigFile)
|
||||||
absPath, _ := expandHomeDir(relPath)
|
absPath, _ := expandHomeDir(relPath)
|
||||||
|
|
||||||
_, err := os.Stat(absPath)
|
_, err := os.Stat(absPath)
|
||||||
|
12
go.mod
12
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/wtfutil/wtf
|
module github.com/wtfutil/wtf
|
||||||
|
|
||||||
go 1.14
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.cloudfoundry.org/bytefmt v0.0.0-20190819182555-854d396b647c
|
code.cloudfoundry.org/bytefmt v0.0.0-20190819182555-854d396b647c
|
||||||
@ -9,13 +9,13 @@ require (
|
|||||||
github.com/PagerDuty/go-pagerduty v0.0.0-20191002190746-f60f4fc45222
|
github.com/PagerDuty/go-pagerduty v0.0.0-20191002190746-f60f4fc45222
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||||
github.com/VictorAvelar/devto-api-go v1.0.0
|
github.com/VictorAvelar/devto-api-go v1.0.0
|
||||||
github.com/adlio/trello v1.7.0
|
github.com/adlio/trello v1.8.0
|
||||||
github.com/alecthomas/chroma v0.8.0
|
github.com/alecthomas/chroma v0.8.1
|
||||||
github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8
|
github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8
|
||||||
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d
|
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
github.com/digitalocean/godo v1.42.1
|
github.com/digitalocean/godo v1.46.0
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||||
github.com/docker/docker v1.13.1
|
github.com/docker/docker v1.13.1
|
||||||
github.com/docker/docker-credential-helpers v0.6.3
|
github.com/docker/docker-credential-helpers v0.6.3
|
||||||
@ -56,10 +56,11 @@ require (
|
|||||||
github.com/spf13/cobra v0.0.5 // indirect
|
github.com/spf13/cobra v0.0.5 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/spf13/viper v1.6.1 // indirect
|
github.com/spf13/viper v1.6.1 // indirect
|
||||||
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
github.com/wtfutil/spotigopher v0.0.0-20191127141047-7d8168fe103a
|
github.com/wtfutil/spotigopher v0.0.0-20191127141047-7d8168fe103a
|
||||||
github.com/wtfutil/todoist v0.0.2-0.20191216004217-0ec29ceda61a
|
github.com/wtfutil/todoist v0.0.2-0.20191216004217-0ec29ceda61a
|
||||||
github.com/xanzy/go-gitlab v0.33.0
|
github.com/xanzy/go-gitlab v0.38.1
|
||||||
github.com/zmb3/spotify v0.0.0-20191010212056-e12fb981aacb
|
github.com/zmb3/spotify v0.0.0-20191010212056-e12fb981aacb
|
||||||
github.com/zorkian/go-datadog-api v2.29.0+incompatible
|
github.com/zorkian/go-datadog-api v2.29.0+incompatible
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
@ -69,6 +70,7 @@ require (
|
|||||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181110093347-3be5f16b70eb // indirect
|
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181110093347-3be5f16b70eb // indirect
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
gotest.tools v2.2.0+incompatible
|
gotest.tools v2.2.0+incompatible
|
||||||
|
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7
|
||||||
k8s.io/apimachinery v0.0.0-20190223094358-dcb391cde5ca
|
k8s.io/apimachinery v0.0.0-20190223094358-dcb391cde5ca
|
||||||
k8s.io/client-go v10.0.0+incompatible
|
k8s.io/client-go v10.0.0+incompatible
|
||||||
)
|
)
|
||||||
|
29
go.sum
29
go.sum
@ -68,8 +68,6 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8
|
|||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PagerDuty/go-pagerduty v0.0.0-20191002190746-f60f4fc45222 h1:QBpFoUrMKvjkZCKiLXylgPPK/cZcp8lviB0AzlABlFA=
|
github.com/PagerDuty/go-pagerduty v0.0.0-20191002190746-f60f4fc45222 h1:QBpFoUrMKvjkZCKiLXylgPPK/cZcp8lviB0AzlABlFA=
|
||||||
github.com/PagerDuty/go-pagerduty v0.0.0-20191002190746-f60f4fc45222/go.mod h1:6hH58nzwYc9mw+TPyM1anW0ivbI0ti4lYc+ZBaKmWts=
|
github.com/PagerDuty/go-pagerduty v0.0.0-20191002190746-f60f4fc45222/go.mod h1:6hH58nzwYc9mw+TPyM1anW0ivbI0ti4lYc+ZBaKmWts=
|
||||||
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
|
||||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
|
||||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/SSSaaS/sssa-golang v0.0.0-20170502204618-d37d7782d752 h1:NMpC6M+PtNNDYpq7ozB7kINpv10L5yeli5GJpka2PX8=
|
github.com/SSSaaS/sssa-golang v0.0.0-20170502204618-d37d7782d752 h1:NMpC6M+PtNNDYpq7ozB7kINpv10L5yeli5GJpka2PX8=
|
||||||
@ -84,12 +82,12 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
|
|||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/VictorAvelar/devto-api-go v1.0.0 h1:oXmzye3xYvlgBX18vX4+v6LVbjoihgIokpeOpzeJzqU=
|
github.com/VictorAvelar/devto-api-go v1.0.0 h1:oXmzye3xYvlgBX18vX4+v6LVbjoihgIokpeOpzeJzqU=
|
||||||
github.com/VictorAvelar/devto-api-go v1.0.0/go.mod h1:gX13cqzMdpo49qP8VtBR2uCnzW7d76LFrAVSX2eLifY=
|
github.com/VictorAvelar/devto-api-go v1.0.0/go.mod h1:gX13cqzMdpo49qP8VtBR2uCnzW7d76LFrAVSX2eLifY=
|
||||||
github.com/adlio/trello v1.7.0 h1:syLRJ27wCM8URf7zOBWGr981cG+dpmLSyMqjEoQc+4g=
|
github.com/adlio/trello v1.8.0 h1:VU/1zwzuRzATsFC8WiK4f8R0HHQPWpf2H658KEchsmA=
|
||||||
github.com/adlio/trello v1.7.0/go.mod h1:l2068AhUuUuQ9Vsb95ECMueHThYyAj4e85lWPmr2/LE=
|
github.com/adlio/trello v1.8.0/go.mod h1:l2068AhUuUuQ9Vsb95ECMueHThYyAj4e85lWPmr2/LE=
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||||
github.com/alecthomas/chroma v0.8.0 h1:HS+HE97sgcqjQGu5uVr8jIE55Mmh5UeQ7kckAhHg2pY=
|
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
|
||||||
github.com/alecthomas/chroma v0.8.0/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
|
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
|
||||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
|
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
|
||||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
||||||
github.com/alecthomas/jsonschema v0.0.0-20200123075451-43663a393755/go.mod h1:Juc2PrI3wtNfUwptSvAIeNx+HrETwHQs6nf+TkOJlOA=
|
github.com/alecthomas/jsonschema v0.0.0-20200123075451-43663a393755/go.mod h1:Juc2PrI3wtNfUwptSvAIeNx+HrETwHQs6nf+TkOJlOA=
|
||||||
@ -98,8 +96,6 @@ github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkx
|
|||||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
|
||||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
|
||||||
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
||||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/andygrunwald/go-gerrit v0.0.0-20181207071854-19ef3e9332a4/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
|
github.com/andygrunwald/go-gerrit v0.0.0-20181207071854-19ef3e9332a4/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
|
||||||
@ -149,7 +145,6 @@ github.com/circonus-labs/circonusllhist v0.0.0-20180430145027-5eb751da55c6 h1:Rm
|
|||||||
github.com/circonus-labs/circonusllhist v0.0.0-20180430145027-5eb751da55c6/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.0.0-20180430145027-5eb751da55c6/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
|
||||||
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
|
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
|
||||||
@ -177,8 +172,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/digitalocean/godo v1.42.1 h1:SJ/XMVsp5CZmyQal8gLlOl9jSl1i3FaN20LlgtK5ZMs=
|
github.com/digitalocean/godo v1.46.0 h1:WRbwjATilgz2NE4NGMeSDpeicy9h4xSKNGuRJ/Nq/fA=
|
||||||
github.com/digitalocean/godo v1.42.1/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
|
github.com/digitalocean/godo v1.46.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
|
||||||
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
||||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 h1:Dzuw9GtbmllUqEcoHfScT9YpKFUssSiZ5PgZkIGf/YQ=
|
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 h1:Dzuw9GtbmllUqEcoHfScT9YpKFUssSiZ5PgZkIGf/YQ=
|
||||||
@ -537,8 +532,6 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz
|
|||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mmcdole/gofeed v1.0.0 h1:PHqwr8fsEm8xarj9s53XeEAFYhRM3E9Ib7Ie766/LTE=
|
|
||||||
github.com/mmcdole/gofeed v1.0.0/go.mod h1:tkVcyzS3qVMlQrQxJoEH1hkTiuo9a8emDzkMi7TZBu0=
|
|
||||||
github.com/mmcdole/gofeed v1.1.0 h1:T2WrGLVJRV04PY2qwhEJLHCt9JiCtBhb6SmC8ZvJH08=
|
github.com/mmcdole/gofeed v1.1.0 h1:T2WrGLVJRV04PY2qwhEJLHCt9JiCtBhb6SmC8ZvJH08=
|
||||||
github.com/mmcdole/gofeed v1.1.0/go.mod h1:PPiVwgDXLlz2N83KB4TrIim2lyYM5Zn7ZWH9Pi4oHUk=
|
github.com/mmcdole/gofeed v1.1.0/go.mod h1:PPiVwgDXLlz2N83KB4TrIim2lyYM5Zn7ZWH9Pi4oHUk=
|
||||||
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
|
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
|
||||||
@ -681,8 +674,6 @@ github.com/sethgrid/pester v0.0.0-20171127025028-760f8913c048/go.mod h1:Ad7IjTpv
|
|||||||
github.com/sguiheux/go-coverage v0.0.0-20190710153556-287b082a7197 h1:qu90yDtRE5WEfRT5mn9v0Xz9RaopLguhbPwZKx4dHq8=
|
github.com/sguiheux/go-coverage v0.0.0-20190710153556-287b082a7197 h1:qu90yDtRE5WEfRT5mn9v0Xz9RaopLguhbPwZKx4dHq8=
|
||||||
github.com/sguiheux/go-coverage v0.0.0-20190710153556-287b082a7197/go.mod h1:0hhKrsUsoT7yvxwNGKa+TSYNA26DNWMqReeZEQq/9FI=
|
github.com/sguiheux/go-coverage v0.0.0-20190710153556-287b082a7197/go.mod h1:0hhKrsUsoT7yvxwNGKa+TSYNA26DNWMqReeZEQq/9FI=
|
||||||
github.com/shirou/gopsutil v0.0.0-20170406131756-e49a95f3d5f8/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
github.com/shirou/gopsutil v0.0.0-20170406131756-e49a95f3d5f8/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
github.com/shirou/gopsutil v2.20.7+incompatible h1:Ymv4OD12d6zm+2yONe39VSmp2XooJe8za7ngOLW/o/w=
|
|
||||||
github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
|
||||||
github.com/shirou/gopsutil v2.20.8+incompatible h1:8c7Atn0FAUZJo+f4wYbN0iVpdWniCQk7IYwGtgdh1mY=
|
github.com/shirou/gopsutil v2.20.8+incompatible h1:8c7Atn0FAUZJo+f4wYbN0iVpdWniCQk7IYwGtgdh1mY=
|
||||||
github.com/shirou/gopsutil v2.20.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
github.com/shirou/gopsutil v2.20.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
@ -718,6 +709,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
|
|||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
||||||
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||||
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||||
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||||
github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8 h1:l6epF6yBwuejBfhGkM5m8VSNM/QAm7ApGyH35ehA7eQ=
|
github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8 h1:l6epF6yBwuejBfhGkM5m8VSNM/QAm7ApGyH35ehA7eQ=
|
||||||
github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
|
github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@ -753,8 +746,8 @@ github.com/wtfutil/spotigopher v0.0.0-20191127141047-7d8168fe103a/go.mod h1:AlO4
|
|||||||
github.com/wtfutil/todoist v0.0.2-0.20191216004217-0ec29ceda61a h1:nD8ALd4TSo+zPHK5MqQWFj01G8fMMHFfC3rWvoq/9JA=
|
github.com/wtfutil/todoist v0.0.2-0.20191216004217-0ec29ceda61a h1:nD8ALd4TSo+zPHK5MqQWFj01G8fMMHFfC3rWvoq/9JA=
|
||||||
github.com/wtfutil/todoist v0.0.2-0.20191216004217-0ec29ceda61a/go.mod h1:YuuGLJSsTK6DGBD5Zaf3J8LSMfpEC2WtzYPey3XVOdI=
|
github.com/wtfutil/todoist v0.0.2-0.20191216004217-0ec29ceda61a/go.mod h1:YuuGLJSsTK6DGBD5Zaf3J8LSMfpEC2WtzYPey3XVOdI=
|
||||||
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
||||||
github.com/xanzy/go-gitlab v0.33.0 h1:MUJZknbLhVXSFzBA5eqGGhQ2yHSu8tPbGBPeB3sN4B0=
|
github.com/xanzy/go-gitlab v0.38.1 h1:st5/Ag4h8CqVfp3LpOWW0Jd4jYHTGETwu0KksYDPnYE=
|
||||||
github.com/xanzy/go-gitlab v0.33.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
github.com/xanzy/go-gitlab v0.38.1/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
@ -1139,6 +1132,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
|||||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:mub0MmFLOn8XLikZOAhgLD1kXJq8jgftSrrv7m00xFo=
|
||||||
|
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:OxvTsCwKosqQ1q7B+8FwXqg4rKZ/UG9dUW+g/VL2xH4=
|
||||||
k8s.io/api v0.0.0-20181204000039-89a74a8d264d h1:HQoGWsWUe/FmRcX9BU440AAMnzBFEf+DBo4nbkQlNzs=
|
k8s.io/api v0.0.0-20181204000039-89a74a8d264d h1:HQoGWsWUe/FmRcX9BU440AAMnzBFEf+DBo4nbkQlNzs=
|
||||||
k8s.io/api v0.0.0-20181204000039-89a74a8d264d/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
k8s.io/api v0.0.0-20181204000039-89a74a8d264d/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||||
k8s.io/apimachinery v0.0.0-20190223094358-dcb391cde5ca h1:rOczZwIfERvL7YLSQ96uI3NUYamzdK/MmSDIfChQRBk=
|
k8s.io/apimachinery v0.0.0-20190223094358-dcb391cde5ca h1:rOczZwIfERvL7YLSQ96uI3NUYamzdK/MmSDIfChQRBk=
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package clocks
|
package clocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,6 +19,14 @@ func NewClock(label string, timeLoc *time.Location) Clock {
|
|||||||
return clock
|
return clock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildClock(label string, location string) (clock Clock, err error) {
|
||||||
|
timeLoc, err := time.LoadLocation(sanitizeLocation(location))
|
||||||
|
if err != nil {
|
||||||
|
return Clock{}, err
|
||||||
|
}
|
||||||
|
return NewClock(label, timeLoc), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (clock *Clock) Date(dateFormat string) string {
|
func (clock *Clock) Date(dateFormat string) string {
|
||||||
return clock.LocalTime().Format(dateFormat)
|
return clock.LocalTime().Format(dateFormat)
|
||||||
}
|
}
|
||||||
@ -33,3 +42,7 @@ func (clock *Clock) ToLocal(t time.Time) time.Time {
|
|||||||
func (clock *Clock) Time(timeFormat string) string {
|
func (clock *Clock) Time(timeFormat string) string {
|
||||||
return clock.LocalTime().Format(timeFormat)
|
return clock.LocalTime().Format(timeFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sanitizeLocation(locStr string) string {
|
||||||
|
return strings.Replace(locStr, " ", "_", -1)
|
||||||
|
}
|
||||||
|
@ -10,7 +10,9 @@ type ClockCollection struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (clocks *ClockCollection) Sorted(sortOrder string) []Clock {
|
func (clocks *ClockCollection) Sorted(sortOrder string) []Clock {
|
||||||
if sortOrder == "chronological" {
|
if sortOrder == "natural" {
|
||||||
|
//no-op
|
||||||
|
} else if sortOrder == "chronological" {
|
||||||
clocks.SortedChronologically()
|
clocks.SortedChronologically()
|
||||||
} else {
|
} else {
|
||||||
clocks.SortedAlphabetically()
|
clocks.SortedAlphabetically()
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package clocks
|
package clocks
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat string) {
|
func (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat string) {
|
||||||
str := ""
|
str := ""
|
||||||
|
|
||||||
if len(clocks) == 0 {
|
if len(clocks) == 0 {
|
||||||
str = fmt.Sprintf("\n%s", " no timezone data available")
|
str = fmt.Sprintf("\n%s", " no timezone data available")
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
for idx, clock := range clocks {
|
for idx, clock := range clocks {
|
||||||
str += fmt.Sprintf(
|
str += fmt.Sprintf(
|
||||||
" [%s]%-12s %-10s %7s[white]\n",
|
" [%s]%-12s %-10s %7s[white]\n",
|
||||||
|
@ -17,8 +17,8 @@ type Settings struct {
|
|||||||
|
|
||||||
dateFormat string `help:"The format of the date string for all clocks." values:"Any valid Go date layout which is handled by Time.Format. Defaults to Jan 2."`
|
dateFormat string `help:"The format of the date string for all clocks." values:"Any valid Go date layout which is handled by Time.Format. Defaults to Jan 2."`
|
||||||
timeFormat string `help:"The format of the time string for all clocks." values:"Any valid Go time layout which is handled by Time.Format. Defaults to 15:04 MST."`
|
timeFormat string `help:"The format of the time string for all clocks." values:"Any valid Go time layout which is handled by Time.Format. Defaults to 15:04 MST."`
|
||||||
locations map[string]interface{} `help:"Defines the timezones for the world clocks that you want to display. key is a unique label that will be displayed in the UI. value is a timezone name." values:"Any TZ database timezone."`
|
locations []Clock `help:"Defines the timezones for the world clocks that you want to display. key is a unique label that will be displayed in the UI. value is a timezone name." values:"Any TZ database timezone."`
|
||||||
sort string `help:"Defines the display order of the clocks in the widget." values:"'alphabetical' or 'chronological'. 'alphabetical' will sort in acending order by key, 'chronological' will sort in ascending order by date/time."`
|
sort string `help:"Defines the display order of the clocks in the widget." values:"'alphabetical', 'chronological', or 'natural. 'alphabetical' will sort in ascending order by key, 'chronological' will sort in ascending order by date/time, 'natural' will keep ordering as per the config."`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSettingsFromYAML creates a new settings instance from a YAML config block
|
// NewSettingsFromYAML creates a new settings instance from a YAML config block
|
||||||
@ -28,9 +28,40 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co
|
|||||||
|
|
||||||
dateFormat: ymlConfig.UString("dateFormat", utils.SimpleDateFormat),
|
dateFormat: ymlConfig.UString("dateFormat", utils.SimpleDateFormat),
|
||||||
timeFormat: ymlConfig.UString("timeFormat", utils.SimpleTimeFormat),
|
timeFormat: ymlConfig.UString("timeFormat", utils.SimpleTimeFormat),
|
||||||
locations: ymlConfig.UMap("locations"),
|
locations: buildLocations(ymlConfig),
|
||||||
sort: ymlConfig.UString("sort"),
|
sort: ymlConfig.UString("sort"),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &settings
|
return &settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildLocations(ymlConfig *config.Config) []Clock {
|
||||||
|
clocks := []Clock{}
|
||||||
|
locations, err := ymlConfig.Map("locations")
|
||||||
|
if err == nil {
|
||||||
|
for k, v := range locations {
|
||||||
|
name := k
|
||||||
|
zone := v.(string)
|
||||||
|
clock, err := BuildClock(name, zone)
|
||||||
|
if err == nil {
|
||||||
|
clocks = append(clocks, clock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clocks
|
||||||
|
}
|
||||||
|
|
||||||
|
listLocations := ymlConfig.UList("locations")
|
||||||
|
for _, location := range listLocations {
|
||||||
|
if location, ok := location.(map[string]interface{}); ok {
|
||||||
|
for k, v := range location {
|
||||||
|
name := k
|
||||||
|
zone := v.(string)
|
||||||
|
clock, err := BuildClock(name, zone)
|
||||||
|
if err == nil {
|
||||||
|
clocks = append(clocks, clock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clocks
|
||||||
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package clocks
|
package clocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
"github.com/wtfutil/wtf/view"
|
"github.com/wtfutil/wtf/view"
|
||||||
)
|
)
|
||||||
@ -28,7 +25,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
|
|||||||
timeFormat: settings.timeFormat,
|
timeFormat: settings.timeFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.clockColl = widget.buildClockCollection(settings.locations)
|
widget.clockColl = widget.buildClockCollection()
|
||||||
|
|
||||||
return &widget
|
return &widget
|
||||||
}
|
}
|
||||||
@ -45,21 +42,10 @@ func (widget *Widget) Refresh() {
|
|||||||
|
|
||||||
/* -------------------- Unexported Functions -------------------- */
|
/* -------------------- Unexported Functions -------------------- */
|
||||||
|
|
||||||
func (widget *Widget) buildClockCollection(locData map[string]interface{}) ClockCollection {
|
func (widget *Widget) buildClockCollection() ClockCollection {
|
||||||
clockColl := ClockCollection{}
|
clockColl := ClockCollection{}
|
||||||
|
|
||||||
for label, locStr := range locData {
|
clockColl.Clocks = widget.settings.locations
|
||||||
timeLoc, err := time.LoadLocation(widget.sanitizeLocation(locStr.(string)))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
clockColl.Clocks = append(clockColl.Clocks, NewClock(label, timeLoc))
|
|
||||||
}
|
|
||||||
|
|
||||||
return clockColl
|
return clockColl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) sanitizeLocation(locStr string) string {
|
|
||||||
return strings.Replace(locStr, " ", "_", -1)
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ package exchangerates
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
"github.com/wtfutil/wtf/view"
|
"github.com/wtfutil/wtf/view"
|
||||||
@ -56,15 +57,26 @@ func (widget *Widget) content() (string, string, bool) {
|
|||||||
return widget.CommonSettings().Title, widget.err.Error(), false
|
return widget.CommonSettings().Title, widget.err.Error(), false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the bases alphabetically to ensure consistent display ordering
|
||||||
|
bases := []string{}
|
||||||
|
for base := range widget.settings.rates {
|
||||||
|
bases = append(bases, base)
|
||||||
|
}
|
||||||
|
sort.Strings(bases)
|
||||||
|
|
||||||
out := ""
|
out := ""
|
||||||
idx := 0
|
|
||||||
for base, rates := range widget.settings.rates {
|
for idx, base := range bases {
|
||||||
|
rates := widget.settings.rates[base]
|
||||||
|
|
||||||
|
rowColor := widget.CommonSettings().RowColor(idx)
|
||||||
|
|
||||||
for _, cur := range rates {
|
for _, cur := range rates {
|
||||||
rate := widget.rates[base][cur]
|
rate := widget.rates[base][cur]
|
||||||
|
|
||||||
out += fmt.Sprintf(
|
out += fmt.Sprintf(
|
||||||
"[%s]1 %s = %s %s[white]\n",
|
"[%s]1 %s = %s %s[white]\n",
|
||||||
widget.CommonSettings().RowColor(idx),
|
rowColor,
|
||||||
base,
|
base,
|
||||||
widget.formatConversionRate(rate),
|
widget.formatConversionRate(rate),
|
||||||
cur,
|
cur,
|
||||||
|
@ -8,6 +8,7 @@ func (widget *Widget) initializeKeyboardControls() {
|
|||||||
widget.SetKeyboardChar("j", widget.Next, "Select next item")
|
widget.SetKeyboardChar("j", widget.Next, "Select next item")
|
||||||
widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
|
widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
|
||||||
widget.SetKeyboardChar("o", widget.openStory, "Open story in browser")
|
widget.SetKeyboardChar("o", widget.openStory, "Open story in browser")
|
||||||
|
widget.SetKeyboardChar("t", widget.toggleDisplayText, "Toggle display between title, link and title+content")
|
||||||
|
|
||||||
widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
|
widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
|
||||||
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
|
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
|
||||||
|
@ -3,11 +3,21 @@ package feedreader
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mmcdole/gofeed"
|
"github.com/mmcdole/gofeed"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
"github.com/wtfutil/wtf/utils"
|
"github.com/wtfutil/wtf/utils"
|
||||||
"github.com/wtfutil/wtf/view"
|
"github.com/wtfutil/wtf/view"
|
||||||
|
"jaytaylor.com/html2text"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShowType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SHOW_TITLE ShowType = iota
|
||||||
|
SHOW_LINK
|
||||||
|
SHOW_CONTENT
|
||||||
)
|
)
|
||||||
|
|
||||||
// FeedItem represents an item returned from an RSS or Atom feed
|
// FeedItem represents an item returned from an RSS or Atom feed
|
||||||
@ -25,6 +35,32 @@ type Widget struct {
|
|||||||
parser *gofeed.Parser
|
parser *gofeed.Parser
|
||||||
settings *Settings
|
settings *Settings
|
||||||
err error
|
err error
|
||||||
|
showType ShowType
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateShowType(showtype ShowType) ShowType {
|
||||||
|
returnValue := SHOW_TITLE
|
||||||
|
switch showtype {
|
||||||
|
case SHOW_TITLE:
|
||||||
|
returnValue = SHOW_LINK
|
||||||
|
case SHOW_LINK:
|
||||||
|
returnValue = SHOW_CONTENT
|
||||||
|
case SHOW_CONTENT:
|
||||||
|
returnValue = SHOW_TITLE
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func getShowText(feedItem *FeedItem, showType ShowType) string {
|
||||||
|
returnValue := feedItem.item.Title
|
||||||
|
switch showType {
|
||||||
|
case SHOW_LINK:
|
||||||
|
returnValue = feedItem.item.Link
|
||||||
|
case SHOW_CONTENT:
|
||||||
|
text, _ := html2text.FromString(feedItem.item.Content, html2text.Options{PrettyTables: true})
|
||||||
|
returnValue = strings.TrimSpace(feedItem.item.Title + "\n" + strings.TrimSpace(text))
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWidget creates a new instance of a widget
|
// NewWidget creates a new instance of a widget
|
||||||
@ -35,6 +71,7 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
|
|||||||
|
|
||||||
parser: gofeed.NewParser(),
|
parser: gofeed.NewParser(),
|
||||||
settings: settings,
|
settings: settings,
|
||||||
|
showType: SHOW_TITLE,
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.SetRenderFunction(widget.Render)
|
widget.SetRenderFunction(widget.Render)
|
||||||
@ -137,11 +174,13 @@ func (widget *Widget) content() (string, string, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayText := getShowText(feedItem, widget.showType)
|
||||||
|
|
||||||
row := fmt.Sprintf(
|
row := fmt.Sprintf(
|
||||||
"[%s]%2d. %s[white]",
|
"[%s]%2d. %s[white]",
|
||||||
rowColor,
|
rowColor,
|
||||||
idx+1,
|
idx+1,
|
||||||
feedItem.item.Title,
|
displayText,
|
||||||
)
|
)
|
||||||
|
|
||||||
str += utils.HighlightableHelper(widget.View, row, idx, len(feedItem.item.Title))
|
str += utils.HighlightableHelper(widget.View, row, idx, len(feedItem.item.Title))
|
||||||
@ -153,7 +192,9 @@ func (widget *Widget) content() (string, string, bool) {
|
|||||||
// feedItems are sorted by published date
|
// feedItems are sorted by published date
|
||||||
func (widget *Widget) sort(feedItems []*FeedItem) []*FeedItem {
|
func (widget *Widget) sort(feedItems []*FeedItem) []*FeedItem {
|
||||||
sort.Slice(feedItems, func(i, j int) bool {
|
sort.Slice(feedItems, func(i, j int) bool {
|
||||||
return feedItems[i].item.PublishedParsed.After(*feedItems[j].item.PublishedParsed)
|
return feedItems[i].item.PublishedParsed != nil &&
|
||||||
|
feedItems[j].item.PublishedParsed != nil &&
|
||||||
|
feedItems[i].item.PublishedParsed.After(*feedItems[j].item.PublishedParsed)
|
||||||
})
|
})
|
||||||
|
|
||||||
return feedItems
|
return feedItems
|
||||||
@ -169,3 +210,8 @@ func (widget *Widget) openStory() {
|
|||||||
utils.OpenFile(story.item.Link)
|
utils.OpenFile(story.item.Link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (widget *Widget) toggleDisplayText() {
|
||||||
|
widget.showType = rotateShowType(widget.showType)
|
||||||
|
widget.Render()
|
||||||
|
}
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/go-github/v26/github"
|
"github.com/google/go-github/v26/github"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
"golang.org/x/text/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (widget *Widget) display() {
|
func (widget *Widget) display() {
|
||||||
@ -54,7 +56,7 @@ func (widget *Widget) content() (string, string, bool) {
|
|||||||
return title, str, false
|
return title, str, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) displayMyPullRequests(repo *GithubRepo, username string) string {
|
func (widget *Widget) displayMyPullRequests(repo *Repo, username string) string {
|
||||||
prs := repo.myPullRequests(username, widget.settings.enableStatus)
|
prs := repo.myPullRequests(username, widget.settings.enableStatus)
|
||||||
|
|
||||||
prLength := len(prs)
|
prLength := len(prs)
|
||||||
@ -77,7 +79,7 @@ func (widget *Widget) displayMyPullRequests(repo *GithubRepo, username string) s
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) displayCustomQuery(repo *GithubRepo, filter string, perPage int) string {
|
func (widget *Widget) displayCustomQuery(repo *Repo, filter string, perPage int) string {
|
||||||
res := repo.customIssueQuery(filter, perPage)
|
res := repo.customIssueQuery(filter, perPage)
|
||||||
|
|
||||||
if res == nil {
|
if res == nil {
|
||||||
@ -104,7 +106,7 @@ func (widget *Widget) displayCustomQuery(repo *GithubRepo, filter string, perPag
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) displayMyReviewRequests(repo *GithubRepo, username string) string {
|
func (widget *Widget) displayMyReviewRequests(repo *Repo, username string) string {
|
||||||
prs := repo.myReviewRequests(username)
|
prs := repo.myReviewRequests(username)
|
||||||
|
|
||||||
if len(prs) == 0 {
|
if len(prs) == 0 {
|
||||||
@ -121,18 +123,20 @@ func (widget *Widget) displayMyReviewRequests(repo *GithubRepo, username string)
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) displayStats(repo *GithubRepo) string {
|
func (widget *Widget) displayStats(repo *Repo) string {
|
||||||
|
prntr := message.NewPrinter(language.English)
|
||||||
|
|
||||||
str := fmt.Sprintf(
|
str := fmt.Sprintf(
|
||||||
" PRs: %d Issues: %d Stars: %d\n",
|
" PRs: %s Issues: %s Stars: %s\n",
|
||||||
repo.PullRequestCount(),
|
prntr.Sprintf("%d", repo.PullRequestCount()),
|
||||||
repo.IssueCount(),
|
prntr.Sprintf("%d", repo.IssueCount()),
|
||||||
repo.StarCount(),
|
prntr.Sprintf("%d", repo.StarCount()),
|
||||||
)
|
)
|
||||||
|
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) title(repo *GithubRepo) string {
|
func (widget *Widget) title(repo *Repo) string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"[%s]%s - %s[white]",
|
"[%s]%s - %s[white]",
|
||||||
widget.settings.common.Colors.TextTheme.Title,
|
widget.settings.common.Colors.TextTheme.Title,
|
||||||
|
@ -15,8 +15,8 @@ const (
|
|||||||
issuesPath = "/issues"
|
issuesPath = "/issues"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GithubRepo defines a new GithubRepo structure
|
// Repo defines a new GitHub Repo structure
|
||||||
type GithubRepo struct {
|
type Repo struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
baseURL string
|
baseURL string
|
||||||
uploadURL string
|
uploadURL string
|
||||||
@ -29,8 +29,8 @@ type GithubRepo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGithubRepo returns a new Github Repo with a name, owner, apiKey, baseURL and uploadURL
|
// NewGithubRepo returns a new Github Repo with a name, owner, apiKey, baseURL and uploadURL
|
||||||
func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *GithubRepo {
|
func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *Repo {
|
||||||
repo := GithubRepo{
|
repo := Repo{
|
||||||
Name: name,
|
Name: name,
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
|
|
||||||
@ -43,22 +43,22 @@ func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *GithubRepo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open will open the GitHub Repo URL using the utils helper
|
// Open will open the GitHub Repo URL using the utils helper
|
||||||
func (repo *GithubRepo) Open() {
|
func (repo *Repo) Open() {
|
||||||
utils.OpenFile(*repo.RemoteRepo.HTMLURL)
|
utils.OpenFile(*repo.RemoteRepo.HTMLURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open will open the GitHub Pull Requests URL using the utils helper
|
// OpenPulls will open the GitHub Pull Requests URL using the utils helper
|
||||||
func (repo *GithubRepo) OpenPulls() {
|
func (repo *Repo) OpenPulls() {
|
||||||
utils.OpenFile(*repo.RemoteRepo.HTMLURL + pullRequestsPath)
|
utils.OpenFile(*repo.RemoteRepo.HTMLURL + pullRequestsPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open will open the GitHub Issues URL using the utils helper
|
// OpenIssues will open the GitHub Issues URL using the utils helper
|
||||||
func (repo *GithubRepo) OpenIssues() {
|
func (repo *Repo) OpenIssues() {
|
||||||
utils.OpenFile(*repo.RemoteRepo.HTMLURL + issuesPath)
|
utils.OpenFile(*repo.RemoteRepo.HTMLURL + issuesPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh reloads the github data via the Github API
|
// Refresh reloads the github data via the Github API
|
||||||
func (repo *GithubRepo) Refresh() {
|
func (repo *Repo) Refresh() {
|
||||||
prs, err := repo.loadPullRequests()
|
prs, err := repo.loadPullRequests()
|
||||||
repo.Err = err
|
repo.Err = err
|
||||||
repo.PullRequests = prs
|
repo.PullRequests = prs
|
||||||
@ -73,7 +73,7 @@ func (repo *GithubRepo) Refresh() {
|
|||||||
/* -------------------- Counts -------------------- */
|
/* -------------------- Counts -------------------- */
|
||||||
|
|
||||||
// IssueCount return the total amount of issues as an int
|
// IssueCount return the total amount of issues as an int
|
||||||
func (repo *GithubRepo) IssueCount() int {
|
func (repo *Repo) IssueCount() int {
|
||||||
if repo.RemoteRepo == nil {
|
if repo.RemoteRepo == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -84,12 +84,12 @@ func (repo *GithubRepo) IssueCount() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PullRequestCount returns the total amount of pull requests as an int
|
// PullRequestCount returns the total amount of pull requests as an int
|
||||||
func (repo *GithubRepo) PullRequestCount() int {
|
func (repo *Repo) PullRequestCount() int {
|
||||||
return len(repo.PullRequests)
|
return len(repo.PullRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StarCount returns the total amount of stars this repo has gained as an int
|
// StarCount returns the total amount of stars this repo has gained as an int
|
||||||
func (repo *GithubRepo) StarCount() int {
|
func (repo *Repo) StarCount() int {
|
||||||
if repo.RemoteRepo == nil {
|
if repo.RemoteRepo == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ func (repo *GithubRepo) StarCount() int {
|
|||||||
|
|
||||||
/* -------------------- Unexported Functions -------------------- */
|
/* -------------------- Unexported Functions -------------------- */
|
||||||
|
|
||||||
func (repo *GithubRepo) isGitHubEnterprise() bool {
|
func (repo *Repo) isGitHubEnterprise() bool {
|
||||||
if len(repo.baseURL) > 0 {
|
if len(repo.baseURL) > 0 {
|
||||||
if len(repo.uploadURL) == 0 {
|
if len(repo.uploadURL) == 0 {
|
||||||
repo.uploadURL = repo.baseURL
|
repo.uploadURL = repo.baseURL
|
||||||
@ -109,7 +109,7 @@ func (repo *GithubRepo) isGitHubEnterprise() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *GithubRepo) oauthClient() *http.Client {
|
func (repo *Repo) oauthClient() *http.Client {
|
||||||
tokenService := oauth2.StaticTokenSource(
|
tokenService := oauth2.StaticTokenSource(
|
||||||
&oauth2.Token{AccessToken: repo.apiKey},
|
&oauth2.Token{AccessToken: repo.apiKey},
|
||||||
)
|
)
|
||||||
@ -117,7 +117,7 @@ func (repo *GithubRepo) oauthClient() *http.Client {
|
|||||||
return oauth2.NewClient(context.Background(), tokenService)
|
return oauth2.NewClient(context.Background(), tokenService)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *GithubRepo) githubClient() (*ghb.Client, error) {
|
func (repo *Repo) githubClient() (*ghb.Client, error) {
|
||||||
oauthClient := repo.oauthClient()
|
oauthClient := repo.oauthClient()
|
||||||
|
|
||||||
if repo.isGitHubEnterprise() {
|
if repo.isGitHubEnterprise() {
|
||||||
@ -128,7 +128,7 @@ func (repo *GithubRepo) githubClient() (*ghb.Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// myPullRequests returns a list of pull requests created by username on this repo
|
// myPullRequests returns a list of pull requests created by username on this repo
|
||||||
func (repo *GithubRepo) myPullRequests(username string, showStatus bool) []*ghb.PullRequest {
|
func (repo *Repo) myPullRequests(username string, showStatus bool) []*ghb.PullRequest {
|
||||||
prs := []*ghb.PullRequest{}
|
prs := []*ghb.PullRequest{}
|
||||||
|
|
||||||
for _, pr := range repo.PullRequests {
|
for _, pr := range repo.PullRequests {
|
||||||
@ -149,7 +149,7 @@ func (repo *GithubRepo) myPullRequests(username string, showStatus bool) []*ghb.
|
|||||||
// individualPRs takes a list of pull requests (presumably returned from
|
// individualPRs takes a list of pull requests (presumably returned from
|
||||||
// github.PullRequests.List) and fetches them individually to get more detailed
|
// github.PullRequests.List) and fetches them individually to get more detailed
|
||||||
// status info on each. see: https://developer.github.com/v3/git/#checking-mergeability-of-pull-requests
|
// status info on each. see: https://developer.github.com/v3/git/#checking-mergeability-of-pull-requests
|
||||||
func (repo *GithubRepo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullRequest {
|
func (repo *Repo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullRequest {
|
||||||
github, err := repo.githubClient()
|
github, err := repo.githubClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return prs
|
return prs
|
||||||
@ -170,7 +170,7 @@ func (repo *GithubRepo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullRequest
|
|||||||
|
|
||||||
// myReviewRequests returns a list of pull requests for which username has been
|
// myReviewRequests returns a list of pull requests for which username has been
|
||||||
// requested to do a code review
|
// requested to do a code review
|
||||||
func (repo *GithubRepo) myReviewRequests(username string) []*ghb.PullRequest {
|
func (repo *Repo) myReviewRequests(username string) []*ghb.PullRequest {
|
||||||
prs := []*ghb.PullRequest{}
|
prs := []*ghb.PullRequest{}
|
||||||
|
|
||||||
for _, pr := range repo.PullRequests {
|
for _, pr := range repo.PullRequests {
|
||||||
@ -184,7 +184,7 @@ func (repo *GithubRepo) myReviewRequests(username string) []*ghb.PullRequest {
|
|||||||
return prs
|
return prs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *GithubRepo) customIssueQuery(filter string, perPage int) *ghb.IssuesSearchResult {
|
func (repo *Repo) customIssueQuery(filter string, perPage int) *ghb.IssuesSearchResult {
|
||||||
github, err := repo.githubClient()
|
github, err := repo.githubClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -199,7 +199,7 @@ func (repo *GithubRepo) customIssueQuery(filter string, perPage int) *ghb.Issues
|
|||||||
return prs
|
return prs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *GithubRepo) loadPullRequests() ([]*ghb.PullRequest, error) {
|
func (repo *Repo) loadPullRequests() ([]*ghb.PullRequest, error) {
|
||||||
github, err := repo.githubClient()
|
github, err := repo.githubClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -217,7 +217,7 @@ func (repo *GithubRepo) loadPullRequests() ([]*ghb.PullRequest, error) {
|
|||||||
return prs, nil
|
return prs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *GithubRepo) loadRemoteRepository() (*ghb.Repository, error) {
|
func (repo *Repo) loadRemoteRepository() (*ghb.Repository, error) {
|
||||||
github, err := repo.githubClient()
|
github, err := repo.githubClient()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -15,7 +15,7 @@ type Widget struct {
|
|||||||
view.KeyboardWidget
|
view.KeyboardWidget
|
||||||
view.TextWidget
|
view.TextWidget
|
||||||
|
|
||||||
GithubRepos []*GithubRepo
|
GithubRepos []*Repo
|
||||||
|
|
||||||
settings *Settings
|
settings *Settings
|
||||||
Selected int
|
Selected int
|
||||||
@ -110,8 +110,8 @@ func (widget *Widget) HelpText() string {
|
|||||||
|
|
||||||
/* -------------------- Unexported Functions -------------------- */
|
/* -------------------- Unexported Functions -------------------- */
|
||||||
|
|
||||||
func (widget *Widget) buildRepoCollection(repoData []string) []*GithubRepo {
|
func (widget *Widget) buildRepoCollection(repoData []string) []*Repo {
|
||||||
githubRepos := []*GithubRepo{}
|
githubRepos := []*Repo{}
|
||||||
|
|
||||||
for _, repo := range repoData {
|
for _, repo := range repoData {
|
||||||
split := strings.Split(repo, "/")
|
split := strings.Split(repo, "/")
|
||||||
@ -130,7 +130,7 @@ func (widget *Widget) buildRepoCollection(repoData []string) []*GithubRepo {
|
|||||||
return githubRepos
|
return githubRepos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) currentGithubRepo() *GithubRepo {
|
func (widget *Widget) currentGithubRepo() *Repo {
|
||||||
if len(widget.GithubRepos) == 0 {
|
if len(widget.GithubRepos) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
16
modules/todo_plus/backend/backend.go
Normal file
16
modules/todo_plus/backend/backend.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/olebedev/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Backend interface {
|
||||||
|
Title() string
|
||||||
|
Setup(*config.Config)
|
||||||
|
BuildProjects() []*Project
|
||||||
|
GetProject(string) *Project
|
||||||
|
LoadTasks(string) ([]Task, error)
|
||||||
|
CloseTask(*Task) error
|
||||||
|
DeleteTask(*Task) error
|
||||||
|
Sources() []string
|
||||||
|
}
|
66
modules/todo_plus/backend/project.go
Normal file
66
modules/todo_plus/backend/project.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
type Task struct {
|
||||||
|
ID string
|
||||||
|
Completed bool
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Project struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
|
||||||
|
Index int
|
||||||
|
Tasks []Task
|
||||||
|
Err error
|
||||||
|
backend Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proj *Project) IsLast() bool {
|
||||||
|
return proj.Index >= len(proj.Tasks)-1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proj *Project) loadTasks() {
|
||||||
|
Tasks, err := proj.backend.LoadTasks(proj.ID)
|
||||||
|
proj.Err = err
|
||||||
|
proj.Tasks = Tasks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proj *Project) LongestLine() int {
|
||||||
|
maxLen := 0
|
||||||
|
|
||||||
|
for _, task := range proj.Tasks {
|
||||||
|
if len(task.Name) > maxLen {
|
||||||
|
maxLen = len(task.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proj *Project) currentTask() *Task {
|
||||||
|
if proj.Index < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proj.Tasks[proj.Index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proj *Project) CloseSelectedTask() {
|
||||||
|
currTask := proj.currentTask()
|
||||||
|
|
||||||
|
if currTask != nil {
|
||||||
|
_ = proj.backend.CloseTask(currTask)
|
||||||
|
proj.loadTasks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proj *Project) DeleteSelectedTask() {
|
||||||
|
currTask := proj.currentTask()
|
||||||
|
|
||||||
|
if currTask != nil {
|
||||||
|
_ = proj.backend.DeleteTask(currTask)
|
||||||
|
|
||||||
|
proj.loadTasks()
|
||||||
|
}
|
||||||
|
}
|
108
modules/todo_plus/backend/todoist.go
Normal file
108
modules/todo_plus/backend/todoist.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/olebedev/config"
|
||||||
|
"github.com/wtfutil/todoist"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Todoist struct {
|
||||||
|
projects []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) Title() string {
|
||||||
|
return "Todoist"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) Setup(config *config.Config) {
|
||||||
|
todoist.Token = config.UString("apiKey")
|
||||||
|
todo.projects = config.UList("projects")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) BuildProjects() []*Project {
|
||||||
|
projects := []*Project{}
|
||||||
|
|
||||||
|
for _, id := range todo.projects {
|
||||||
|
i := strconv.Itoa(id.(int))
|
||||||
|
proj := todo.GetProject(i)
|
||||||
|
projects = append(projects, proj)
|
||||||
|
}
|
||||||
|
return projects
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) GetProject(id string) *Project {
|
||||||
|
// Todoist seems to experience a lot of network issues on their side
|
||||||
|
// If we can't connect, handle it with an empty project until we can
|
||||||
|
proj := &Project{
|
||||||
|
Index: -1,
|
||||||
|
backend: todo,
|
||||||
|
}
|
||||||
|
i64, _ := strconv.ParseUint(id, 10, 32)
|
||||||
|
i := uint(i64)
|
||||||
|
project, err := todoist.GetProject(i)
|
||||||
|
if err != nil {
|
||||||
|
proj.Err = err
|
||||||
|
return proj
|
||||||
|
}
|
||||||
|
|
||||||
|
proj.ID = strconv.FormatUint(uint64(project.ID), 10)
|
||||||
|
proj.Name = project.Name
|
||||||
|
|
||||||
|
tasks, err := todo.LoadTasks(proj.ID)
|
||||||
|
proj.Err = err
|
||||||
|
proj.Tasks = tasks
|
||||||
|
|
||||||
|
return proj
|
||||||
|
}
|
||||||
|
|
||||||
|
func toTask(task todoist.Task) Task {
|
||||||
|
id := strconv.FormatUint(uint64(task.ID), 10)
|
||||||
|
return Task{
|
||||||
|
ID: id,
|
||||||
|
Completed: task.Completed,
|
||||||
|
Name: task.Content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) LoadTasks(id string) ([]Task, error) {
|
||||||
|
tasks, err := todoist.ListTask(todoist.QueryParam{"project_id": id})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var finalTasks []Task
|
||||||
|
for _, item := range tasks {
|
||||||
|
finalTasks = append(finalTasks, toTask(item))
|
||||||
|
}
|
||||||
|
return finalTasks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) CloseTask(task *Task) error {
|
||||||
|
if task != nil {
|
||||||
|
i64, _ := strconv.ParseUint(task.ID, 10, 32)
|
||||||
|
i := uint(i64)
|
||||||
|
internal := todoist.Task{ID: i}
|
||||||
|
return internal.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) DeleteTask(task *Task) error {
|
||||||
|
if task != nil {
|
||||||
|
i64, _ := strconv.ParseUint(task.ID, 10, 32)
|
||||||
|
i := uint(i64)
|
||||||
|
internal := todoist.Task{ID: i}
|
||||||
|
return internal.Delete()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Todoist) Sources() []string {
|
||||||
|
var result []string
|
||||||
|
for _, id := range todo.projects {
|
||||||
|
i := strconv.Itoa(id.(int))
|
||||||
|
result = append(result, i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
170
modules/todo_plus/backend/trello.go
Normal file
170
modules/todo_plus/backend/trello.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/adlio/trello"
|
||||||
|
"github.com/olebedev/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Trello struct {
|
||||||
|
username string
|
||||||
|
boardName string
|
||||||
|
client *trello.Client
|
||||||
|
board string
|
||||||
|
projects []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) Title() string {
|
||||||
|
return "Trello"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) Setup(config *config.Config) {
|
||||||
|
todo.username = config.UString("username")
|
||||||
|
todo.boardName = config.UString("board")
|
||||||
|
todo.client = trello.NewClient(
|
||||||
|
config.UString("apiKey"),
|
||||||
|
config.UString("accessToken"),
|
||||||
|
)
|
||||||
|
board, err := getBoardID(todo.client, todo.username, todo.boardName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
todo.board = board
|
||||||
|
todo.projects = config.UList("lists")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBoardID(client *trello.Client, username, boardName string) (string, error) {
|
||||||
|
member, err := client.GetMember(username, trello.Defaults())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
boards, err := member.GetBoards(trello.Defaults())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, board := range boards {
|
||||||
|
if board.Name == boardName {
|
||||||
|
return board.ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("could not find board with name %s", boardName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListId(client *trello.Client, boardID string, listName string) (string, error) {
|
||||||
|
board, err := client.GetBoard(boardID, trello.Defaults())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
boardLists, err := board.GetLists(trello.Defaults())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, list := range boardLists {
|
||||||
|
if list.Name == listName {
|
||||||
|
return list.ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCardsOnList(client *trello.Client, listID string) ([]*trello.Card, error) {
|
||||||
|
list, err := client.GetList(listID, trello.Defaults())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cards, err := list.GetCards(trello.Defaults())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cards, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) BuildProjects() []*Project {
|
||||||
|
projects := []*Project{}
|
||||||
|
|
||||||
|
for _, id := range todo.projects {
|
||||||
|
proj := todo.GetProject(id.(string))
|
||||||
|
projects = append(projects, proj)
|
||||||
|
}
|
||||||
|
return projects
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) GetProject(id string) *Project {
|
||||||
|
proj := &Project{
|
||||||
|
Index: -1,
|
||||||
|
backend: todo,
|
||||||
|
}
|
||||||
|
|
||||||
|
listId, err := getListId(todo.client, todo.board, id)
|
||||||
|
if err != nil {
|
||||||
|
proj.Err = err
|
||||||
|
return proj
|
||||||
|
}
|
||||||
|
proj.ID = listId
|
||||||
|
proj.Name = id
|
||||||
|
|
||||||
|
tasks, err := todo.LoadTasks(listId)
|
||||||
|
proj.Err = err
|
||||||
|
proj.Tasks = tasks
|
||||||
|
|
||||||
|
return proj
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromTrello(task *trello.Card) Task {
|
||||||
|
return Task{
|
||||||
|
ID: task.ID,
|
||||||
|
Completed: task.Closed,
|
||||||
|
Name: task.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) LoadTasks(id string) ([]Task, error) {
|
||||||
|
tasks, err := getCardsOnList(todo.client, id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var finalTasks []Task
|
||||||
|
for _, item := range tasks {
|
||||||
|
finalTasks = append(finalTasks, fromTrello(item))
|
||||||
|
}
|
||||||
|
return finalTasks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) CloseTask(task *Task) error {
|
||||||
|
args := trello.Arguments{
|
||||||
|
"closed": "true",
|
||||||
|
}
|
||||||
|
if task != nil {
|
||||||
|
// Card has an internal client rep which we can't access
|
||||||
|
// Just force a lookup
|
||||||
|
internal, err := todo.client.GetCard(task.ID, trello.Arguments{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return internal.Update(args)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) DeleteTask(task *Task) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (todo *Trello) Sources() []string {
|
||||||
|
var result []string
|
||||||
|
for _, id := range todo.projects {
|
||||||
|
result = append(result, id.(string))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package todoist
|
package todo_plus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -14,27 +14,26 @@ func (widget *Widget) content() (string, string, bool) {
|
|||||||
return widget.CommonSettings().Title, "", false
|
return widget.CommonSettings().Title, "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
if proj.err != nil {
|
if proj.Err != nil {
|
||||||
return widget.CommonSettings().Title, proj.err.Error(), true
|
return widget.CommonSettings().Title, proj.Err.Error(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
title := fmt.Sprintf(
|
title := fmt.Sprintf(
|
||||||
"[%s]%s[white]",
|
"[%s]%s[white]",
|
||||||
widget.settings.common.Colors.TextTheme.Title,
|
widget.settings.common.Colors.TextTheme.Title,
|
||||||
proj.Project.Name,
|
proj.Name)
|
||||||
)
|
|
||||||
|
|
||||||
str := ""
|
str := ""
|
||||||
|
|
||||||
for idx, item := range proj.tasks {
|
for idx, item := range proj.Tasks {
|
||||||
row := fmt.Sprintf(
|
row := fmt.Sprintf(
|
||||||
`[%s]| | %s[%s]`,
|
`[%s]| | %s[%s]`,
|
||||||
widget.RowColor(idx),
|
widget.RowColor(idx),
|
||||||
tview.Escape(item.Content),
|
tview.Escape(item.Name),
|
||||||
widget.RowColor(idx),
|
widget.RowColor(idx),
|
||||||
)
|
)
|
||||||
|
|
||||||
str += utils.HighlightableHelper(widget.View, row, idx, len(item.Content))
|
str += utils.HighlightableHelper(widget.View, row, idx, len(item.Name))
|
||||||
}
|
}
|
||||||
return title, str, false
|
return title, str, false
|
||||||
}
|
}
|
@ -1,21 +1,21 @@
|
|||||||
package todoist
|
package todo_plus
|
||||||
|
|
||||||
import "github.com/gdamore/tcell"
|
import "github.com/gdamore/tcell"
|
||||||
|
|
||||||
func (widget *Widget) initializeKeyboardControls() {
|
func (widget *Widget) initializeKeyboardControls() {
|
||||||
widget.InitializeCommonControls(widget.Refresh)
|
widget.InitializeCommonControls(widget.Refresh)
|
||||||
|
|
||||||
widget.SetKeyboardChar("c", widget.Close, "Close item")
|
|
||||||
widget.SetKeyboardChar("d", widget.Delete, "Delete item")
|
widget.SetKeyboardChar("d", widget.Delete, "Delete item")
|
||||||
widget.SetKeyboardChar("h", widget.PrevSource, "Select previous project")
|
|
||||||
widget.SetKeyboardChar("j", widget.Prev, "Select previous item")
|
widget.SetKeyboardChar("j", widget.Prev, "Select previous item")
|
||||||
widget.SetKeyboardChar("k", widget.Next, "Select next item")
|
widget.SetKeyboardChar("k", widget.Next, "Select next item")
|
||||||
|
widget.SetKeyboardChar("h", widget.PrevSource, "Select previous project")
|
||||||
|
widget.SetKeyboardChar("c", widget.Close, "Close item")
|
||||||
widget.SetKeyboardChar("l", widget.NextSource, "Select next project")
|
widget.SetKeyboardChar("l", widget.NextSource, "Select next project")
|
||||||
widget.SetKeyboardChar("u", widget.Unselect, "Clear selection")
|
widget.SetKeyboardChar("u", widget.Unselect, "Clear selection")
|
||||||
|
|
||||||
widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
|
widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
|
||||||
|
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
|
||||||
widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
|
widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
|
||||||
widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous project")
|
widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous project")
|
||||||
widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next project")
|
widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next project")
|
||||||
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
|
|
||||||
}
|
}
|
81
modules/todo_plus/settings.go
Normal file
81
modules/todo_plus/settings.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package todo_plus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/olebedev/config"
|
||||||
|
"github.com/wtfutil/wtf/cfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultTitle = "Todo"
|
||||||
|
defaultFocusable = true
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
common *cfg.Common
|
||||||
|
|
||||||
|
backendType string
|
||||||
|
backendSettings *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||||
|
|
||||||
|
backend, _ := ymlConfig.Get("backendSettings")
|
||||||
|
|
||||||
|
settings := Settings{
|
||||||
|
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
|
||||||
|
|
||||||
|
backendType: ymlConfig.UString("backendType"),
|
||||||
|
backendSettings: backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromTodoist(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||||
|
apiKey := ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_TODOIST_TOKEN")))
|
||||||
|
cfg.ModuleSecret(name, globalConfig, &apiKey).Load()
|
||||||
|
projects := ymlConfig.UList("projects")
|
||||||
|
backend, _ := config.ParseYaml("apiKey: " + apiKey)
|
||||||
|
_ = backend.Set(".projects", projects)
|
||||||
|
|
||||||
|
settings := Settings{
|
||||||
|
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
|
||||||
|
|
||||||
|
backendType: "todoist",
|
||||||
|
backendSettings: backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromTrello(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||||
|
|
||||||
|
accessToken := ymlConfig.UString("accessToken", ymlConfig.UString("apikey", os.Getenv("WTF_TRELLO_ACCESS_TOKEN")))
|
||||||
|
apiKey := ymlConfig.UString("apiKey", os.Getenv("WTF_TRELLO_API_KEY"))
|
||||||
|
cfg.ModuleSecret(name, globalConfig, &apiKey).Load()
|
||||||
|
board := ymlConfig.UString("board")
|
||||||
|
username := ymlConfig.UString("username")
|
||||||
|
var lists []interface{}
|
||||||
|
list, err := ymlConfig.String("list")
|
||||||
|
if err == nil {
|
||||||
|
lists = append(lists, list)
|
||||||
|
} else {
|
||||||
|
lists = ymlConfig.UList("list")
|
||||||
|
}
|
||||||
|
backend, _ := config.ParseYaml("apiKey: " + apiKey)
|
||||||
|
_ = backend.Set(".accessToken", accessToken)
|
||||||
|
_ = backend.Set(".board", board)
|
||||||
|
_ = backend.Set(".username", username)
|
||||||
|
_ = backend.Set(".lists", lists)
|
||||||
|
|
||||||
|
settings := Settings{
|
||||||
|
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
|
||||||
|
|
||||||
|
backendType: "trello",
|
||||||
|
backendSettings: backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &settings
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package todoist
|
package todo_plus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
"github.com/wtfutil/todoist"
|
"github.com/wtfutil/wtf/modules/todo_plus/backend"
|
||||||
"github.com/wtfutil/wtf/view"
|
"github.com/wtfutil/wtf/view"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,8 +14,9 @@ type Widget struct {
|
|||||||
view.MultiSourceWidget
|
view.MultiSourceWidget
|
||||||
view.ScrollableWidget
|
view.ScrollableWidget
|
||||||
|
|
||||||
projects []*Project
|
projects []*backend.Project
|
||||||
settings *Settings
|
settings *Settings
|
||||||
|
backend backend.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWidget creates a new instance of a widget
|
// NewWidget creates a new instance of a widget
|
||||||
@ -26,8 +29,9 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
|
|||||||
settings: settings,
|
settings: settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.loadAPICredentials()
|
widget.backend = getBackend(settings.backendType)
|
||||||
widget.loadProjects()
|
widget.backend.Setup(settings.backendSettings)
|
||||||
|
widget.CommonSettings().Title = widget.backend.Title()
|
||||||
|
|
||||||
widget.SetRenderFunction(widget.display)
|
widget.SetRenderFunction(widget.display)
|
||||||
widget.initializeKeyboardControls()
|
widget.initializeKeyboardControls()
|
||||||
@ -39,13 +43,28 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
|
|||||||
return &widget
|
return &widget
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBackend(backendType string) backend.Backend {
|
||||||
|
switch backendType {
|
||||||
|
case "trello":
|
||||||
|
backend := &backend.Trello{}
|
||||||
|
return backend
|
||||||
|
case "todoist":
|
||||||
|
backend := &backend.Todoist{}
|
||||||
|
return backend
|
||||||
|
default:
|
||||||
|
log.Fatal(backendType + " is not a supported backend")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------- Exported Functions -------------------- */
|
/* -------------------- Exported Functions -------------------- */
|
||||||
|
|
||||||
func (widget *Widget) CurrentProject() *Project {
|
func (widget *Widget) CurrentProject() *backend.Project {
|
||||||
return widget.ProjectAt(widget.Idx)
|
return widget.ProjectAt(widget.Idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) ProjectAt(idx int) *Project {
|
func (widget *Widget) ProjectAt(idx int) *backend.Project {
|
||||||
if len(widget.projects) == 0 {
|
if len(widget.projects) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -54,14 +73,13 @@ func (widget *Widget) ProjectAt(idx int) *Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) Refresh() {
|
func (widget *Widget) Refresh() {
|
||||||
if widget.Disabled() || widget.CurrentProject() == nil {
|
if widget.Disabled() {
|
||||||
widget.SetItemCount(0)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.loadProjects()
|
widget.projects = widget.backend.BuildProjects()
|
||||||
|
widget.Sources = widget.backend.Sources()
|
||||||
widget.SetItemCount(len(widget.CurrentProject().tasks))
|
widget.SetItemCount(len(widget.CurrentProject().Tasks))
|
||||||
widget.display()
|
widget.display()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,74 +89,57 @@ func (widget *Widget) HelpText() string {
|
|||||||
|
|
||||||
func (widget *Widget) NextSource() {
|
func (widget *Widget) NextSource() {
|
||||||
widget.MultiSourceWidget.NextSource()
|
widget.MultiSourceWidget.NextSource()
|
||||||
widget.Selected = widget.CurrentProject().index
|
widget.Selected = widget.CurrentProject().Index
|
||||||
widget.SetItemCount(len(widget.CurrentProject().tasks))
|
widget.SetItemCount(len(widget.CurrentProject().Tasks))
|
||||||
widget.RenderFunction()
|
widget.RenderFunction()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) PrevSource() {
|
func (widget *Widget) PrevSource() {
|
||||||
widget.MultiSourceWidget.PrevSource()
|
widget.MultiSourceWidget.PrevSource()
|
||||||
widget.Selected = widget.CurrentProject().index
|
widget.Selected = widget.CurrentProject().Index
|
||||||
widget.SetItemCount(len(widget.CurrentProject().tasks))
|
widget.SetItemCount(len(widget.CurrentProject().Tasks))
|
||||||
widget.RenderFunction()
|
widget.RenderFunction()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) Prev() {
|
func (widget *Widget) Prev() {
|
||||||
widget.ScrollableWidget.Prev()
|
widget.ScrollableWidget.Prev()
|
||||||
widget.CurrentProject().index = widget.Selected
|
widget.CurrentProject().Index = widget.Selected
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) Next() {
|
func (widget *Widget) Next() {
|
||||||
widget.ScrollableWidget.Next()
|
widget.ScrollableWidget.Next()
|
||||||
widget.CurrentProject().index = widget.Selected
|
widget.CurrentProject().Index = widget.Selected
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) Unselect() {
|
func (widget *Widget) Unselect() {
|
||||||
widget.ScrollableWidget.Unselect()
|
widget.ScrollableWidget.Unselect()
|
||||||
widget.CurrentProject().index = -1
|
widget.CurrentProject().Index = -1
|
||||||
widget.RenderFunction()
|
widget.RenderFunction()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- Keyboard Movement -------------------- */
|
/* -------------------- Keyboard Movement -------------------- */
|
||||||
|
|
||||||
// Close closes the currently-selected task in the currently-selected project
|
// Close closes the currently-selected task in the currently-selected project
|
||||||
func (widget *Widget) Close() {
|
func (w *Widget) Close() {
|
||||||
widget.CurrentProject().closeSelectedTask()
|
w.CurrentProject().CloseSelectedTask()
|
||||||
widget.SetItemCount(len(widget.CurrentProject().tasks))
|
w.SetItemCount(len(w.CurrentProject().Tasks))
|
||||||
|
|
||||||
if widget.CurrentProject().isLast() {
|
if w.CurrentProject().IsLast() {
|
||||||
widget.Prev()
|
w.Prev()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
widget.CurrentProject().index = widget.Selected
|
w.CurrentProject().Index = w.Selected
|
||||||
widget.RenderFunction()
|
w.RenderFunction()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the currently-selected task in the currently-selected project
|
// Delete deletes the currently-selected task in the currently-selected project
|
||||||
func (widget *Widget) Delete() {
|
func (w *Widget) Delete() {
|
||||||
widget.CurrentProject().deleteSelectedTask()
|
w.CurrentProject().DeleteSelectedTask()
|
||||||
widget.SetItemCount(len(widget.CurrentProject().tasks))
|
w.SetItemCount(len(w.CurrentProject().Tasks))
|
||||||
|
|
||||||
if widget.CurrentProject().isLast() {
|
if w.CurrentProject().IsLast() {
|
||||||
widget.Prev()
|
w.Prev()
|
||||||
}
|
}
|
||||||
widget.CurrentProject().index = widget.Selected
|
w.CurrentProject().Index = w.Selected
|
||||||
widget.RenderFunction()
|
w.RenderFunction()
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------- Unexported Functions -------------------- */
|
|
||||||
|
|
||||||
func (widget *Widget) loadAPICredentials() {
|
|
||||||
todoist.Token = widget.settings.apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (widget *Widget) loadProjects() {
|
|
||||||
projects := []*Project{}
|
|
||||||
|
|
||||||
for _, id := range widget.settings.projects {
|
|
||||||
proj := NewProject(id)
|
|
||||||
projects = append(projects, proj)
|
|
||||||
}
|
|
||||||
|
|
||||||
widget.projects = projects
|
|
||||||
}
|
}
|
@ -1,93 +0,0 @@
|
|||||||
package todoist
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/wtfutil/todoist"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Project struct {
|
|
||||||
todoist.Project
|
|
||||||
|
|
||||||
index int
|
|
||||||
tasks []todoist.Task
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProject(id uint) *Project {
|
|
||||||
// Todoist seems to experience a lot of network issues on their side
|
|
||||||
// If we can't connect, handle it with an empty project until we can
|
|
||||||
project, err := todoist.GetProject(id)
|
|
||||||
proj := &Project{
|
|
||||||
index: -1,
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
proj.err = err
|
|
||||||
return proj
|
|
||||||
}
|
|
||||||
|
|
||||||
proj.Project = project
|
|
||||||
|
|
||||||
proj.loadTasks()
|
|
||||||
|
|
||||||
return proj
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proj *Project) isLast() bool {
|
|
||||||
return proj.index >= len(proj.tasks)-1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proj *Project) loadTasks() {
|
|
||||||
tasks, err := todoist.ListTask(todoist.QueryParam{"project_id": fmt.Sprintf("%d", proj.ID)})
|
|
||||||
if err != nil {
|
|
||||||
proj.err = err
|
|
||||||
proj.tasks = nil
|
|
||||||
} else {
|
|
||||||
proj.err = nil
|
|
||||||
proj.tasks = tasks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proj *Project) LongestLine() int {
|
|
||||||
maxLen := 0
|
|
||||||
|
|
||||||
for _, task := range proj.tasks {
|
|
||||||
if len(task.Content) > maxLen {
|
|
||||||
maxLen = len(task.Content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxLen
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proj *Project) currentTask() *todoist.Task {
|
|
||||||
if proj.index < 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &proj.tasks[proj.index]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proj *Project) closeSelectedTask() {
|
|
||||||
currTask := proj.currentTask()
|
|
||||||
|
|
||||||
if currTask != nil {
|
|
||||||
if err := currTask.Close(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
proj.loadTasks()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proj *Project) deleteSelectedTask() {
|
|
||||||
currTask := proj.currentTask()
|
|
||||||
|
|
||||||
if currTask != nil {
|
|
||||||
if err := currTask.Delete(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
proj.loadTasks()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package todoist
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/olebedev/config"
|
|
||||||
"github.com/wtfutil/wtf/cfg"
|
|
||||||
"github.com/wtfutil/wtf/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultFocusable = true
|
|
||||||
defaultTitle = "Todoist"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Settings defines the configuration properties for this module
|
|
||||||
type Settings struct {
|
|
||||||
common *cfg.Common
|
|
||||||
|
|
||||||
apiKey string `help:"Your Todoist API key"`
|
|
||||||
projects []uint
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSettingsFromYAML creates a new settings instance from a YAML config block
|
|
||||||
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
|
||||||
settings := Settings{
|
|
||||||
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
|
|
||||||
|
|
||||||
apiKey: ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_TODOIST_TOKEN"))),
|
|
||||||
projects: utils.IntsToUints(utils.ToInts(ymlConfig.UList("projects"))),
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
|
|
||||||
|
|
||||||
return &settings
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package trello
|
|
||||||
|
|
||||||
type TrelloCard struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
List string
|
|
||||||
Description string
|
|
||||||
}
|
|
||||||
|
|
||||||
type TrelloList struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
package trello
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/adlio/trello"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetCards(client *trello.Client, username string, boardName string, listNames []string) (*SearchResult, error) {
|
|
||||||
boardID, err := getBoardID(client, username, boardName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lists, err := getLists(client, boardID, listNames)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
searchResult := &SearchResult{Total: 0}
|
|
||||||
searchResult.TrelloCards = make(map[string][]TrelloCard)
|
|
||||||
|
|
||||||
for _, list := range lists {
|
|
||||||
cards, err := getCardsOnList(client, list.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
searchResult.Total += len(cards)
|
|
||||||
cardArray := make([]TrelloCard, 0)
|
|
||||||
|
|
||||||
for _, card := range cards {
|
|
||||||
trelloCard := TrelloCard{
|
|
||||||
ID: card.ID,
|
|
||||||
List: list.Name,
|
|
||||||
Name: card.Name,
|
|
||||||
Description: card.Desc,
|
|
||||||
}
|
|
||||||
cardArray = append(cardArray, trelloCard)
|
|
||||||
}
|
|
||||||
|
|
||||||
searchResult.TrelloCards[list.Name] = cardArray
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchResult, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBoardID(client *trello.Client, username, boardName string) (string, error) {
|
|
||||||
member, err := client.GetMember(username, trello.Defaults())
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
boards, err := member.GetBoards(trello.Defaults())
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, board := range boards {
|
|
||||||
if board.Name == boardName {
|
|
||||||
return board.ID, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", fmt.Errorf("could not find board with name %s", boardName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLists(client *trello.Client, boardID string, listNames []string) ([]TrelloList, error) {
|
|
||||||
comparison := make(map[string]string, len(listNames))
|
|
||||||
results := []TrelloList{}
|
|
||||||
//convert to a map for comparison
|
|
||||||
for _, item := range listNames {
|
|
||||||
comparison[item] = ""
|
|
||||||
}
|
|
||||||
board, err := client.GetBoard(boardID, trello.Defaults())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
boardLists, err := board.GetLists(trello.Defaults())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, list := range boardLists {
|
|
||||||
if _, ok := comparison[list.Name]; ok {
|
|
||||||
results = append(results, TrelloList{ID: list.ID, Name: list.Name})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCardsOnList(client *trello.Client, listID string) ([]*trello.Card, error) {
|
|
||||||
list, err := client.GetList(listID, trello.Defaults())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cards, err := list.GetCards(trello.Defaults())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cards, nil
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package trello
|
|
||||||
|
|
||||||
type SearchResult struct {
|
|
||||||
Total int
|
|
||||||
TrelloCards map[string][]TrelloCard
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package trello
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/olebedev/config"
|
|
||||||
"github.com/wtfutil/wtf/cfg"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultFocusable = false
|
|
||||||
defaultTitle = "Trello"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Settings struct {
|
|
||||||
common *cfg.Common
|
|
||||||
|
|
||||||
accessToken string
|
|
||||||
apiKey string
|
|
||||||
board string
|
|
||||||
list []string
|
|
||||||
username string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
|
||||||
settings := Settings{
|
|
||||||
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
|
|
||||||
|
|
||||||
accessToken: ymlConfig.UString("accessToken", ymlConfig.UString("apikey", os.Getenv("WTF_TRELLO_ACCESS_TOKEN"))),
|
|
||||||
apiKey: ymlConfig.UString("apiKey", os.Getenv("WTF_TRELLO_API_KEY")),
|
|
||||||
board: ymlConfig.UString("board"),
|
|
||||||
username: ymlConfig.UString("username"),
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.ModuleSecret(name+"-api", globalConfig, &settings.apiKey).Load()
|
|
||||||
cfg.ModuleSecret(name+"-access", globalConfig, &settings.accessToken).Load()
|
|
||||||
|
|
||||||
settings.list = buildLists(ymlConfig, globalConfig)
|
|
||||||
|
|
||||||
return &settings
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildLists(ymlConfig *config.Config, globalConfig *config.Config) []string {
|
|
||||||
lists := []string{}
|
|
||||||
|
|
||||||
// Single list
|
|
||||||
list, err := ymlConfig.String("list")
|
|
||||||
if err == nil {
|
|
||||||
lists = append(lists, list)
|
|
||||||
return lists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array of lists
|
|
||||||
listList := ymlConfig.UList("list")
|
|
||||||
for _, listName := range listList {
|
|
||||||
if list, ok := listName.(string); ok {
|
|
||||||
lists = append(lists, list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lists
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package trello
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/adlio/trello"
|
|
||||||
"github.com/rivo/tview"
|
|
||||||
"github.com/wtfutil/wtf/view"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Widget struct {
|
|
||||||
view.TextWidget
|
|
||||||
|
|
||||||
settings *Settings
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWidget(app *tview.Application, settings *Settings) *Widget {
|
|
||||||
widget := Widget{
|
|
||||||
TextWidget: view.NewTextWidget(app, settings.common),
|
|
||||||
|
|
||||||
settings: settings,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &widget
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------- Exported Functions -------------------- */
|
|
||||||
|
|
||||||
func (widget *Widget) Refresh() {
|
|
||||||
widget.Redraw(widget.content)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------- Unexported Functions -------------------- */
|
|
||||||
|
|
||||||
func (widget *Widget) content() (string, string, bool) {
|
|
||||||
|
|
||||||
client := trello.NewClient(
|
|
||||||
widget.settings.apiKey,
|
|
||||||
widget.settings.accessToken,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get the cards
|
|
||||||
searchResult, err := GetCards(
|
|
||||||
client,
|
|
||||||
widget.settings.username,
|
|
||||||
widget.settings.board,
|
|
||||||
widget.settings.list,
|
|
||||||
)
|
|
||||||
|
|
||||||
var title string
|
|
||||||
content := ""
|
|
||||||
|
|
||||||
wrap := false
|
|
||||||
if err != nil {
|
|
||||||
wrap = true
|
|
||||||
title = widget.CommonSettings().Title
|
|
||||||
content = err.Error()
|
|
||||||
} else {
|
|
||||||
title = fmt.Sprintf(
|
|
||||||
"[white]%s: [green]%s ",
|
|
||||||
widget.CommonSettings().Title,
|
|
||||||
widget.settings.board,
|
|
||||||
)
|
|
||||||
for list, cardArray := range searchResult.TrelloCards {
|
|
||||||
content += fmt.Sprintf(" [%s]%s[white]\n", widget.settings.common.Colors.Subheading, list)
|
|
||||||
|
|
||||||
for _, card := range cardArray {
|
|
||||||
content += fmt.Sprintf(" %s[white]\n", card.Name)
|
|
||||||
}
|
|
||||||
content = fmt.Sprintf("%s\n", content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title, content, wrap
|
|
||||||
}
|
|
62
view/info_table_test.go
Normal file
62
view/info_table_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeMap() map[string]string {
|
||||||
|
m := make(map[string]string)
|
||||||
|
m["foo"] = "val1"
|
||||||
|
m["bar"] = "val2"
|
||||||
|
m["baz"] = "val3"
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestTable(height, colWidth0, colWidth1 int) *InfoTable {
|
||||||
|
var headers [2]string
|
||||||
|
|
||||||
|
headers[0] = "hdr0"
|
||||||
|
headers[1] = "hdr1"
|
||||||
|
|
||||||
|
table := NewInfoTable(
|
||||||
|
headers[:],
|
||||||
|
makeMap(),
|
||||||
|
colWidth0,
|
||||||
|
colWidth1,
|
||||||
|
height,
|
||||||
|
)
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RenderSimpleInfoTable(t *testing.T) {
|
||||||
|
table := newTestTable(4, 1, 1).Render()
|
||||||
|
|
||||||
|
assert.Equal(t, " ----- ------ \n HDR0 HDR1 \n ----- ------ \n bar val2 \n baz val3 \n foo val1 \n ----- ------ \n", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RenderPaddedInfoTable(t *testing.T) {
|
||||||
|
table := newTestTable(6, 1, 1).Render()
|
||||||
|
|
||||||
|
assert.Equal(t, " ----- ------ \n HDR0 HDR1 \n ----- ------ \n bar val2 \n baz val3 \n foo val1 \n \n \n ----- ------ \n", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RenderWithSpecifiedWidthLeftColumn(t *testing.T) {
|
||||||
|
table := newTestTable(4, 10, 1).Render()
|
||||||
|
|
||||||
|
assert.Equal(t, " ------------ ------ \n HDR0 HDR1 \n ------------ ------ \n bar val2 \n baz val3 \n foo val1 \n ------------ ------ \n", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RenderWithSpecifiedWidthRightColumn(t *testing.T) {
|
||||||
|
table := newTestTable(4, 1, 10).Render()
|
||||||
|
|
||||||
|
assert.Equal(t, " ----- ------------ \n HDR0 HDR1 \n ----- ------------ \n bar val2 \n baz val3 \n foo val1 \n ----- ------------ \n", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RenderWithSpecifiedWidthBothColumns(t *testing.T) {
|
||||||
|
table := newTestTable(4, 15, 10).Render()
|
||||||
|
|
||||||
|
assert.Equal(t, " ----------------- ------------ \n HDR0 HDR1 \n ----------------- ------------ \n bar val2 \n baz val3 \n foo val1 \n ----------------- ------------ \n", table)
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
"github.com/wtfutil/wtf/cfg"
|
"github.com/wtfutil/wtf/cfg"
|
||||||
"github.com/wtfutil/wtf/wtf"
|
"github.com/wtfutil/wtf/wtf"
|
||||||
@ -36,7 +38,9 @@ func (widget *TextWidget) Redraw(data func() (string, string, bool)) {
|
|||||||
widget.View.Clear()
|
widget.View.Clear()
|
||||||
widget.View.SetWrap(wrap)
|
widget.View.SetWrap(wrap)
|
||||||
widget.View.SetTitle(widget.ContextualTitle(title))
|
widget.View.SetTitle(widget.ContextualTitle(title))
|
||||||
widget.View.SetText(content)
|
// widget.View.SetText(strings.TrimSpace(content))
|
||||||
|
widget.View.SetText(strings.TrimRight(content, "\n"))
|
||||||
|
// widget.View.SetText(content)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user