commit f1465a26a1cd01bb55d19511cfc632e9c7c2ab4d Author: Kalle Carlbark Date: Fri Aug 9 22:37:19 2019 +0200 Initial commit. diff --git a/hummhumm.go b/hummhumm.go new file mode 100644 index 0000000..4bb532e --- /dev/null +++ b/hummhumm.go @@ -0,0 +1,210 @@ +package main + +import ( + "encoding/json" + "fmt" + termui "github.com/gizak/termui/v3" + "github.com/gizak/termui/v3/widgets" + "io/ioutil" + "log" + // "math" + "net/http" + "strconv" + "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" + +func (h *HnStories) populateStories(numstories int, done chan bool, widget *widgets.Gauge) { + var item string + var itemUrl string + var counter int = 1 + for _, id := range h.Items[:numstories] { + item = strconv.Itoa(id) + 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 item HnItem + + json.Unmarshal(storydata, &item) + + h.Stories = append(h.Stories, HnItem{ + By: item.By, + Id: item.Id, + Added: item.Added, + Title: item.Title, + Type: item.Type, + Url: item.Url, + Kids: item.Kids, + }) + counter++ + fcounter := float64(counter) + fnumstories := float64(numstories) + percentage := (fcounter / fnumstories) * 100 + widget.Percent = int(percentage) + termui.Render(widget) + } + widget.Percent = 100 + done <- true +} +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, done chan []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++ + } + done <- 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 numstories = 10 + done1 := make(chan bool, 10) + done2 := make(chan bool, 10) + g0.Percent = 1 + termui.Render(g0) + go a.httpHNFetchIds(hnurl, done1) + <-done1 + go a.populateStories(numstories, done2, g0) + <-done2 + termui.Clear() + l := widgets.NewList() + //l.Title = "[Y] hacker news" + termuiitems := make(chan []string, numstories) + go a.httpHNDisplayStories(numstories, termuiitems) + l.Border = false + l.SetRect(0, 1, x, y) + l.Rows = <-termuiitems + l.TextStyle = termui.NewStyle(termui.ColorYellow) + l.WrapText = true + x, y = termui.TerminalDimensions() + 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", "": + return + case "j", "": + l.ScrollDown() + case "k", "": + l.ScrollUp() + case "": + l.ScrollHalfPageDown() + case "": + l.ScrollHalfPageUp() + case "": + l.ScrollPageDown() + case "": + l.ScrollPageUp() + case "g": + if previousKey == "g" { + l.ScrollTop() + } + case "": + l.ScrollTop() + case "G", "": + l.ScrollBottom() + case "": + 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) + } +}