M db/db.go => db/db.go +10 -3
@@ 187,6 187,11 @@ func (s *Storage) Backup(path string) error {
Items: make([]barefeed.Item, len(v.Items)),
}
+ if v.Updated != nil {
+ up := barefeed.ToTimestamp(*v.Updated)
+ f.Updated = &up
+ }
+
j := 0
for k1 := range v.Items {
val, ok := s.Items[k1]
@@ 343,7 348,7 @@ func (s *Storage) Update() (Items, error) {
for _, f := range s.Feeds {
updateURL := f.UpdateURL
- items, err := f.FindNewItems(s.Items, s.GeminiPath)
+ items, updated, err := f.FindNewItems(s.Items, s.GeminiPath)
if err != nil {
if err := db.Close(); err != nil {
return nil, err
@@ 351,7 356,7 @@ func (s *Storage) Update() (Items, error) {
return nil, fmt.Errorf("error finding new items for `%s`: %s", updateURL, err.Error())
}
- if err := s.Feeds.AddItems(db, updateURL, items); err != nil {
+ if err := s.Feeds.AddItems(db, updateURL, items, updated); err != nil {
if err := db.Close(); err != nil {
return nil, err
}
@@ 471,7 476,9 @@ func (s *Storage) MarkUnread(key string) (Items, error) {
}
for k := range itemMap {
- if !s.Items[k].Read {
+ if _, ok := s.Items[k]; !ok {
+ continue
+ } else if !s.Items[k].Read {
continue
}
M db/feed.go => db/feed.go +26 -9
@@ 21,6 21,7 @@ import (
"encoding/json"
"fmt"
"sort"
+ "time"
"git.sr.ht/~chrisppy/beagles/util"
"github.com/mmcdole/gofeed"
@@ 41,9 42,17 @@ type Feed struct {
Description string `json:"description"`
UpdateURL string `json:"updateurl"`
Link string `json:"link"`
+ Updated *time.Time `json:"updated"`
Items map[string]bool `json:"items"`
}
+func getFeedUpdated(feed *gofeed.Feed) *time.Time {
+ if feed.UpdatedParsed != nil {
+ return feed.UpdatedParsed
+ }
+ return feed.PublishedParsed
+}
+
func processFeed(feed *gofeed.Feed, url string) (*Feed, error) {
if feed == nil {
return nil, nil
@@ 59,6 68,7 @@ func processFeed(feed *gofeed.Feed, url string) (*Feed, error) {
Description: d,
UpdateURL: url,
Link: feed.FeedLink,
+ Updated: getFeedUpdated(feed),
Items: make(map[string]bool),
}
@@ 75,18 85,23 @@ func processFeed(feed *gofeed.Feed, url string) (*Feed, error) {
// FindNewItems will process the items under the feed and add return any new
// ones since last update
-func (f *Feed) FindNewItems(citems map[string]*Item, gmniPath string) (map[string]*Item, error) {
+func (f *Feed) FindNewItems(citems map[string]*Item, gmniPath string) (Items, *time.Time, error) {
feed, err := util.ParseFeed(f.UpdateURL, gmniPath)
if err != nil {
- return nil, fmt.Errorf("unable to fetch url: %s", err.Error())
+ return nil, nil, fmt.Errorf("unable to fetch url: %s", err.Error())
}
+ updated := getFeedUpdated(feed)
+
if feed == nil {
- return nil, nil
+ return nil, nil, nil
+ } else if f.Updated != nil && updated != nil {
+ if f.Updated == updated {
+ return nil, nil, nil
+ }
}
- items := make(map[string]*Item)
-
+ items := make(Items)
for _, item := range feed.Items {
link := getLink(item)
@@ 96,13 111,13 @@ func (f *Feed) FindNewItems(citems map[string]*Item, gmniPath string) (map[strin
i, err := processItem(item)
if err != nil {
- return nil, err
+ return nil, nil, err
}
i.FeedURL = f.UpdateURL
items[i.Link] = i
}
- return items, nil
+ return items, updated, nil
}
// Compare will determine equality of strings
@@ 200,12 215,12 @@ func (f Feeds) Delete(db *bolt.DB, url string) (*Feed, error) {
}
// AddItems will update the feeds map of items and unread count
-func (f Feeds) AddItems(db *bolt.DB, url string, items Items) error {
+func (f Feeds) AddItems(db *bolt.DB, url string, items Items, updated *time.Time) error {
if url == "" {
return fmt.Errorf("url was empty, unable to delete")
}
- if len(items) == 0 {
+ if len(items) == 0 && updated == nil {
return nil
}
@@ 214,6 229,8 @@ func (f Feeds) AddItems(db *bolt.DB, url string, items Items) error {
return fmt.Errorf("feed does not exist")
}
+ feed.Updated = updated
+
for _, item := range items {
feed.Items[item.Link] = false
}
M go.mod => go.mod +1 -1
@@ 4,7 4,7 @@ go 1.15
require (
git.sr.ht/~adnano/go-gemini v0.1.10
- git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201229170024-75dc5f7d571e
+ git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201231052557-c9dc6d4deb60
git.sr.ht/~chrisppy/go-opml v0.0.0-20201229021831-d223d86f160e
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc
git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f
M go.sum => go.sum +2 -2
@@ 1,7 1,7 @@
git.sr.ht/~adnano/go-gemini v0.1.10 h1:enuYuY2pC+1BsP1GE73wLIyhXc4r+Ryx6TUubynmSgo=
git.sr.ht/~adnano/go-gemini v0.1.10/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0=
-git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201229170024-75dc5f7d571e h1:gWpCPRsUQ3cfRtgVQk29W/TrljnjzseSUFh52V0aY4M=
-git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201229170024-75dc5f7d571e/go.mod h1:V/4QgRdPISXxrbyn/0YNdrh1qR2PHk8hdcIJsdmJFlQ=
+git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201231052557-c9dc6d4deb60 h1:B9GcMx6b4PirZp+kywPHnKwggOGKopWgwd+J4FvxXQg=
+git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201231052557-c9dc6d4deb60/go.mod h1:V/4QgRdPISXxrbyn/0YNdrh1qR2PHk8hdcIJsdmJFlQ=
git.sr.ht/~chrisppy/go-opml v0.0.0-20201229021831-d223d86f160e h1:XOqA6g2gAr/ma6/W8xlhyt7CUIhkSsi+vGBMpXGiaxk=
git.sr.ht/~chrisppy/go-opml v0.0.0-20201229021831-d223d86f160e/go.mod h1:dhjKH7fks5UN50Cnma/JCtxx8IRw8xAicwKV9gAtsHk=
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc h1:51BD67xFX+bozd3ZRuOUfalrhx4/nQSh6A9lI08rYOk=
M ui/actions.go => ui/actions.go +28 -32
@@ 52,6 52,8 @@ func (i *UI) onCommandEnter(key tcell.Key) {
case ":unhide":
i.unhide()
case ":update", ":up":
+ i.commandLine.setText("updating feeds...")
+ i.app.draw()
go i.updateFeed(args)
default:
i.commandLine.setError("unrecognized command: " + input)
@@ 345,7 347,6 @@ func (i *UI) markUnread(event *tcell.EventKey) *tcell.EventKey {
}
nitems, err := i.DB.MarkUnread(key)
-
if err != nil {
i.commandLine.setError(err.Error())
return
@@ 459,10 460,9 @@ func (i *UI) unmarkFavorite(event *tcell.EventKey) *tcell.EventKey {
i.commandLine.setError(err.Error())
return
}
- i.setStatus(i.status)
})
-
i.app.draw()
+
return nil
}
@@ 496,10 496,9 @@ func (i *UI) openLink(event *tcell.EventKey) *tcell.EventKey {
i.app.draw()
}
}()
- i.setStatus(i.status)
})
-
i.app.draw()
+
return nil
}
@@ 525,12 524,12 @@ func (i *UI) play(event *tcell.EventKey) *tcell.EventKey {
return event
}
- i.app.update(func() {
- item, ok := i.DB.Items[key]
- if !ok {
- return
- }
+ item, ok := i.DB.Items[key]
+ if !ok {
+ return nil
+ }
+ i.app.update(func() {
if err := item.ExtPlay(ep.Bin, ep.Args, i.DownloadDataHome, i.DB.Feeds); err != nil {
i.commandLine.setError(err.Error())
i.app.draw()
@@ 539,6 538,7 @@ func (i *UI) play(event *tcell.EventKey) *tcell.EventKey {
i.setStatus(i.status)
})
i.app.draw()
+
return nil
}
@@ 562,12 562,12 @@ func (i *UI) download(event *tcell.EventKey) *tcell.EventKey {
i.commandLine.setText("downloading...")
i.app.draw()
- i.app.update(func() {
- item, ok := i.DB.Items[key]
- if !ok {
- return
- }
+ item, ok := i.DB.Items[key]
+ if !ok {
+ return nil
+ }
+ i.app.update(func() {
if err := item.Download(i.DownloadDataHome, i.DB.Feeds); err != nil {
i.commandLine.setError(err.Error())
return
@@ 581,29 581,25 @@ func (i *UI) download(event *tcell.EventKey) *tcell.EventKey {
}
func (i *UI) updateFeed(args []string) {
- i.commandLine.setText("updating feeds...")
- i.app.draw()
-
- i.app.update(func() {
- nitems, err := i.DB.Update()
- if err != nil {
- i.Logger.Errorln(err.Error())
- }
+ nitems, err := i.DB.Update()
+ if err != nil {
+ i.Logger.Errorln(err.Error())
+ return
+ }
- for _, item := range nitems {
+ for _, item := range nitems.Sort() {
+ i.app.update(func() {
i.list.insert(item)
i.sub.insert(item, i.hideRead)
- if i.Config.Podcast.AutoDownload {
- if err := item.Download(i.DownloadDataHome, i.DB.Feeds); err != nil {
- i.Logger.Errorln(err.Error())
- }
+ })
+ if i.Config.Podcast.AutoDownload {
+ if err := item.Download(i.DownloadDataHome, i.DB.Feeds); err != nil {
+ i.Logger.Errorln(err.Error())
}
}
+ }
- i.commandLine.setText("")
- i.setStatus(i.status)
- })
- i.app.draw()
+ i.setStatus(i.status)
}
func (i *UI) addFeed(args []string) {
M ui/content.go => ui/content.go +32 -14
@@ 18,8 18,8 @@
package ui
import (
- "fmt"
"strings"
+ "time"
"git.sr.ht/~chrisppy/beagles/db"
"git.sr.ht/~chrisppy/beagles/util"
@@ 50,20 50,31 @@ func (i *UI) newContent() *content {
}
}
-func (w *content) setDescription(text string, isHTML bool) {
+func (w *content) setDescription(text string, updated *time.Time, isHTML bool) {
// TODO: Handle gemini content to text
w.Widget.SetTextColor(w.TxtColor)
+
+ var sb strings.Builder
+
+ if updated != nil {
+ sb.WriteString("Feed Updated: ")
+ sb.WriteString(updated.Format("2006-01-02"))
+ sb.WriteByte('\n')
+ sb.WriteByte('\n')
+ }
+
if isHTML {
t, err := util.StripHTML(text)
if err != nil {
w.Widget.SetTextColor(w.ErrColor)
t = err.Error()
}
- w.Widget.SetText(t)
+ sb.WriteString(t)
} else {
- w.Widget.SetText(text)
+ sb.WriteString(text)
}
+ w.Widget.SetText(sb.String())
w.Widget.ScrollToBeginning()
}
@@ 76,34 87,41 @@ func (w *content) setText(item *db.Item) {
return
}
- content := ""
+ var sb strings.Builder
if item.Title != "" {
- content = fmt.Sprintf("%s\n\n", item.Title)
+ sb.WriteString(item.Title)
+ sb.WriteByte('\n')
+ sb.WriteByte('\n')
}
- content = fmt.Sprintf("%s%s\n\n", content, item.Date.Format("2006-01-02"))
+ sb.WriteString(item.Date.Format("2006-01-02"))
+ sb.WriteByte('\n')
+ sb.WriteByte('\n')
if item.Link != "" {
- content = fmt.Sprintf("%s%s\n\n", content, item.Link)
+ sb.WriteString(item.Link)
+ sb.WriteByte('\n')
+ sb.WriteByte('\n')
}
c := item.Content
- var text string
if c == "" {
- text = "No Content"
+ sb.WriteString("No Content")
+ sb.WriteByte('\n')
} else if strings.HasPrefix(item.Link, "http") {
t, err := util.StripHTML(c)
if err != nil {
w.Widget.SetTextColor(w.ErrColor)
t = err.Error()
}
- text = t
+ sb.WriteString(t)
+ sb.WriteByte('\n')
} else if strings.HasPrefix(item.Link, "gemini") {
- text = c
+ sb.WriteString(c)
+ sb.WriteByte('\n')
}
- content = fmt.Sprintf("%s%s\n", content, text)
- w.Widget.SetText(content)
+ w.Widget.SetText(sb.String())
w.Widget.ScrollToBeginning()
}
M ui/helpContent.go => ui/helpContent.go +1 -1
@@ 138,7 138,7 @@ SEE ALSO:
i.Config.Play.Text)
c := i.newContent()
- c.setDescription(content, false)
+ c.setDescription(content, nil, false)
return c
}
M ui/status.go => ui/status.go +9 -7
@@ 86,14 86,16 @@ func (i *UI) setStatus(s statusType) {
cltext = "/"
}
- if i.Mode == normal {
- i.panels.SetCurrentPanel(panel)
- i.status = s
- }
+ i.app.update(func() {
+ if i.Mode == normal {
+ i.panels.SetCurrentPanel(panel)
+ i.status = s
+ }
- i.commandLine.setText(cltext)
- i.statusLine.setText(sltext)
- i.app.switchTo(widget)
+ i.commandLine.setText(cltext)
+ i.statusLine.setText(sltext)
+ i.app.switchTo(widget)
+ })
i.app.draw()
}
M ui/ui.go => ui/ui.go +1 -1
@@ 104,7 104,7 @@ func (i *UI) Init() {
if it, ok := i.DB.Items[key]; ok {
i.subContent.setText(it)
} else if f, ok := i.DB.Feeds[key]; ok {
- i.subContent.setDescription(f.Description, true)
+ i.subContent.setDescription(f.Description, f.Updated, true)
} else {
i.subContent.clear()
}