diff --git a/config.example.ini b/config.example.ini index 74fbd7c..5833e2b 100644 --- a/config.example.ini +++ b/config.example.ini @@ -14,7 +14,6 @@ auto-load-newer=true # default=60 auto-load-seconds=60 - # The date format to be used # See https://godoc.org/time#Time.Format # default=2006-01-02 15:04 @@ -24,6 +23,22 @@ date-format=2006-01-02 15:04 # default=15:04 date-today-format=15:04 +# This displays relative dates instead +# for statuses that are one day or older +# the output is 1y2m1d (1 year 2 months and 1 day) +# +# The value is an integear +# -1 = don't use relative dates +# 0 = always use relative dates, except for dates < 1 day +# 1 - ∞ = number of days to use relative dates +# +# Example: date-relative=28 will display a relative date +# for toots that are between 1-28 days old. Otherwhise it +# will use the short or long format +# +# default=-1 +date-relative=-1 + # The timeline that opens up when you start tut # Valid values: home, direct, local, federated # default=home diff --git a/config.go b/config.go index fa05866..72cbc57 100644 --- a/config.go +++ b/config.go @@ -20,6 +20,7 @@ type GeneralConfig struct { AutoLoadSeconds int DateTodayFormat string DateFormat string + DateRelative int StartTimeline TimelineType NotificationFeed bool } @@ -168,6 +169,12 @@ func parseGeneral(cfg *ini.File) GeneralConfig { } general.DateTodayFormat = dateTodayFormat + dateRelative, err := cfg.Section("general").Key("date-relative").Int() + if err != nil { + dateRelative = -1 + } + general.DateRelative = dateRelative + tl := cfg.Section("general").Key("timeline").In("home", []string{"home", "direct", "local", "federated"}) switch tl { case "home": @@ -277,7 +284,6 @@ auto-load-newer=true # default=60 auto-load-seconds=60 - # The date format to be used # See https://godoc.org/time#Time.Format # default=2006-01-02 15:04 @@ -287,6 +293,22 @@ date-format=2006-01-02 15:04 # default=15:04 date-today-format=15:04 +# This displays relative dates instead +# for statuses that are one day or older +# the output is 1y2m1d (1 year 2 months and 1 day) +# +# The value is an integear +# -1 = don't use relative dates +# 0 = always use relative dates, except for dates < 1 day +# 1 - ∞ = number of days to use relative dates +# +# Example: date-relative=28 will display a relative date +# for toots that are between 1-28 days old. Otherwhise it +# will use the short or long format +# +# default=-1 +date-relative=-1 + # The timeline that opens up when you start tut # Valid values: home, direct, local, federated # default=home diff --git a/feed.go b/feed.go index 575a6df..6bbacbf 100644 --- a/feed.go +++ b/feed.go @@ -240,20 +240,15 @@ func showUser(app *App, user *mastodon.Account, relation *mastodon.Relationship, return text, controls } -func drawStatusList(statuses []*mastodon.Status, longFormat, shortFormat string) <-chan string { +func drawStatusList(statuses []*mastodon.Status, longFormat, shortFormat string, relativeDate int) <-chan string { ch := make(chan string) go func() { today := time.Now() - ty, tm, td := today.Date() for _, s := range statuses { - sLocal := s.CreatedAt.Local() - sy, sm, sd := sLocal.Date() - format := longFormat - if ty == sy && tm == sm && td == sd { - format = shortFormat - } - content := fmt.Sprintf("%s %s", sLocal.Format(format), s.Account.Acct) + dateOutput := OutputDate(sLocal, today, longFormat, shortFormat, relativeDate) + + content := fmt.Sprintf("%s %s", dateOutput, s.Account.Acct) ch <- content } close(ch) @@ -487,7 +482,7 @@ func (t *TimelineFeed) GetCurrentUser() *mastodon.Account { } func (t *TimelineFeed) GetFeedList() <-chan string { - return drawStatusList(t.statuses, t.app.Config.General.DateFormat, t.app.Config.General.DateTodayFormat) + return drawStatusList(t.statuses, t.app.Config.General.DateFormat, t.app.Config.General.DateTodayFormat, t.app.Config.General.DateRelative) } func (t *TimelineFeed) LoadNewer() int { @@ -637,7 +632,7 @@ func (t *ThreadFeed) GetCurrentUser() *mastodon.Account { } func (t *ThreadFeed) GetFeedList() <-chan string { - return drawStatusList(t.statuses, t.app.Config.General.DateFormat, t.app.Config.General.DateTodayFormat) + return drawStatusList(t.statuses, t.app.Config.General.DateFormat, t.app.Config.General.DateTodayFormat, t.app.Config.General.DateRelative) } func (t *ThreadFeed) LoadNewer() int { @@ -772,7 +767,7 @@ func (u *UserFeed) GetFeedList() <-chan string { ch := make(chan string) go func() { ch <- "Profile" - for s := range drawStatusList(u.statuses, u.app.Config.General.DateFormat, u.app.Config.General.DateTodayFormat) { + for s := range drawStatusList(u.statuses, u.app.Config.General.DateFormat, u.app.Config.General.DateTodayFormat, u.app.Config.General.DateRelative) { ch <- s } close(ch) @@ -972,15 +967,15 @@ func (n *NotificationsFeed) GetFeedList() <-chan string { notifications := n.notifications go func() { today := time.Now() - ty, tm, td := today.Date() for _, item := range notifications { sLocal := item.CreatedAt.Local() - sy, sm, sd := sLocal.Date() - format := n.app.Config.General.DateFormat - if ty == sy && tm == sm && td == sd { - format = n.app.Config.General.DateTodayFormat - } - content := fmt.Sprintf("%s %s", sLocal.Format(format), item.Account.Acct) + long := n.app.Config.General.DateFormat + short := n.app.Config.General.DateTodayFormat + relative := n.app.Config.General.DateRelative + + dateOutput := OutputDate(sLocal, today, long, short, relative) + + content := fmt.Sprintf("%s %s", dateOutput, item.Account.Acct) ch <- content } close(ch) @@ -1175,7 +1170,7 @@ func (t *TagFeed) GetCurrentUser() *mastodon.Account { } func (t *TagFeed) GetFeedList() <-chan string { - return drawStatusList(t.statuses, t.app.Config.General.DateFormat, t.app.Config.General.DateTodayFormat) + return drawStatusList(t.statuses, t.app.Config.General.DateFormat, t.app.Config.General.DateTodayFormat, t.app.Config.General.DateRelative) } func (t *TagFeed) LoadNewer() int { diff --git a/go.mod b/go.mod index 61c897c..b3b59c9 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( github.com/gdamore/tcell v1.3.0 + github.com/icza/gox v0.0.0-20200702115100-7dc3510ae515 // indirect github.com/kyoh86/xdg v1.2.0 github.com/mattn/go-mastodon v0.0.5-0.20200727014106-315df7d9162e github.com/microcosm-cc/bluemonday v1.0.3 diff --git a/go.sum b/go.sum index 0c843be..a95a484 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/icza/gox v0.0.0-20200702115100-7dc3510ae515 h1:0M95ImIfyqsVml89tVxg4DXQ+qv0WPHphNK/nAukgbM= +github.com/icza/gox v0.0.0-20200702115100-7dc3510ae515/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI= diff --git a/util.go b/util.go index 5ac9d38..fc17280 100644 --- a/util.go +++ b/util.go @@ -14,6 +14,7 @@ import ( "time" "github.com/gdamore/tcell" + "github.com/icza/gox/timex" "github.com/mattn/go-mastodon" "github.com/microcosm-cc/bluemonday" "github.com/rivo/tview" @@ -271,3 +272,47 @@ func SublteText(style StyleConfig, text string) string { subtle := ColorMark(style.Subtle) return fmt.Sprintf("%s%s", subtle, text) } + +func FloorDate(t time.Time) time.Time { + return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) +} + +func OutputDate(status time.Time, today time.Time, long, short string, relativeDate int) string { + ty, tm, td := today.Date() + sy, sm, sd := status.Date() + + format := long + sameDay := false + displayRelative := false + + if ty == sy && tm == sm && td == sd { + format = short + sameDay = true + } + + todayFloor := FloorDate(today) + statusFloor := FloorDate(status) + + if relativeDate > -1 && !sameDay { + days := int(todayFloor.Sub(statusFloor).Hours() / 24) + if relativeDate == 0 || days <= relativeDate { + displayRelative = true + } + } + var dateOutput string + if displayRelative { + y, m, d, _, _, _ := timex.Diff(statusFloor, todayFloor) + if y > 0 { + dateOutput = fmt.Sprintf("%s%dy", dateOutput, y) + } + if dateOutput != "" || m > 0 { + dateOutput = fmt.Sprintf("%s%dm", dateOutput, m) + } + if dateOutput != "" || d > 0 { + dateOutput = fmt.Sprintf("%s%dd", dateOutput, d) + } + } else { + dateOutput = status.Format(format) + } + return dateOutput +}