Rename app. Also add Lobsters as tab#2.
This commit is contained in:
parent
a92846a8eb
commit
4cb5097341
2 changed files with 441 additions and 229 deletions
441
hmhm.go
Normal file
441
hmhm.go
Normal file
|
@ -0,0 +1,441 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
termui "github.com/gizak/termui/v3"
|
||||||
|
"github.com/gizak/termui/v3/widgets"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
// "math"
|
||||||
|
"net/http"
|
||||||
|
//"sort"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HnStories struct {
|
||||||
|
Stories []HnItem
|
||||||
|
Items []int
|
||||||
|
List []string
|
||||||
|
}
|
||||||
|
type HnItem struct {
|
||||||
|
By string `json:"by,omitempty"`
|
||||||
|
Descendants int `json:"descendants,omitempty"`
|
||||||
|
Id int `json:"id,omitempty"`
|
||||||
|
Kids []int `json:"kids,omitempty"`
|
||||||
|
Score int `json:"score,omitempty"`
|
||||||
|
Added int `json:"time,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Url string `json:"url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LStories struct {
|
||||||
|
Stories []LobsterItem
|
||||||
|
List []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type LobsterItem struct {
|
||||||
|
ShortID string `json:"short_id"`
|
||||||
|
ShortIDURL string `json:"short_id_url"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Score int `json:"score"`
|
||||||
|
Upvotes int `json:"upvotes"`
|
||||||
|
Downvotes int `json:"downvotes"`
|
||||||
|
CommentCount int `json:"comment_count"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
CommentsURL string `json:"comments_url"`
|
||||||
|
SubmitterUser struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
About string `json:"about"`
|
||||||
|
IsModerator bool `json:"is_moderator"`
|
||||||
|
Karma int `json:"karma"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
InvitedByUser string `json:"invited_by_user"`
|
||||||
|
} `json:"submitter_user"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrlHN string = "https://hacker-news.firebaseio.com/v0"
|
||||||
|
const apiUrlL string = "https://lobste.rs/hottest.json"
|
||||||
|
const mpRSS string = "https://blog.acolyer.org/feed/"
|
||||||
|
const hnumstories int = 30
|
||||||
|
const lnumstories int = 25
|
||||||
|
const refreshrate time.Duration = 300
|
||||||
|
|
||||||
|
func (l *LStories) fetchLStories() {
|
||||||
|
timeout := time.Duration(10 * time.Second)
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest("GET", apiUrlL, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
response, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
storydata, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Unmarshal(storydata, &l.Stories)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HnStories) fetchHNStories(item string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
var itemUrl string
|
||||||
|
itemUrl = apiUrlHN + "/item/" + item + ".json"
|
||||||
|
timeout := time.Duration(10 * time.Second)
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
}
|
||||||
|
request, err := http.NewRequest("GET", itemUrl, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
response, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
storydata, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var story HnItem
|
||||||
|
|
||||||
|
json.Unmarshal(storydata, &story)
|
||||||
|
|
||||||
|
h.Stories = append(h.Stories, HnItem{
|
||||||
|
By: story.By,
|
||||||
|
Id: story.Id,
|
||||||
|
Added: story.Added,
|
||||||
|
Title: story.Title,
|
||||||
|
Type: story.Type,
|
||||||
|
Url: story.Url,
|
||||||
|
Kids: story.Kids,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HnStories) httpHNFetchIds(done chan bool) {
|
||||||
|
timeout := time.Duration(5 * time.Second)
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
}
|
||||||
|
request, err := http.NewRequest("GET", apiUrlHN+"/topstories.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
response, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
d := json.NewDecoder(response.Body)
|
||||||
|
err = d.Decode(&h.Items)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HnStories) httpHNDisplayStories(wg *sync.WaitGroup) {
|
||||||
|
wg.Add(1)
|
||||||
|
var counter int = 1
|
||||||
|
for _, item := range h.Stories[:hnumstories] {
|
||||||
|
itemstring := fmt.Sprintf("%-6s %-6s | %d comments", "["+strconv.Itoa(counter)+".](fg:white,bold)", "["+item.Title+"](fg:green)", len(item.Kids))
|
||||||
|
h.List = append(h.List, itemstring)
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (l *LStories) listLStories() {
|
||||||
|
var counter int = 1
|
||||||
|
for _, item := range l.Stories[:lnumstories] {
|
||||||
|
itemstring := fmt.Sprintf("%-6s %-6s | %d comments", "["+strconv.Itoa(counter)+".](fg:white,bold)", "["+item.Title+"](fg:green)", item.CommentCount)
|
||||||
|
l.List = append(l.List, itemstring)
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HnStories) populateHNStories(gauge *widgets.Gauge, wg *sync.WaitGroup) {
|
||||||
|
done := make(chan bool)
|
||||||
|
go h.httpHNFetchIds(done)
|
||||||
|
<-done
|
||||||
|
var counter int = 1
|
||||||
|
for _, item := range h.Items[:hnumstories] {
|
||||||
|
wg.Add(1)
|
||||||
|
go h.fetchHNStories(strconv.Itoa(item), wg)
|
||||||
|
fcounter := float64(counter)
|
||||||
|
fnumstories := float64(hnumstories)
|
||||||
|
percentage := (fcounter / fnumstories) * 100
|
||||||
|
gauge.Percent = int(percentage)
|
||||||
|
termui.Render(gauge)
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
go h.httpHNDisplayStories(wg)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
func (h *HnStories) clearHNStruct() {
|
||||||
|
h = nil
|
||||||
|
}
|
||||||
|
func (l *LStories) clearLStruct() {
|
||||||
|
l = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := termui.Init(); err != nil {
|
||||||
|
log.Fatalf("failed to initialize termui: %v", err)
|
||||||
|
}
|
||||||
|
defer termui.Close()
|
||||||
|
x, y := termui.TerminalDimensions()
|
||||||
|
tabpane := widgets.NewTabPane("[Y] Hacker News", "[L] Lobste.rs")
|
||||||
|
tabpane.SetRect(0, 0, x, 1)
|
||||||
|
tabpane.Border = false
|
||||||
|
g0 := widgets.NewGauge()
|
||||||
|
g0.Title = "Fetching Stories.."
|
||||||
|
g0.SetRect(0, 3, x, 6)
|
||||||
|
g0.BarColor = termui.ColorYellow
|
||||||
|
g0.LabelStyle = termui.NewStyle(termui.ColorBlue)
|
||||||
|
g0.BorderStyle.Fg = termui.ColorWhite
|
||||||
|
g0.Percent = 1
|
||||||
|
termui.Render(g0)
|
||||||
|
var h HnStories
|
||||||
|
var l LStories
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
l.fetchLStories()
|
||||||
|
l.listLStories()
|
||||||
|
h.populateHNStories(g0, &wg)
|
||||||
|
termui.Clear()
|
||||||
|
hlist := widgets.NewList()
|
||||||
|
hlist.Border = false
|
||||||
|
x, y = termui.TerminalDimensions()
|
||||||
|
hlist.SetRect(0, 1, x, y)
|
||||||
|
hlist.TextStyle = termui.NewStyle(termui.ColorYellow)
|
||||||
|
hlist.WrapText = true
|
||||||
|
hlist.Rows = h.List
|
||||||
|
|
||||||
|
llist := widgets.NewList()
|
||||||
|
llist.Border = false
|
||||||
|
x, y = termui.TerminalDimensions()
|
||||||
|
llist.SetRect(0, 1, x, y)
|
||||||
|
llist.TextStyle = termui.NewStyle(termui.ColorYellow)
|
||||||
|
llist.WrapText = true
|
||||||
|
llist.Rows = l.List
|
||||||
|
renderTab := func() {
|
||||||
|
switch tabpane.ActiveTabIndex {
|
||||||
|
case 0:
|
||||||
|
termui.Render(hlist)
|
||||||
|
case 1:
|
||||||
|
termui.Render(llist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
termui.Render(tabpane, hlist)
|
||||||
|
|
||||||
|
ticker := time.NewTicker(refreshrate * time.Second).C
|
||||||
|
previousKey := ""
|
||||||
|
uiEvents := termui.PollEvents()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-uiEvents:
|
||||||
|
switch e.ID {
|
||||||
|
case "h":
|
||||||
|
tabpane.FocusLeft()
|
||||||
|
termui.Clear()
|
||||||
|
termui.Render(tabpane)
|
||||||
|
renderTab()
|
||||||
|
case "l":
|
||||||
|
tabpane.FocusRight()
|
||||||
|
termui.Clear()
|
||||||
|
termui.Render(tabpane)
|
||||||
|
renderTab()
|
||||||
|
case "q", "<C-c>":
|
||||||
|
return
|
||||||
|
case "r":
|
||||||
|
termui.Clear()
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
h.clearHNStruct()
|
||||||
|
g0.Title = "Refreshing HN Stories.."
|
||||||
|
g0.Percent = 1
|
||||||
|
var wg2 sync.WaitGroup
|
||||||
|
h.populateHNStories(g0, &wg2)
|
||||||
|
hlist := widgets.NewList()
|
||||||
|
hlist.Border = false
|
||||||
|
x, y = termui.TerminalDimensions()
|
||||||
|
hlist.SetRect(0, 1, x, y)
|
||||||
|
hlist.TextStyle = termui.NewStyle(termui.ColorYellow)
|
||||||
|
hlist.WrapText = true
|
||||||
|
hlist.Rows = h.List
|
||||||
|
} else if tabpane.ActiveTabIndex == 1 {
|
||||||
|
l.clearLStruct()
|
||||||
|
g0.Title = "Refreshing Lobster Stories.."
|
||||||
|
g0.Percent = 100
|
||||||
|
termui.Render(g0)
|
||||||
|
l.fetchLStories()
|
||||||
|
l.listLStories()
|
||||||
|
llist := widgets.NewList()
|
||||||
|
llist.Border = false
|
||||||
|
x, y = termui.TerminalDimensions()
|
||||||
|
llist.SetRect(0, 1, x, y)
|
||||||
|
llist.TextStyle = termui.NewStyle(termui.ColorYellow)
|
||||||
|
llist.WrapText = true
|
||||||
|
llist.Rows = l.List
|
||||||
|
}
|
||||||
|
case "j", "<Down>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollDown()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollDown()
|
||||||
|
}
|
||||||
|
case "k", "<Up>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollUp()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollUp()
|
||||||
|
}
|
||||||
|
case "<C-d>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollHalfPageDown()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollHalfPageDown()
|
||||||
|
}
|
||||||
|
case "<C-u>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollHalfPageUp()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollHalfPageUp()
|
||||||
|
}
|
||||||
|
case "<C-f>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollPageDown()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollPageDown()
|
||||||
|
}
|
||||||
|
case "<C-b>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollPageUp()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollPageUp()
|
||||||
|
}
|
||||||
|
case "g":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollDown()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollDown()
|
||||||
|
}
|
||||||
|
if previousKey == "g" {
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollTop()
|
||||||
|
} else if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollTop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "<Home>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollTop()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollTop()
|
||||||
|
}
|
||||||
|
case "G", "<End>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
hlist.ScrollBottom()
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 1 {
|
||||||
|
llist.ScrollBottom()
|
||||||
|
}
|
||||||
|
case "<Enter>":
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
url := h.Stories[hlist.SelectedRow].Url
|
||||||
|
openbrowser(url)
|
||||||
|
} else if tabpane.ActiveTabIndex == 1 {
|
||||||
|
url := l.Stories[llist.SelectedRow].Url
|
||||||
|
openbrowser(url)
|
||||||
|
}
|
||||||
|
case "<Resize>":
|
||||||
|
payload := e.Payload.(termui.Resize)
|
||||||
|
hlist.SetRect(0, 1, payload.Width, payload.Height)
|
||||||
|
termui.Clear()
|
||||||
|
termui.Render(tabpane, hlist, llist)
|
||||||
|
}
|
||||||
|
|
||||||
|
if previousKey == "g" {
|
||||||
|
previousKey = ""
|
||||||
|
} else {
|
||||||
|
previousKey = e.ID
|
||||||
|
}
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
termui.Render(tabpane, hlist)
|
||||||
|
} else if tabpane.ActiveTabIndex == 1 {
|
||||||
|
termui.Render(tabpane, llist)
|
||||||
|
}
|
||||||
|
case <-ticker:
|
||||||
|
termui.Clear()
|
||||||
|
if tabpane.ActiveTabIndex == 0 {
|
||||||
|
h.clearHNStruct()
|
||||||
|
g0.Title = "Refreshing HN Stories.."
|
||||||
|
g0.Percent = 1
|
||||||
|
var wg2 sync.WaitGroup
|
||||||
|
h.populateHNStories(g0, &wg2)
|
||||||
|
hlist := widgets.NewList()
|
||||||
|
hlist.Border = false
|
||||||
|
x, y = termui.TerminalDimensions()
|
||||||
|
hlist.SetRect(0, 1, x, y)
|
||||||
|
hlist.TextStyle = termui.NewStyle(termui.ColorYellow)
|
||||||
|
hlist.WrapText = true
|
||||||
|
hlist.Rows = h.List
|
||||||
|
termui.Render(tabpane, hlist)
|
||||||
|
} else if tabpane.ActiveTabIndex == 1 {
|
||||||
|
l.clearLStruct()
|
||||||
|
g0.Title = "Refreshing Lobster Stories.."
|
||||||
|
g0.Percent = 100
|
||||||
|
termui.Render(g0)
|
||||||
|
l.fetchLStories()
|
||||||
|
l.listLStories()
|
||||||
|
llist := widgets.NewList()
|
||||||
|
llist.Border = false
|
||||||
|
x, y = termui.TerminalDimensions()
|
||||||
|
llist.SetRect(0, 1, x, y)
|
||||||
|
llist.TextStyle = termui.NewStyle(termui.ColorYellow)
|
||||||
|
llist.WrapText = true
|
||||||
|
llist.Rows = l.List
|
||||||
|
termui.Render(tabpane, llist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func openbrowser(url string) {
|
||||||
|
var err error
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
|
err = exec.Command("xdg-open", url).Start()
|
||||||
|
case "windows":
|
||||||
|
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||||
|
case "darwin":
|
||||||
|
err = exec.Command("open", "-a", "Safari", url).Start()
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported platform")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
229
hummhumm.go
229
hummhumm.go
|
@ -1,229 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
termui "github.com/gizak/termui/v3"
|
|
||||||
"github.com/gizak/termui/v3/widgets"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
// "math"
|
|
||||||
"net/http"
|
|
||||||
//"sort"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HnStories struct {
|
|
||||||
Stories []HnItem
|
|
||||||
Items []int
|
|
||||||
}
|
|
||||||
type HnItem struct {
|
|
||||||
By string `json:"by,omitempty"`
|
|
||||||
Descendants int `json:"descendants,omitempty"`
|
|
||||||
Id int `json:"id,omitempty"`
|
|
||||||
Kids []int `json:"kids,omitempty"`
|
|
||||||
Score int `json:"score,omitempty"`
|
|
||||||
Added int `json:"time,omitempty"`
|
|
||||||
Title string `json:"title,omitempty"`
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
Url string `json:"url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiUrl string = "https://hacker-news.firebaseio.com/v0"
|
|
||||||
const numstories int = 30
|
|
||||||
|
|
||||||
func (h *HnStories) fetchHNStories(item string, wg *sync.WaitGroup) {
|
|
||||||
defer wg.Done()
|
|
||||||
var itemUrl string
|
|
||||||
itemUrl = apiUrl + "/item/" + item + ".json"
|
|
||||||
timeout := time.Duration(5 * time.Second)
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: timeout,
|
|
||||||
}
|
|
||||||
request, err := http.NewRequest("GET", itemUrl, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
response, err := client.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
storydata, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var story HnItem
|
|
||||||
|
|
||||||
json.Unmarshal(storydata, &story)
|
|
||||||
|
|
||||||
h.Stories = append(h.Stories, HnItem{
|
|
||||||
By: story.By,
|
|
||||||
Id: story.Id,
|
|
||||||
Added: story.Added,
|
|
||||||
Title: story.Title,
|
|
||||||
Type: story.Type,
|
|
||||||
Url: story.Url,
|
|
||||||
Kids: story.Kids,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HnStories) httpHNFetchIds(url string, done chan bool) {
|
|
||||||
timeout := time.Duration(5 * time.Second)
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: timeout,
|
|
||||||
}
|
|
||||||
request, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
response, err := client.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
d := json.NewDecoder(response.Body)
|
|
||||||
err = d.Decode(&h.Items)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
done <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HnStories) httpHNDisplayStories(numstories int) []string {
|
|
||||||
counter := 1
|
|
||||||
var termuiitems []string
|
|
||||||
for _, item := range h.Stories[:numstories] {
|
|
||||||
itemstring := fmt.Sprintf("%-6s %-6s | %d comments", "["+strconv.Itoa(counter)+".](fg:white,bold)", "["+item.Title+"](fg:green)", len(item.Kids))
|
|
||||||
termuiitems = append(termuiitems, itemstring)
|
|
||||||
counter++
|
|
||||||
}
|
|
||||||
return termuiitems
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if err := termui.Init(); err != nil {
|
|
||||||
log.Fatalf("failed to initialize termui: %v", err)
|
|
||||||
}
|
|
||||||
defer termui.Close()
|
|
||||||
x, y := termui.TerminalDimensions()
|
|
||||||
tabpane := widgets.NewTabPane("[Y] Hacker News", "[L] Lobste.rs")
|
|
||||||
tabpane.SetRect(0, 0, x, 1)
|
|
||||||
tabpane.Border = false
|
|
||||||
g0 := widgets.NewGauge()
|
|
||||||
g0.Title = "Fetching Stories.."
|
|
||||||
g0.SetRect(0, 3, x, 6)
|
|
||||||
g0.BarColor = termui.ColorYellow
|
|
||||||
g0.LabelStyle = termui.NewStyle(termui.ColorBlue)
|
|
||||||
g0.BorderStyle.Fg = termui.ColorWhite
|
|
||||||
hnurl := apiUrl + "/topstories.json"
|
|
||||||
var a HnStories
|
|
||||||
var counter = 1
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
done1 := make(chan bool, 10)
|
|
||||||
g0.Percent = 1
|
|
||||||
termui.Render(g0)
|
|
||||||
go a.httpHNFetchIds(hnurl, done1)
|
|
||||||
<-done1
|
|
||||||
for _, item := range a.Items[:numstories] {
|
|
||||||
wg.Add(1)
|
|
||||||
go a.fetchHNStories(strconv.Itoa(item), &wg)
|
|
||||||
fcounter := float64(counter)
|
|
||||||
fnumstories := float64(numstories)
|
|
||||||
percentage := (fcounter / fnumstories) * 100
|
|
||||||
g0.Percent = int(percentage)
|
|
||||||
termui.Render(g0)
|
|
||||||
counter++
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
termui.Clear()
|
|
||||||
l := widgets.NewList()
|
|
||||||
chanitems := make(chan []string, numstories)
|
|
||||||
var items []string
|
|
||||||
//l.Title = "[Y] hacker news"
|
|
||||||
l.Border = false
|
|
||||||
x, y = termui.TerminalDimensions()
|
|
||||||
l.SetRect(0, 1, x, y)
|
|
||||||
l.TextStyle = termui.NewStyle(termui.ColorYellow)
|
|
||||||
l.WrapText = true
|
|
||||||
//sort.SliceStable(a.Stories, func(i, j int) bool {
|
|
||||||
// return a.Stories[i].Score > a.Stories[j].Score
|
|
||||||
//})
|
|
||||||
go func(items []string) {
|
|
||||||
chanitems <- a.httpHNDisplayStories(numstories)
|
|
||||||
}(items)
|
|
||||||
select {
|
|
||||||
case items := <-chanitems:
|
|
||||||
l.Rows = items
|
|
||||||
renderTab := func() {
|
|
||||||
switch tabpane.ActiveTabIndex {
|
|
||||||
case 0:
|
|
||||||
termui.Render(tabpane, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
termui.Render(tabpane, l)
|
|
||||||
|
|
||||||
previousKey := ""
|
|
||||||
uiEvents := termui.PollEvents()
|
|
||||||
for {
|
|
||||||
e := <-uiEvents
|
|
||||||
switch e.ID {
|
|
||||||
case "h":
|
|
||||||
tabpane.FocusLeft()
|
|
||||||
termui.Clear()
|
|
||||||
termui.Render(tabpane, l)
|
|
||||||
renderTab()
|
|
||||||
case "l":
|
|
||||||
tabpane.FocusRight()
|
|
||||||
termui.Clear()
|
|
||||||
termui.Render(tabpane, l)
|
|
||||||
renderTab()
|
|
||||||
case "q", "<C-c>":
|
|
||||||
return
|
|
||||||
case "r":
|
|
||||||
|
|
||||||
case "j", "<Down>":
|
|
||||||
l.ScrollDown()
|
|
||||||
case "k", "<Up>":
|
|
||||||
l.ScrollUp()
|
|
||||||
case "<C-d>":
|
|
||||||
l.ScrollHalfPageDown()
|
|
||||||
case "<C-u>":
|
|
||||||
l.ScrollHalfPageUp()
|
|
||||||
case "<C-f>":
|
|
||||||
l.ScrollPageDown()
|
|
||||||
case "<C-b>":
|
|
||||||
l.ScrollPageUp()
|
|
||||||
case "g":
|
|
||||||
if previousKey == "g" {
|
|
||||||
l.ScrollTop()
|
|
||||||
}
|
|
||||||
case "<Home>":
|
|
||||||
l.ScrollTop()
|
|
||||||
case "G", "<End>":
|
|
||||||
l.ScrollBottom()
|
|
||||||
case "<Enter>":
|
|
||||||
url := a.Stories[l.SelectedRow].Url
|
|
||||||
err := exec.Command("open", "-a", "Safari", url).Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
case "<Resize>":
|
|
||||||
payload := e.Payload.(termui.Resize)
|
|
||||||
l.SetRect(0, 1, payload.Width, payload.Height)
|
|
||||||
termui.Clear()
|
|
||||||
termui.Render(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
if previousKey == "g" {
|
|
||||||
previousKey = ""
|
|
||||||
} else {
|
|
||||||
previousKey = e.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
termui.Render(l, tabpane)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue