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