package main import ( "fmt" "log" "net/http" "os" "golang.org/x/net/html" "gioui.org/app" "gioui.org/font/gofont" "gioui.org/io/system" "gioui.org/layout" "gioui.org/op" "gioui.org/unit" "gioui.org/widget/material" ) // FeedItem represents a single RSS entry type FeedItem struct { Title string Link string } // Simple RSS fetcher (parses only and <link>) func fetchRSS(url string) ([]FeedItem, error) { resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() doc, err := html.Parse(resp.Body) if err != nil { return nil, err } var items []FeedItem var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "item" { var item FeedItem for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == html.ElementNode { switch c.Data { case "title": if c.FirstChild != nil { item.Title = c.FirstChild.Data } case "link": if c.FirstChild != nil { item.Link = c.FirstChild.Data } } } } items = append(items, item) } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) return items, nil } func main() { go func() { w := app.NewWindow( app.Title("MiniRSS"), app.Size(unit.Dp(400), unit.Dp(600)), ) if err := loop(w); err != nil { log.Fatal(err) } os.Exit(0) }() app.Main() } func loop(w *app.Window) error { th := material.NewTheme(gofont.Collection()) // Replace with any RSS feed you want to test feedURL := "https://xkcd.com/atom.xml" items, err := fetchRSS(feedURL) if err != nil { return fmt.Errorf("failed to fetch RSS: %w", err) } list := layout.List{ Axis: layout.Vertical, } var ops op.Ops for { e := <-w.Events() switch e := e.(type) { case system.DestroyEvent: return e.Err case system.FrameEvent: gtx := layout.NewContext(&ops, e) list.Layout(gtx, len(items), func(gtx layout.Context, i int) layout.Dimensions { title := material.H6(th, items[i].Title) return title.Layout(gtx) }) e.Frame(gtx.Ops) } } }