1
0
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:
Chris Cummer 2020-10-08 11:44:10 -07:00
commit 589af9917a
38 changed files with 834 additions and 554 deletions

View File

@ -959,6 +959,41 @@
"name": "Tim Hwang",
"avatar_url": "https://avatars3.githubusercontent.com/u/5831434?v=4",
"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": [
]
}

View File

@ -1,7 +1,7 @@
language: go
go:
- "1.14.x"
- "1.15.x"
before_install:
# Make sure travis builds work for forks

View File

@ -4,6 +4,32 @@
### ⚡️ 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)
* 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)

View File

@ -1,4 +1,4 @@
FROM golang:1.13-alpine as build
FROM golang:1.15-alpine as build
ARG version=master

View File

@ -1,4 +1,4 @@
FROM golang:1.13 as build
FROM golang:1.15 as build
ARG version=master

View File

@ -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.
### 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
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/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://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>
</table>

View File

@ -59,10 +59,9 @@ import (
"github.com/wtfutil/wtf/modules/subreddit"
"github.com/wtfutil/wtf/modules/textfile"
"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/travisci"
"github.com/wtfutil/wtf/modules/trello"
"github.com/wtfutil/wtf/modules/twitch"
"github.com/wtfutil/wtf/modules/twitter"
"github.com/wtfutil/wtf/modules/twitterstats"
@ -269,9 +268,12 @@ func MakeWidget(
case "todo":
settings := todo.NewSettingsFromYAML(moduleName, moduleConfig, config)
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":
settings := todoist.NewSettingsFromYAML(moduleName, moduleConfig, config)
widget = todoist.NewWidget(app, pages, settings)
settings := todo_plus.FromTodoist(moduleName, moduleConfig, config)
widget = todo_plus.NewWidget(app, pages, settings)
case "transmission":
settings := transmission.NewSettingsFromYAML(moduleName, moduleConfig, config)
widget = transmission.NewWidget(app, pages, settings)
@ -279,8 +281,8 @@ func MakeWidget(
settings := travisci.NewSettingsFromYAML(moduleName, moduleConfig, config)
widget = travisci.NewWidget(app, pages, settings)
case "trello":
settings := trello.NewSettingsFromYAML(moduleName, moduleConfig, config)
widget = trello.NewWidget(app, settings)
settings := todo_plus.FromTrello(moduleName, moduleConfig, config)
widget = todo_plus.NewWidget(app, pages, settings)
case "twitch":
settings := twitch.NewSettingsFromYAML(moduleName, moduleConfig, config)
widget = twitch.NewWidget(app, pages, settings)

View File

@ -37,7 +37,7 @@ func CreateFile(fileName string) (string, error) {
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
_, 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
func chmodConfigFile() {
configDir, _ := WtfConfigDir()
relPath := fmt.Sprintf("%s%s", configDir, WtfConfigFile)
relPath := filepath.Join(configDir, WtfConfigFile)
absPath, _ := expandHomeDir(relPath)
_, err := os.Stat(absPath)

12
go.mod
View File

@ -1,6 +1,6 @@
module github.com/wtfutil/wtf
go 1.14
go 1.15
require (
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/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/VictorAvelar/devto-api-go v1.0.0
github.com/adlio/trello v1.7.0
github.com/alecthomas/chroma v0.8.0
github.com/adlio/trello v1.8.0
github.com/alecthomas/chroma v0.8.1
github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
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/docker v1.13.1
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/pflag v1.0.5 // 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/wtfutil/spotigopher v0.0.0-20191127141047-7d8168fe103a
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/zorkian/go-datadog-api v2.29.0+incompatible
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/yaml.v2 v2.3.0
gotest.tools v2.2.0+incompatible
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7
k8s.io/apimachinery v0.0.0-20190223094358-dcb391cde5ca
k8s.io/client-go v10.0.0+incompatible
)

29
go.sum
View File

@ -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/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/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/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
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/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/adlio/trello v1.7.0 h1:syLRJ27wCM8URf7zOBWGr981cG+dpmLSyMqjEoQc+4g=
github.com/adlio/trello v1.7.0/go.mod h1:l2068AhUuUuQ9Vsb95ECMueHThYyAj4e85lWPmr2/LE=
github.com/adlio/trello v1.8.0 h1:VU/1zwzuRzATsFC8WiK4f8R0HHQPWpf2H658KEchsmA=
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/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.8.0 h1:HS+HE97sgcqjQGu5uVr8jIE55Mmh5UeQ7kckAhHg2pY=
github.com/alecthomas/chroma v0.8.0/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
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/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
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/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/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/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
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/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/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/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
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/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/digitalocean/godo v1.42.1 h1:SJ/XMVsp5CZmyQal8gLlOl9jSl1i3FaN20LlgtK5ZMs=
github.com/digitalocean/godo v1.42.1/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
github.com/digitalocean/godo v1.46.0 h1:WRbwjATilgz2NE4NGMeSDpeicy9h4xSKNGuRJ/Nq/fA=
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/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
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/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
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/go.mod h1:PPiVwgDXLlz2N83KB4TrIim2lyYM5Zn7ZWH9Pi4oHUk=
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/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 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/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
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.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
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/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
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/go.mod h1:YuuGLJSsTK6DGBD5Zaf3J8LSMfpEC2WtzYPey3XVOdI=
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.33.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/go-gitlab v0.38.1 h1:st5/Ag4h8CqVfp3LpOWW0Jd4jYHTGETwu0KksYDPnYE=
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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
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/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/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20190223094358-dcb391cde5ca h1:rOczZwIfERvL7YLSQ96uI3NUYamzdK/MmSDIfChQRBk=

View File

@ -1,6 +1,7 @@
package clocks
import (
"strings"
"time"
)
@ -18,6 +19,14 @@ func NewClock(label string, timeLoc *time.Location) 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 {
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 {
return clock.LocalTime().Format(timeFormat)
}
func sanitizeLocation(locStr string) string {
return strings.Replace(locStr, " ", "_", -1)
}

View File

@ -10,7 +10,9 @@ type ClockCollection struct {
}
func (clocks *ClockCollection) Sorted(sortOrder string) []Clock {
if sortOrder == "chronological" {
if sortOrder == "natural" {
//no-op
} else if sortOrder == "chronological" {
clocks.SortedChronologically()
} else {
clocks.SortedAlphabetically()

View File

@ -1,15 +1,13 @@
package clocks
import (
"fmt"
)
import "fmt"
func (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat string) {
str := ""
if len(clocks) == 0 {
str = fmt.Sprintf("\n%s", " no timezone data available")
} else {
for idx, clock := range clocks {
str += fmt.Sprintf(
" [%s]%-12s %-10s %7s[white]\n",

View File

@ -15,10 +15,10 @@ const (
type Settings struct {
common *cfg.Common
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."`
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."`
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."`
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."`
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', '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
@ -28,9 +28,40 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co
dateFormat: ymlConfig.UString("dateFormat", utils.SimpleDateFormat),
timeFormat: ymlConfig.UString("timeFormat", utils.SimpleTimeFormat),
locations: ymlConfig.UMap("locations"),
locations: buildLocations(ymlConfig),
sort: ymlConfig.UString("sort"),
}
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
}

View File

@ -1,9 +1,6 @@
package clocks
import (
"strings"
"time"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/view"
)
@ -28,7 +25,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
timeFormat: settings.timeFormat,
}
widget.clockColl = widget.buildClockCollection(settings.locations)
widget.clockColl = widget.buildClockCollection()
return &widget
}
@ -45,21 +42,10 @@ func (widget *Widget) Refresh() {
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) buildClockCollection(locData map[string]interface{}) ClockCollection {
func (widget *Widget) buildClockCollection() ClockCollection {
clockColl := ClockCollection{}
for label, locStr := range locData {
timeLoc, err := time.LoadLocation(widget.sanitizeLocation(locStr.(string)))
if err != nil {
continue
}
clockColl.Clocks = append(clockColl.Clocks, NewClock(label, timeLoc))
}
clockColl.Clocks = widget.settings.locations
return clockColl
}
func (widget *Widget) sanitizeLocation(locStr string) string {
return strings.Replace(locStr, " ", "_", -1)
}

View File

@ -3,6 +3,7 @@ package exchangerates
import (
"fmt"
"regexp"
"sort"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/view"
@ -56,15 +57,26 @@ func (widget *Widget) content() (string, string, bool) {
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 := ""
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 {
rate := widget.rates[base][cur]
out += fmt.Sprintf(
"[%s]1 %s = %s %s[white]\n",
widget.CommonSettings().RowColor(idx),
rowColor,
base,
widget.formatConversionRate(rate),
cur,

View File

@ -8,6 +8,7 @@ func (widget *Widget) initializeKeyboardControls() {
widget.SetKeyboardChar("j", widget.Next, "Select next item")
widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
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.KeyUp, widget.Prev, "Select previous item")

View File

@ -3,11 +3,21 @@ package feedreader
import (
"fmt"
"sort"
"strings"
"github.com/mmcdole/gofeed"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/utils"
"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
@ -25,6 +35,32 @@ type Widget struct {
parser *gofeed.Parser
settings *Settings
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
@ -35,6 +71,7 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
parser: gofeed.NewParser(),
settings: settings,
showType: SHOW_TITLE,
}
widget.SetRenderFunction(widget.Render)
@ -137,11 +174,13 @@ func (widget *Widget) content() (string, string, bool) {
}
}
displayText := getShowText(feedItem, widget.showType)
row := fmt.Sprintf(
"[%s]%2d. %s[white]",
rowColor,
idx+1,
feedItem.item.Title,
displayText,
)
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
func (widget *Widget) sort(feedItems []*FeedItem) []*FeedItem {
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
@ -169,3 +210,8 @@ func (widget *Widget) openStory() {
utils.OpenFile(story.item.Link)
}
}
func (widget *Widget) toggleDisplayText() {
widget.showType = rotateShowType(widget.showType)
widget.Render()
}

View File

@ -4,6 +4,8 @@ import (
"fmt"
"github.com/google/go-github/v26/github"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
func (widget *Widget) display() {
@ -54,7 +56,7 @@ func (widget *Widget) content() (string, string, bool) {
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)
prLength := len(prs)
@ -77,7 +79,7 @@ func (widget *Widget) displayMyPullRequests(repo *GithubRepo, username string) s
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)
if res == nil {
@ -104,7 +106,7 @@ func (widget *Widget) displayCustomQuery(repo *GithubRepo, filter string, perPag
return str
}
func (widget *Widget) displayMyReviewRequests(repo *GithubRepo, username string) string {
func (widget *Widget) displayMyReviewRequests(repo *Repo, username string) string {
prs := repo.myReviewRequests(username)
if len(prs) == 0 {
@ -121,18 +123,20 @@ func (widget *Widget) displayMyReviewRequests(repo *GithubRepo, username string)
return str
}
func (widget *Widget) displayStats(repo *GithubRepo) string {
func (widget *Widget) displayStats(repo *Repo) string {
prntr := message.NewPrinter(language.English)
str := fmt.Sprintf(
" PRs: %d Issues: %d Stars: %d\n",
repo.PullRequestCount(),
repo.IssueCount(),
repo.StarCount(),
" PRs: %s Issues: %s Stars: %s\n",
prntr.Sprintf("%d", repo.PullRequestCount()),
prntr.Sprintf("%d", repo.IssueCount()),
prntr.Sprintf("%d", repo.StarCount()),
)
return str
}
func (widget *Widget) title(repo *GithubRepo) string {
func (widget *Widget) title(repo *Repo) string {
return fmt.Sprintf(
"[%s]%s - %s[white]",
widget.settings.common.Colors.TextTheme.Title,

View File

@ -15,8 +15,8 @@ const (
issuesPath = "/issues"
)
// GithubRepo defines a new GithubRepo structure
type GithubRepo struct {
// Repo defines a new GitHub Repo structure
type Repo struct {
apiKey string
baseURL string
uploadURL string
@ -29,8 +29,8 @@ type GithubRepo struct {
}
// NewGithubRepo returns a new Github Repo with a name, owner, apiKey, baseURL and uploadURL
func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *GithubRepo {
repo := GithubRepo{
func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *Repo {
repo := Repo{
Name: name,
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
func (repo *GithubRepo) Open() {
func (repo *Repo) Open() {
utils.OpenFile(*repo.RemoteRepo.HTMLURL)
}
// Open will open the GitHub Pull Requests URL using the utils helper
func (repo *GithubRepo) OpenPulls() {
// OpenPulls will open the GitHub Pull Requests URL using the utils helper
func (repo *Repo) OpenPulls() {
utils.OpenFile(*repo.RemoteRepo.HTMLURL + pullRequestsPath)
}
// Open will open the GitHub Issues URL using the utils helper
func (repo *GithubRepo) OpenIssues() {
// OpenIssues will open the GitHub Issues URL using the utils helper
func (repo *Repo) OpenIssues() {
utils.OpenFile(*repo.RemoteRepo.HTMLURL + issuesPath)
}
// Refresh reloads the github data via the Github API
func (repo *GithubRepo) Refresh() {
func (repo *Repo) Refresh() {
prs, err := repo.loadPullRequests()
repo.Err = err
repo.PullRequests = prs
@ -73,7 +73,7 @@ func (repo *GithubRepo) Refresh() {
/* -------------------- Counts -------------------- */
// IssueCount return the total amount of issues as an int
func (repo *GithubRepo) IssueCount() int {
func (repo *Repo) IssueCount() int {
if repo.RemoteRepo == nil {
return 0
}
@ -84,12 +84,12 @@ func (repo *GithubRepo) IssueCount() 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)
}
// 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 {
return 0
}
@ -99,7 +99,7 @@ func (repo *GithubRepo) StarCount() int {
/* -------------------- Unexported Functions -------------------- */
func (repo *GithubRepo) isGitHubEnterprise() bool {
func (repo *Repo) isGitHubEnterprise() bool {
if len(repo.baseURL) > 0 {
if len(repo.uploadURL) == 0 {
repo.uploadURL = repo.baseURL
@ -109,7 +109,7 @@ func (repo *GithubRepo) isGitHubEnterprise() bool {
return false
}
func (repo *GithubRepo) oauthClient() *http.Client {
func (repo *Repo) oauthClient() *http.Client {
tokenService := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: repo.apiKey},
)
@ -117,7 +117,7 @@ func (repo *GithubRepo) oauthClient() *http.Client {
return oauth2.NewClient(context.Background(), tokenService)
}
func (repo *GithubRepo) githubClient() (*ghb.Client, error) {
func (repo *Repo) githubClient() (*ghb.Client, error) {
oauthClient := repo.oauthClient()
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
func (repo *GithubRepo) myPullRequests(username string, showStatus bool) []*ghb.PullRequest {
func (repo *Repo) myPullRequests(username string, showStatus bool) []*ghb.PullRequest {
prs := []*ghb.PullRequest{}
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
// 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
func (repo *GithubRepo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullRequest {
func (repo *Repo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullRequest {
github, err := repo.githubClient()
if err != nil {
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
// 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{}
for _, pr := range repo.PullRequests {
@ -184,7 +184,7 @@ func (repo *GithubRepo) myReviewRequests(username string) []*ghb.PullRequest {
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()
if err != nil {
return nil
@ -199,7 +199,7 @@ func (repo *GithubRepo) customIssueQuery(filter string, perPage int) *ghb.Issues
return prs
}
func (repo *GithubRepo) loadPullRequests() ([]*ghb.PullRequest, error) {
func (repo *Repo) loadPullRequests() ([]*ghb.PullRequest, error) {
github, err := repo.githubClient()
if err != nil {
return nil, err
@ -217,7 +217,7 @@ func (repo *GithubRepo) loadPullRequests() ([]*ghb.PullRequest, error) {
return prs, nil
}
func (repo *GithubRepo) loadRemoteRepository() (*ghb.Repository, error) {
func (repo *Repo) loadRemoteRepository() (*ghb.Repository, error) {
github, err := repo.githubClient()
if err != nil {

View File

@ -15,7 +15,7 @@ type Widget struct {
view.KeyboardWidget
view.TextWidget
GithubRepos []*GithubRepo
GithubRepos []*Repo
settings *Settings
Selected int
@ -110,8 +110,8 @@ func (widget *Widget) HelpText() string {
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) buildRepoCollection(repoData []string) []*GithubRepo {
githubRepos := []*GithubRepo{}
func (widget *Widget) buildRepoCollection(repoData []string) []*Repo {
githubRepos := []*Repo{}
for _, repo := range repoData {
split := strings.Split(repo, "/")
@ -130,7 +130,7 @@ func (widget *Widget) buildRepoCollection(repoData []string) []*GithubRepo {
return githubRepos
}
func (widget *Widget) currentGithubRepo() *GithubRepo {
func (widget *Widget) currentGithubRepo() *Repo {
if len(widget.GithubRepos) == 0 {
return nil
}

View 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
}

View 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()
}
}

View 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
}

View 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
}

View File

@ -1,4 +1,4 @@
package todoist
package todo_plus
import (
"fmt"
@ -14,27 +14,26 @@ func (widget *Widget) content() (string, string, bool) {
return widget.CommonSettings().Title, "", false
}
if proj.err != nil {
return widget.CommonSettings().Title, proj.err.Error(), true
if proj.Err != nil {
return widget.CommonSettings().Title, proj.Err.Error(), true
}
title := fmt.Sprintf(
"[%s]%s[white]",
widget.settings.common.Colors.TextTheme.Title,
proj.Project.Name,
)
proj.Name)
str := ""
for idx, item := range proj.tasks {
for idx, item := range proj.Tasks {
row := fmt.Sprintf(
`[%s]| | %s[%s]`,
widget.RowColor(idx),
tview.Escape(item.Content),
tview.Escape(item.Name),
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
}

View File

@ -1,21 +1,21 @@
package todoist
package todo_plus
import "github.com/gdamore/tcell"
func (widget *Widget) initializeKeyboardControls() {
widget.InitializeCommonControls(widget.Refresh)
widget.SetKeyboardChar("c", widget.Close, "Close 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("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("u", widget.Unselect, "Clear selection")
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.KeyLeft, widget.PrevSource, "Select previous project")
widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next project")
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
}

View 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
}

View File

@ -1,8 +1,10 @@
package todoist
package todo_plus
import (
"log"
"github.com/rivo/tview"
"github.com/wtfutil/todoist"
"github.com/wtfutil/wtf/modules/todo_plus/backend"
"github.com/wtfutil/wtf/view"
)
@ -12,8 +14,9 @@ type Widget struct {
view.MultiSourceWidget
view.ScrollableWidget
projects []*Project
projects []*backend.Project
settings *Settings
backend backend.Backend
}
// NewWidget creates a new instance of a widget
@ -26,8 +29,9 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
settings: settings,
}
widget.loadAPICredentials()
widget.loadProjects()
widget.backend = getBackend(settings.backendType)
widget.backend.Setup(settings.backendSettings)
widget.CommonSettings().Title = widget.backend.Title()
widget.SetRenderFunction(widget.display)
widget.initializeKeyboardControls()
@ -39,13 +43,28 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
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 -------------------- */
func (widget *Widget) CurrentProject() *Project {
func (widget *Widget) CurrentProject() *backend.Project {
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 {
return nil
}
@ -54,14 +73,13 @@ func (widget *Widget) ProjectAt(idx int) *Project {
}
func (widget *Widget) Refresh() {
if widget.Disabled() || widget.CurrentProject() == nil {
widget.SetItemCount(0)
if widget.Disabled() {
return
}
widget.loadProjects()
widget.SetItemCount(len(widget.CurrentProject().tasks))
widget.projects = widget.backend.BuildProjects()
widget.Sources = widget.backend.Sources()
widget.SetItemCount(len(widget.CurrentProject().Tasks))
widget.display()
}
@ -71,74 +89,57 @@ func (widget *Widget) HelpText() string {
func (widget *Widget) NextSource() {
widget.MultiSourceWidget.NextSource()
widget.Selected = widget.CurrentProject().index
widget.SetItemCount(len(widget.CurrentProject().tasks))
widget.Selected = widget.CurrentProject().Index
widget.SetItemCount(len(widget.CurrentProject().Tasks))
widget.RenderFunction()
}
func (widget *Widget) PrevSource() {
widget.MultiSourceWidget.PrevSource()
widget.Selected = widget.CurrentProject().index
widget.SetItemCount(len(widget.CurrentProject().tasks))
widget.Selected = widget.CurrentProject().Index
widget.SetItemCount(len(widget.CurrentProject().Tasks))
widget.RenderFunction()
}
func (widget *Widget) Prev() {
widget.ScrollableWidget.Prev()
widget.CurrentProject().index = widget.Selected
widget.CurrentProject().Index = widget.Selected
}
func (widget *Widget) Next() {
widget.ScrollableWidget.Next()
widget.CurrentProject().index = widget.Selected
widget.CurrentProject().Index = widget.Selected
}
func (widget *Widget) Unselect() {
widget.ScrollableWidget.Unselect()
widget.CurrentProject().index = -1
widget.CurrentProject().Index = -1
widget.RenderFunction()
}
/* -------------------- Keyboard Movement -------------------- */
// Close closes the currently-selected task in the currently-selected project
func (widget *Widget) Close() {
widget.CurrentProject().closeSelectedTask()
widget.SetItemCount(len(widget.CurrentProject().tasks))
func (w *Widget) Close() {
w.CurrentProject().CloseSelectedTask()
w.SetItemCount(len(w.CurrentProject().Tasks))
if widget.CurrentProject().isLast() {
widget.Prev()
if w.CurrentProject().IsLast() {
w.Prev()
return
}
widget.CurrentProject().index = widget.Selected
widget.RenderFunction()
w.CurrentProject().Index = w.Selected
w.RenderFunction()
}
// Delete deletes the currently-selected task in the currently-selected project
func (widget *Widget) Delete() {
widget.CurrentProject().deleteSelectedTask()
widget.SetItemCount(len(widget.CurrentProject().tasks))
func (w *Widget) Delete() {
w.CurrentProject().DeleteSelectedTask()
w.SetItemCount(len(w.CurrentProject().Tasks))
if widget.CurrentProject().isLast() {
widget.Prev()
if w.CurrentProject().IsLast() {
w.Prev()
}
widget.CurrentProject().index = widget.Selected
widget.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
w.CurrentProject().Index = w.Selected
w.RenderFunction()
}

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -1,13 +0,0 @@
package trello
type TrelloCard struct {
ID string
Name string
List string
Description string
}
type TrelloList struct {
ID string
Name string
}

View File

@ -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
}

View File

@ -1,6 +0,0 @@
package trello
type SearchResult struct {
Total int
TrelloCards map[string][]TrelloCard
}

View File

@ -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
}

View File

@ -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
View 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)
}

View File

@ -1,6 +1,8 @@
package view
import (
"strings"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/cfg"
"github.com/wtfutil/wtf/wtf"
@ -36,7 +38,9 @@ func (widget *TextWidget) Redraw(data func() (string, string, bool)) {
widget.View.Clear()
widget.View.SetWrap(wrap)
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)
})
}