2020-08-17 15:33:53 -04:00
|
|
|
package display
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-11-17 20:56:15 -05:00
|
|
|
"net/url"
|
|
|
|
"path"
|
|
|
|
"strconv"
|
2020-08-17 15:33:53 -04:00
|
|
|
"strings"
|
2020-08-17 17:36:50 -04:00
|
|
|
"time"
|
2020-08-17 15:33:53 -04:00
|
|
|
|
2020-11-17 20:56:15 -05:00
|
|
|
"github.com/gdamore/tcell"
|
2020-08-28 19:33:37 -04:00
|
|
|
"github.com/makeworld-the-better-one/amfora/cache"
|
2020-11-17 20:56:15 -05:00
|
|
|
"github.com/makeworld-the-better-one/amfora/config"
|
2020-08-17 15:33:53 -04:00
|
|
|
"github.com/makeworld-the-better-one/amfora/feeds"
|
2020-11-17 20:56:15 -05:00
|
|
|
"github.com/makeworld-the-better-one/amfora/logger"
|
2020-08-17 15:33:53 -04:00
|
|
|
"github.com/makeworld-the-better-one/amfora/renderer"
|
|
|
|
"github.com/makeworld-the-better-one/amfora/structs"
|
2020-11-17 20:56:15 -05:00
|
|
|
"github.com/mmcdole/gofeed"
|
|
|
|
"github.com/spf13/viper"
|
2020-08-17 15:33:53 -04:00
|
|
|
)
|
|
|
|
|
2020-08-28 19:33:37 -04:00
|
|
|
var feedPageUpdated time.Time
|
|
|
|
|
2020-08-29 20:51:51 -04:00
|
|
|
// toLocalDay truncates the provided time to a date only,
|
|
|
|
// but converts to the local time first.
|
|
|
|
func toLocalDay(t time.Time) time.Time {
|
|
|
|
t = t.Local()
|
|
|
|
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
|
|
|
}
|
|
|
|
|
2020-08-17 15:33:53 -04:00
|
|
|
// Feeds displays the feeds page on the current tab.
|
|
|
|
func Feeds(t *tab) {
|
2020-11-17 20:56:15 -05:00
|
|
|
logger.Log.Println("display.Feeds called")
|
|
|
|
|
2020-11-17 11:59:06 -05:00
|
|
|
// Retrieve cached version if there hasn't been any updates
|
2020-08-28 19:33:37 -04:00
|
|
|
p, ok := cache.GetPage("about:feeds")
|
2020-08-29 20:51:51 -04:00
|
|
|
if feedPageUpdated.After(feeds.LastUpdated) && ok {
|
2020-11-18 16:10:22 -05:00
|
|
|
logger.Log.Println("using cached feeds page")
|
2020-08-28 19:33:37 -04:00
|
|
|
setPage(t, p)
|
|
|
|
t.applyBottomBar()
|
|
|
|
return
|
|
|
|
}
|
2020-08-17 15:33:53 -04:00
|
|
|
|
2020-11-18 16:10:22 -05:00
|
|
|
logger.Log.Println("started rendering feeds page")
|
|
|
|
|
2020-11-18 21:24:26 -05:00
|
|
|
feedPageRaw := "# Feeds & Pages\n\n" +
|
|
|
|
"See the help (by pressing ?) for details on how to use this page.\n\n" +
|
|
|
|
"If you just opened Amfora then updates will appear incrementally. Reload the page to see them.\n"
|
2020-11-18 16:10:22 -05:00
|
|
|
|
2020-08-28 19:33:37 -04:00
|
|
|
// curDay represents what day of posts the loop is on.
|
|
|
|
// It only goes backwards in time.
|
2020-11-17 11:59:06 -05:00
|
|
|
// Its initial setting means:
|
|
|
|
// Only display posts older than 26 hours in the future, nothing further in the future.
|
|
|
|
//
|
|
|
|
// 26 hours was chosen because it is the largest timezone difference
|
|
|
|
// currently in the world. Posts may be dated in the future
|
|
|
|
// due to software bugs, where the local user's date is used, but
|
|
|
|
// the UTC timezone is specified. I believe gemfeed does this.
|
|
|
|
curDay := toLocalDay(time.Now()).Add(26 * time.Hour)
|
2020-08-29 20:51:51 -04:00
|
|
|
|
|
|
|
pe := feeds.GetPageEntries()
|
2020-08-28 19:33:37 -04:00
|
|
|
|
|
|
|
for _, entry := range pe.Entries { // From new to old
|
|
|
|
// Convert to local time, remove sub-day info
|
2020-08-29 20:51:51 -04:00
|
|
|
pub := toLocalDay(entry.Published)
|
2020-08-17 15:33:53 -04:00
|
|
|
|
2020-08-28 19:33:37 -04:00
|
|
|
if pub.Before(curDay) {
|
2020-08-17 15:33:53 -04:00
|
|
|
// This post is on a new day, add a day header
|
2020-11-18 16:10:22 -05:00
|
|
|
curDay = pub
|
2020-08-17 15:33:53 -04:00
|
|
|
feedPageRaw += fmt.Sprintf("\n## %s\n\n", curDay.Format("Jan 02, 2006"))
|
|
|
|
}
|
2020-11-19 11:34:10 -05:00
|
|
|
if entry.Title == "" || entry.Title == "/" {
|
2020-11-19 12:50:49 -05:00
|
|
|
// Just put author/title
|
2020-11-19 11:34:10 -05:00
|
|
|
// Mainly used for when you're tracking the root domain of a site
|
2020-11-19 12:50:49 -05:00
|
|
|
feedPageRaw += fmt.Sprintf("=>%s %s\n", entry.URL, entry.Prefix)
|
2020-11-19 11:34:10 -05:00
|
|
|
} else {
|
|
|
|
// Include title and dash
|
2020-11-19 12:50:49 -05:00
|
|
|
feedPageRaw += fmt.Sprintf("=>%s %s - %s\n", entry.URL, entry.Prefix, entry.Title)
|
2020-11-19 11:34:10 -05:00
|
|
|
}
|
2020-08-17 15:33:53 -04:00
|
|
|
}
|
|
|
|
|
2020-09-01 22:26:34 -04:00
|
|
|
content, links := renderer.RenderGemini(feedPageRaw, textWidth(), leftMargin(), false)
|
2020-08-17 15:33:53 -04:00
|
|
|
page := structs.Page{
|
|
|
|
Raw: feedPageRaw,
|
|
|
|
Content: content,
|
|
|
|
Links: links,
|
2020-08-28 12:07:08 -04:00
|
|
|
URL: "about:feeds",
|
2020-08-17 15:33:53 -04:00
|
|
|
Width: termW,
|
|
|
|
Mediatype: structs.TextGemini,
|
|
|
|
}
|
2020-08-29 20:51:51 -04:00
|
|
|
go cache.AddPage(&page)
|
2020-08-17 15:33:53 -04:00
|
|
|
setPage(t, &page)
|
|
|
|
t.applyBottomBar()
|
2020-08-28 19:33:37 -04:00
|
|
|
|
|
|
|
feedPageUpdated = time.Now()
|
2020-11-18 16:10:22 -05:00
|
|
|
|
|
|
|
logger.Log.Println("done rendering feeds page")
|
2020-08-28 19:33:37 -04:00
|
|
|
}
|
|
|
|
|
2020-11-17 20:56:15 -05:00
|
|
|
// openFeedModal displays the "Add feed/page" modal
|
|
|
|
// It returns whether the user wanted to add the feed/page.
|
|
|
|
// The tracked arg specifies whether this feed/page is already
|
|
|
|
// being tracked.
|
|
|
|
func openFeedModal(validFeed, tracked bool) bool {
|
|
|
|
logger.Log.Println("display.openFeedModal called")
|
|
|
|
// Reuses yesNoModal
|
|
|
|
|
|
|
|
if viper.GetBool("a-general.color") {
|
|
|
|
yesNoModal.
|
|
|
|
SetBackgroundColor(config.GetColor("feed_modal_bg")).
|
|
|
|
SetTextColor(config.GetColor("feed_modal_text"))
|
|
|
|
yesNoModal.GetFrame().
|
|
|
|
SetBorderColor(config.GetColor("feed_modal_text")).
|
|
|
|
SetTitleColor(config.GetColor("feed_modal_text"))
|
|
|
|
} else {
|
|
|
|
yesNoModal.
|
|
|
|
SetBackgroundColor(tcell.ColorBlack).
|
|
|
|
SetTextColor(tcell.ColorWhite)
|
|
|
|
yesNoModal.GetFrame().
|
|
|
|
SetBorderColor(tcell.ColorWhite).
|
|
|
|
SetTitleColor(tcell.ColorWhite)
|
|
|
|
}
|
|
|
|
if validFeed {
|
|
|
|
yesNoModal.GetFrame().SetTitle("Feed Tracking")
|
|
|
|
if tracked {
|
|
|
|
yesNoModal.SetText("This is already being tracked. Would you like to manually update it?")
|
|
|
|
} else {
|
|
|
|
yesNoModal.SetText("Would you like to start tracking this feed?")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
yesNoModal.GetFrame().SetTitle("Page Tracking")
|
|
|
|
if tracked {
|
|
|
|
yesNoModal.SetText("This is already being tracked. Would you like to manually update it?")
|
|
|
|
} else {
|
|
|
|
yesNoModal.SetText("Would you like to start tracking this page?")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tabPages.ShowPage("yesno")
|
|
|
|
tabPages.SendToFront("yesno")
|
|
|
|
App.SetFocus(yesNoModal)
|
|
|
|
App.Draw()
|
|
|
|
|
|
|
|
resp := <-yesNoCh
|
|
|
|
tabPages.SwitchToPage(strconv.Itoa(curTab))
|
|
|
|
App.SetFocus(tabs[curTab].view)
|
|
|
|
App.Draw()
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
// getFeedFromPage is like feeds.GetFeed but takes a structs.Page as input.
|
|
|
|
func getFeedFromPage(p *structs.Page) (*gofeed.Feed, bool) {
|
|
|
|
parsed, _ := url.Parse(p.URL)
|
|
|
|
filename := path.Base(parsed.Path)
|
|
|
|
r := strings.NewReader(p.Raw)
|
|
|
|
return feeds.GetFeed(p.RawMediatype, filename, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
// addFeedDirect is only for adding feeds, not pages.
|
|
|
|
// It's for when you already have a feed and know if it's tracked.
|
|
|
|
// Use mainly by handleURL because it already did a lot of the work.
|
|
|
|
//
|
|
|
|
// Like addFeed, it should be called in a goroutine.
|
|
|
|
func addFeedDirect(u string, feed *gofeed.Feed, tracked bool) {
|
|
|
|
logger.Log.Println("display.addFeedDirect called")
|
|
|
|
|
|
|
|
if openFeedModal(true, tracked) {
|
|
|
|
err := feeds.AddFeed(u, feed)
|
|
|
|
if err != nil {
|
|
|
|
Error("Feed Error", err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-18 16:10:22 -05:00
|
|
|
// addFeed goes through the process of tracking the current page/feed.
|
2020-11-17 20:56:15 -05:00
|
|
|
// It is the high-level way of doing it. It should be called in a goroutine.
|
|
|
|
func addFeed() {
|
|
|
|
logger.Log.Println("display.addFeed called")
|
|
|
|
|
|
|
|
t := tabs[curTab]
|
|
|
|
p := t.page
|
|
|
|
|
|
|
|
if !t.hasContent() {
|
|
|
|
// It's an about: page, or a malformed one
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
feed, isFeed := getFeedFromPage(p)
|
|
|
|
tracked := feeds.IsTracked(p.URL)
|
|
|
|
|
|
|
|
if openFeedModal(isFeed, tracked) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if isFeed {
|
|
|
|
err = feeds.AddFeed(p.URL, feed)
|
|
|
|
} else {
|
|
|
|
err = feeds.AddPage(p.URL, strings.NewReader(p.Raw))
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
Error("Feed/Page Error", err.Error())
|
|
|
|
}
|
|
|
|
}
|
2020-08-17 15:33:53 -04:00
|
|
|
}
|