mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
WTF-42 WIP Add FeedReader, an RSS/Atom feed reader
This commit is contained in:
parent
9d38f5439d
commit
f19f1ee86d
1
go.mod
1
go.mod
@ -26,6 +26,7 @@ require (
|
|||||||
github.com/jessevdk/go-flags v1.4.0
|
github.com/jessevdk/go-flags v1.4.0
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.8 // indirect
|
github.com/mattn/go-isatty v0.0.8 // indirect
|
||||||
|
github.com/mmcdole/gofeed v1.0.0-beta2.0.20190420154928-0e68beaf6fdf
|
||||||
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4
|
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4
|
||||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||||
github.com/onsi/gomega v1.5.0 // indirect
|
github.com/onsi/gomega v1.5.0 // indirect
|
||||||
|
12
go.sum
12
go.sum
@ -11,6 +11,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFD
|
|||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/PagerDuty/go-pagerduty v0.0.0-20190503230806-cf1437c7c8d6 h1:JucouG/P7B+i18/RJbFpbqJyaserYaQzFMlfK/eIEY8=
|
github.com/PagerDuty/go-pagerduty v0.0.0-20190503230806-cf1437c7c8d6 h1:JucouG/P7B+i18/RJbFpbqJyaserYaQzFMlfK/eIEY8=
|
||||||
github.com/PagerDuty/go-pagerduty v0.0.0-20190503230806-cf1437c7c8d6/go.mod h1:6hH58nzwYc9mw+TPyM1anW0ivbI0ti4lYc+ZBaKmWts=
|
github.com/PagerDuty/go-pagerduty v0.0.0-20190503230806-cf1437c7c8d6/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/StackExchange/wmi v0.0.0-20190523213609-cbe66965904d h1:VWP4o43LuzNbykZJzMUv5b9DWLgn0sn3GUj3RUyWMMQ=
|
github.com/StackExchange/wmi v0.0.0-20190523213609-cbe66965904d h1:VWP4o43LuzNbykZJzMUv5b9DWLgn0sn3GUj3RUyWMMQ=
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213609-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20190523213609-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/adlio/trello v1.0.0 h1:7Mp6DnNXBHBAdhfcutftFDnX7K/G9yEtScAEplJzu+0=
|
github.com/adlio/trello v1.0.0 h1:7Mp6DnNXBHBAdhfcutftFDnX7K/G9yEtScAEplJzu+0=
|
||||||
@ -26,6 +28,8 @@ 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/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E=
|
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E=
|
||||||
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/andygrunwald/go-gerrit v0.0.0-20190625080919-64931d233c2d h1:VqtwQ/1Q39dznaTGQXmPzwdTbqKd2jdlfNgawTVM6YU=
|
github.com/andygrunwald/go-gerrit v0.0.0-20190625080919-64931d233c2d h1:VqtwQ/1Q39dznaTGQXmPzwdTbqKd2jdlfNgawTVM6YU=
|
||||||
github.com/andygrunwald/go-gerrit v0.0.0-20190625080919-64931d233c2d/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
|
github.com/andygrunwald/go-gerrit v0.0.0-20190625080919-64931d233c2d/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
|
||||||
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d h1:28xWzPQ9bdGxKAAwQpZipZZ9Xz8kQcgMPF9cZnvMeuI=
|
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d h1:28xWzPQ9bdGxKAAwQpZipZZ9Xz8kQcgMPF9cZnvMeuI=
|
||||||
@ -33,6 +37,7 @@ github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d/go.mod h
|
|||||||
github.com/cenkalti/backoff v2.2.0+incompatible h1:8qVbEY6GLhoLlLi1Ac2ZkVhedNwlhQXc39qivKp9+GI=
|
github.com/cenkalti/backoff v2.2.0+incompatible h1:8qVbEY6GLhoLlLi1Ac2ZkVhedNwlhQXc39qivKp9+GI=
|
||||||
github.com/cenkalti/backoff v2.2.0+incompatible/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
github.com/cenkalti/backoff v2.2.0+incompatible/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
||||||
github.com/darkSasori/todoist v0.0.0-20180703032645-ec6b38b374ab h1:T9EEtA6FSJMVypkNlKjrRo04u1j5Tk+gghymflyivDw=
|
github.com/darkSasori/todoist v0.0.0-20180703032645-ec6b38b374ab h1:T9EEtA6FSJMVypkNlKjrRo04u1j5Tk+gghymflyivDw=
|
||||||
@ -103,6 +108,10 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE
|
|||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mmcdole/gofeed v1.0.0-beta2.0.20190420154928-0e68beaf6fdf h1:poo3e5STwUVGyyUX0e3fHQUwT1tV8IYEFUUpYd/7LuA=
|
||||||
|
github.com/mmcdole/gofeed v1.0.0-beta2.0.20190420154928-0e68beaf6fdf/go.mod h1:tkVcyzS3qVMlQrQxJoEH1hkTiuo9a8emDzkMi7TZBu0=
|
||||||
|
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
|
||||||
|
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
|
||||||
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4 h1:JnVsYEQzhEcOspy6ngIYNF2u0h2mjkXZptzX0IzZQ4g=
|
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4 h1:JnVsYEQzhEcOspy6ngIYNF2u0h2mjkXZptzX0IzZQ4g=
|
||||||
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4/go.mod h1:RL5+WRxWTAXqqCi9i+eZlHrUtO7AQujUqWi+xMohmc4=
|
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4/go.mod h1:RL5+WRxWTAXqqCi9i+eZlHrUtO7AQujUqWi+xMohmc4=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@ -154,10 +163,13 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
|||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/wtfutil/wtf/modules/cryptoexchanges/blockfolio"
|
"github.com/wtfutil/wtf/modules/cryptoexchanges/blockfolio"
|
||||||
"github.com/wtfutil/wtf/modules/cryptoexchanges/cryptolive"
|
"github.com/wtfutil/wtf/modules/cryptoexchanges/cryptolive"
|
||||||
"github.com/wtfutil/wtf/modules/datadog"
|
"github.com/wtfutil/wtf/modules/datadog"
|
||||||
|
"github.com/wtfutil/wtf/modules/feedreader"
|
||||||
"github.com/wtfutil/wtf/modules/gcal"
|
"github.com/wtfutil/wtf/modules/gcal"
|
||||||
"github.com/wtfutil/wtf/modules/gerrit"
|
"github.com/wtfutil/wtf/modules/gerrit"
|
||||||
"github.com/wtfutil/wtf/modules/git"
|
"github.com/wtfutil/wtf/modules/git"
|
||||||
@ -92,6 +93,9 @@ func MakeWidget(
|
|||||||
case "datadog":
|
case "datadog":
|
||||||
settings := datadog.NewSettingsFromYAML(widgetName, moduleConfig, globalConfig)
|
settings := datadog.NewSettingsFromYAML(widgetName, moduleConfig, globalConfig)
|
||||||
widget = datadog.NewWidget(app, pages, settings)
|
widget = datadog.NewWidget(app, pages, settings)
|
||||||
|
case "feedreader":
|
||||||
|
settings := feedreader.NewSettingsFromYAML(widgetName, moduleConfig, globalConfig)
|
||||||
|
widget = feedreader.NewWidget(app, pages, settings)
|
||||||
case "gcal":
|
case "gcal":
|
||||||
settings := gcal.NewSettingsFromYAML(widgetName, moduleConfig, globalConfig)
|
settings := gcal.NewSettingsFromYAML(widgetName, moduleConfig, globalConfig)
|
||||||
widget = gcal.NewWidget(app, settings)
|
widget = gcal.NewWidget(app, settings)
|
||||||
|
16
modules/feedreader/keyboard.go
Normal file
16
modules/feedreader/keyboard.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package feedreader
|
||||||
|
|
||||||
|
import "github.com/gdamore/tcell"
|
||||||
|
|
||||||
|
func (widget *Widget) initializeKeyboardControls() {
|
||||||
|
widget.SetKeyboardChar("/", widget.ShowHelp, "Show/hide this help widget")
|
||||||
|
widget.SetKeyboardChar("r", widget.Refresh, "Refresh widget")
|
||||||
|
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.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
|
||||||
|
widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
|
||||||
|
widget.SetKeyboardKey(tcell.KeyEnter, widget.openStory, "Open story in browser")
|
||||||
|
widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
|
||||||
|
}
|
31
modules/feedreader/settings.go
Normal file
31
modules/feedreader/settings.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package feedreader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/olebedev/config"
|
||||||
|
"github.com/wtfutil/wtf/cfg"
|
||||||
|
"github.com/wtfutil/wtf/wtf"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultTitle = "Feed Reader"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Settings defines the configuration properties for this module
|
||||||
|
type Settings struct {
|
||||||
|
common *cfg.Common
|
||||||
|
|
||||||
|
feeds []string
|
||||||
|
feedLimit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, ymlConfig, globalConfig),
|
||||||
|
|
||||||
|
feeds: wtf.ToStrs(ymlConfig.UList("feeds")),
|
||||||
|
feedLimit: ymlConfig.UInt("feedLimit", -1),
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings
|
||||||
|
}
|
165
modules/feedreader/widget.go
Normal file
165
modules/feedreader/widget.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package feedreader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/mmcdole/gofeed"
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
"github.com/wtfutil/wtf/wtf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FeedItem represents an item returned from an RSS or Atom feed
|
||||||
|
type FeedItem struct {
|
||||||
|
item *gofeed.Item
|
||||||
|
viewed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget is the container for RSS and Atom data
|
||||||
|
type Widget struct {
|
||||||
|
wtf.KeyboardWidget
|
||||||
|
wtf.ScrollableWidget
|
||||||
|
|
||||||
|
stories []*FeedItem
|
||||||
|
parser *gofeed.Parser
|
||||||
|
settings *Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWidget creates a new instance of a widget
|
||||||
|
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
|
||||||
|
widget := &Widget{
|
||||||
|
KeyboardWidget: wtf.NewKeyboardWidget(app, pages, settings.common),
|
||||||
|
ScrollableWidget: wtf.NewScrollableWidget(app, settings.common, true),
|
||||||
|
|
||||||
|
parser: gofeed.NewParser(),
|
||||||
|
settings: settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.SetRenderFunction(widget.Render)
|
||||||
|
widget.initializeKeyboardControls()
|
||||||
|
widget.View.SetInputCapture(widget.InputCapture)
|
||||||
|
|
||||||
|
widget.KeyboardWidget.SetView(widget.View)
|
||||||
|
|
||||||
|
return widget
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- Exported Functions -------------------- */
|
||||||
|
|
||||||
|
// Fetch retrieves RSS and Atom feed data
|
||||||
|
func (widget *Widget) Fetch(feedURLs []string) ([]*FeedItem, error) {
|
||||||
|
data := []*FeedItem{}
|
||||||
|
|
||||||
|
for _, feedURL := range feedURLs {
|
||||||
|
feedItems, err := widget.fetchForFeed(feedURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, feedItem := range feedItems {
|
||||||
|
data = append(data, feedItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = widget.sort(data)
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh updates the data in the widget
|
||||||
|
func (widget *Widget) Refresh() {
|
||||||
|
feedItems, err := widget.Fetch(widget.settings.feeds)
|
||||||
|
if err != nil {
|
||||||
|
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.stories = feedItems
|
||||||
|
widget.SetItemCount(len(feedItems))
|
||||||
|
|
||||||
|
widget.Render()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render sets up the widget data for redrawing to the screen
|
||||||
|
func (widget *Widget) Render() {
|
||||||
|
if widget.stories == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
title := widget.CommonSettings.Title
|
||||||
|
widget.Redraw(title, widget.contentFrom(widget.stories), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- Unexported Functions -------------------- */
|
||||||
|
|
||||||
|
func (widget *Widget) fetchForFeed(feedURL string) ([]*FeedItem, error) {
|
||||||
|
feed, err := widget.parser.ParseURL(feedURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
feedItems := []*FeedItem{}
|
||||||
|
|
||||||
|
for idx, gofeedItem := range feed.Items {
|
||||||
|
if widget.settings.feedLimit >= 1 && idx >= widget.settings.feedLimit {
|
||||||
|
// We only want to get the widget.settings.feedLimit latest articles,
|
||||||
|
// not all of them. To get all, set feedLimit to < 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
feedItem := &FeedItem{
|
||||||
|
item: gofeedItem,
|
||||||
|
viewed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
feedItems = append(feedItems, feedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
return feedItems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget *Widget) contentFrom(data []*FeedItem) string {
|
||||||
|
var str string
|
||||||
|
|
||||||
|
for idx, feedItem := range data {
|
||||||
|
rowColor := widget.RowColor(idx)
|
||||||
|
|
||||||
|
if feedItem.viewed {
|
||||||
|
// Grays out viewed items in the list, while preserving background highlighting when selected
|
||||||
|
rowColor = "gray"
|
||||||
|
if idx == widget.Selected {
|
||||||
|
rowColor = fmt.Sprintf("gray:%s", widget.settings.common.Colors.HighlightBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row := fmt.Sprintf(
|
||||||
|
"[%s]%2d. %s[white]",
|
||||||
|
rowColor,
|
||||||
|
idx+1,
|
||||||
|
feedItem.item.Title,
|
||||||
|
)
|
||||||
|
|
||||||
|
str += wtf.HighlightableHelper(widget.View, row, idx, len(feedItem.item.Title))
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.Published < feedItems[j].item.Published
|
||||||
|
})
|
||||||
|
|
||||||
|
return feedItems
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget *Widget) openStory() {
|
||||||
|
sel := widget.GetSelected()
|
||||||
|
|
||||||
|
if sel >= 0 && widget.stories != nil && sel < len(widget.stories) {
|
||||||
|
story := widget.stories[sel]
|
||||||
|
story.viewed = true
|
||||||
|
|
||||||
|
wtf.OpenFile(story.item.Link)
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ type Widget struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
|
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
|
||||||
widget := Widget{
|
widget := &Widget{
|
||||||
KeyboardWidget: wtf.NewKeyboardWidget(app, pages, settings.common),
|
KeyboardWidget: wtf.NewKeyboardWidget(app, pages, settings.common),
|
||||||
ScrollableWidget: wtf.NewScrollableWidget(app, settings.common, true),
|
ScrollableWidget: wtf.NewScrollableWidget(app, settings.common, true),
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
|
|||||||
|
|
||||||
widget.KeyboardWidget.SetView(widget.View)
|
widget.KeyboardWidget.SetView(widget.View)
|
||||||
|
|
||||||
return &widget
|
return widget
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- Exported Functions -------------------- */
|
/* -------------------- Exported Functions -------------------- */
|
||||||
@ -53,9 +53,7 @@ func (widget *Widget) Refresh() {
|
|||||||
var stories []Story
|
var stories []Story
|
||||||
for idx := 0; idx < widget.settings.numberOfStories; idx++ {
|
for idx := 0; idx < widget.settings.numberOfStories; idx++ {
|
||||||
story, e := GetStory(storyIds[idx])
|
story, e := GetStory(storyIds[idx])
|
||||||
if e != nil {
|
if e == nil {
|
||||||
// panic(e)
|
|
||||||
} else {
|
|
||||||
stories = append(stories, story)
|
stories = append(stories, story)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,8 +64,7 @@ func (widget *Widget) Refresh() {
|
|||||||
widget.Render()
|
widget.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- Unexported Functions -------------------- */
|
// Render sets up the widget data for redrawing to the screen
|
||||||
|
|
||||||
func (widget *Widget) Render() {
|
func (widget *Widget) Render() {
|
||||||
if widget.stories == nil {
|
if widget.stories == nil {
|
||||||
return
|
return
|
||||||
@ -77,10 +74,12 @@ func (widget *Widget) Render() {
|
|||||||
widget.Redraw(title, widget.contentFrom(widget.stories), false)
|
widget.Redraw(title, widget.contentFrom(widget.stories), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------- Unexported Functions -------------------- */
|
||||||
|
|
||||||
func (widget *Widget) contentFrom(stories []Story) string {
|
func (widget *Widget) contentFrom(stories []Story) string {
|
||||||
var str string
|
var str string
|
||||||
for idx, story := range stories {
|
|
||||||
|
|
||||||
|
for idx, story := range stories {
|
||||||
u, _ := url.Parse(story.URL)
|
u, _ := url.Parse(story.URL)
|
||||||
|
|
||||||
row := fmt.Sprintf(
|
row := fmt.Sprintf(
|
||||||
|
@ -29,7 +29,7 @@ type Settings struct {
|
|||||||
|
|
||||||
// NewSettingsFromYAML creates a new settings instance from a YAML config block
|
// NewSettingsFromYAML creates a new settings instance from a YAML config block
|
||||||
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||||
settings := Settings{
|
settings := &Settings{
|
||||||
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, ymlConfig, globalConfig),
|
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, ymlConfig, globalConfig),
|
||||||
|
|
||||||
accounts: wtf.ToStrs(ymlConfig.UList("accounts")),
|
accounts: wtf.ToStrs(ymlConfig.UList("accounts")),
|
||||||
@ -45,7 +45,7 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co
|
|||||||
settings.common.RefreshInterval = minRefreshInterval
|
settings.common.RefreshInterval = minRefreshInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
return &settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSince returns TRUE if there's a valid "since" value setting, FALSE if there is not
|
// HasSince returns TRUE if there's a valid "since" value setting, FALSE if there is not
|
||||||
|
@ -72,7 +72,7 @@ func (widget *Widget) contentFrom(data []*Status) string {
|
|||||||
color = widget.settings.colors.pwned
|
color = widget.settings.colors.pwned
|
||||||
}
|
}
|
||||||
|
|
||||||
str = str + fmt.Sprintf(" [%s]%s[white]\n", color, stat.Account)
|
str += fmt.Sprintf(" [%s]%s[white]\n", color, stat.Account)
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
return str
|
||||||
|
Loading…
x
Reference in New Issue
Block a user