From 1cd5d5a3565d68c0fe352d830bc28e2937d40b81 Mon Sep 17 00:00:00 2001 From: Rasmus Lindroth Date: Tue, 20 Dec 2022 19:18:45 +0100 Subject: [PATCH] 1.0.29 (#232) * bump version * add mentions timeline * add switch and close * make name optional for timelines * add move window --- README.md | 7 +- api/item.go | 20 +-- config.example.ini | 25 +++- config/config.go | 59 +++++++-- config/default_config.go | 25 +++- config/help.tmpl | 13 +- feed/feed.go | 43 ++++-- main.go | 2 +- ui/cmdbar.go | 37 +++++- ui/commands.go | 121 +++++++++++++++++ ui/feed.go | 276 ++++++++++++++++++++++++--------------- ui/input.go | 14 ++ ui/item_status.go | 2 + ui/timeline.go | 141 ++++++++++++++++---- 14 files changed, 602 insertions(+), 183 deletions(-) diff --git a/README.md b/README.md index da33b8a..a69030a 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,13 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t ## Currently supported commands * `:q` `:quit` exit -* `:timeline` home, local, federated, direct, notifications, favorited, special-all, special-boosts, special-replies - * `:tl` h, l, f, d, n, fav, sa, sb, sr (shorter form) +* `:timeline` home, local, federated, direct, notifications, mentions favorited, special-all, special-boosts, special-replies + * `:tl` h, l, f, d, n, m, fav, sa, sb, sr (shorter form) * `:blocking` lists users that you have blocked * `:boosts` lists users that boosted the toot * `:bookmarks` lists all your bookmarks * `:clear-notifications` clear all notifications +* `:close-window` closes the current window * `:compose` compose a new toot * `:edit` edit one of your toots * `:favorited` lists toots you've favorited @@ -58,6 +59,8 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:lists` show a list of your lists * `:list-placement` top, right, bottom, left * `:list-split` row, column +* `:move-window` left, right, up, down, home, end + * `:mv` l, r, u, d, h, e * `:muting` lists users that you have muted * `:newer` force load newer toots in current timeline * `:preferences` update your profile and some other settings diff --git a/api/item.go b/api/item.go index 3abf57d..7e31720 100644 --- a/api/item.go +++ b/api/item.go @@ -149,8 +149,8 @@ func (s *StatusItem) Filtered(tl config.FeedType) (bool, string, string, bool) { used := false for _, w := range f.Where { switch w { - case "home", "special": - if tl == config.TimelineHome || tl == config.List { + case "home": + if tl == config.TimelineHome || tl == config.List || tl == config.TimelineHomeSpecial { used = true } case "thread": @@ -158,28 +158,20 @@ func (s *StatusItem) Filtered(tl config.FeedType) (bool, string, string, bool) { used = true } case "notifications": - if tl == config.Notifications { + if tl == config.Notifications || tl == config.Mentions { used = true } - case "account": if tl == config.User { used = true } case "public": where := []config.FeedType{ - config.Favorites, - config.Favorited, - config.Boosts, + config.TimelineFederated, + config.TimelineLocal, config.Tag, - config.Notifications, - config.TimelineHome, - config.TimelineHomeSpecial, - config.Conversations, - config.User, - config.List, } - if !slices.Contains(where, tl) { + if slices.Contains(where, tl) { used = true } } diff --git a/config.example.ini b/config.example.ini index 2e0f207..cdec252 100644 --- a/config.example.ini +++ b/config.example.ini @@ -14,7 +14,7 @@ mouse-support=false # they should show and the key to activate them. # # Available timelines: home, direct, local, federated, special, bookmarks, -# saved, favorited, notifications, lists, tag +# saved, favorited, notifications, lists, mentions, tag # # The one named special are the home timeline with only boosts and/or replies. # @@ -171,11 +171,13 @@ leader-timeout=1000 # of two parts first the action then the shortcut. And they're separated by a # comma. # -# Available commands: home, direct, local, federated, special-all, -# special-boosts, special-replies, clear-notifications, compose, edit, history, -# blocking, bookmarks, refetch, saved, favorited, boosts, favorites, following, -# followers, muting, newer, preferences, profile, notifications, lists, -# stick-to-top, tag, tags, window, list-placement, list-split, proportions +# Available commands: blocking, bookmarks, boosts, clear-notifications, +# close-window, compose, direct, edit, favorited, favorites, federated, +# followers, following, history, home, list-placement, list-split, lists, local, +# mentions, move-window-left, move-window-right, move-window-up, +# move-window-down, move-window-home, move-window-end, muting, newer, +# notifications, preferences, profile, proportions, refetch, saved, special-all, +# special-boosts, special-replies, stick-to-top, switch, tag, tags, window # # The ones named special-* are the home timeline with only boosts and/or # replies. All contains both, -boosts only boosts and -replies only replies. @@ -194,6 +196,9 @@ leader-timeout=1000 # proportions takes the arguments [int] [int], where the first integer is the # list and the other content, e.g. proportions 1 3. See list-proportion above # for more information. +# switch let's you go to a timeline if it already exists, if it doesn't it will +# open the timeline in a new window. The syntax is almost the same as in +# timelines= and is displayed under the examples. # # Some examples: # leader-action=local,lo @@ -207,6 +212,14 @@ leader-timeout=1000 # leader-action=list-split column,c # leader-action=proportions 1 3,3 # +# Syntax for switch: +# leader-action=switch feed,shortcut,[name],[showBoosts],[showReplies] +# showBoosts can be either true or false and they are both optional. Here are +# some examples: +# +# leader-action=switch home,false,true,h +# leader-action=switch tag tut,tt +# [media] diff --git a/config/config.go b/config/config.go index 241e268..b4d573a 100644 --- a/config/config.go +++ b/config/config.go @@ -76,6 +76,7 @@ const ( LeaderProfile LeaderProportions LeaderNotifications + LeaderMentions LeaderLists LeaderRefetch LeaderTag @@ -83,8 +84,14 @@ const ( LeaderStickToTop LeaderHistory LeaderUser - LeaderWindow LeaderLoadNewer + LeaderWindow + LeaderCloseWindow + LeaderSwitch + LeaderMoveWindowLeft + LeaderMoveWindowRight + LeaderMoveWindowHome + LeaderMoveWindowEnd ) type FeedType uint @@ -109,6 +116,7 @@ const ( TimelineHome TimelineHomeSpecial TimelineLocal + Mentions Conversations User UserList @@ -897,8 +905,8 @@ func parseGeneral(cfg *ini.File) General { var las []LeaderAction for _, l := range lactions { parts := strings.Split(l, ",") - if len(parts) != 2 { - fmt.Printf("leader-action must consist of two parts separated by a comma. Your value is: %s\n", strings.Join(parts, ",")) + if len(parts) < 2 { + fmt.Printf("leader-action must consist of atleast two parts separated by a comma. Your value is: %s\n", strings.Join(parts, ",")) os.Exit(1) } for i, p := range parts { @@ -959,6 +967,8 @@ func parseGeneral(cfg *ini.File) General { la.Command = LeaderProfile case "notifications": la.Command = LeaderNotifications + case "mentions": + la.Command = LeaderMentions case "lists": la.Command = LeaderLists case "stick-to-top": @@ -982,6 +992,27 @@ func parseGeneral(cfg *ini.File) General { case "window": la.Command = LeaderWindow la.Subaction = subaction + case "close-window": + la.Command = LeaderCloseWindow + case "move-window-left", "move-window-up": + la.Command = LeaderMoveWindowLeft + case "move-window-right", "move-window-down": + la.Command = LeaderMoveWindowRight + case "move-window-home": + la.Command = LeaderMoveWindowHome + case "move-window-end": + la.Command = LeaderMoveWindowEnd + case "switch": + la.Command = LeaderSwitch + sa := "" + if len(parts) > 2 { + sa = strings.Join(parts[2:], ",") + } + if len(sa) > 0 { + la.Subaction = fmt.Sprintf("%s,%s", subaction, sa) + } else { + la.Subaction = subaction + } case "newer": la.Command = LeaderLoadNewer default: @@ -1036,6 +1067,8 @@ func parseGeneral(cfg *ini.File) General { tl.FeedType = Favorited case "notifications": tl.FeedType = Notifications + case "mentions": + tl.FeedType = Mentions case "lists": tl.FeedType = Lists case "tag": @@ -1045,25 +1078,33 @@ func parseGeneral(cfg *ini.File) General { fmt.Printf("timeline %s is invalid\n", parts[0]) os.Exit(1) } + tfStr := []string{"true", "false"} tl.Name = parts[1] + if slices.Contains(tfStr, tl.Name) || (strings.HasPrefix(parts[1], "\"") && strings.HasSuffix(parts[1], "\"")) || + (strings.HasPrefix(parts[1], "'") && strings.HasSuffix(parts[1], "'")) { + tl.Name = "" + } tfs := []bool{true, true} - tfStr := []string{"true", "false"} stop := len(parts) - if len(parts) > 2 { - if slices.Contains(tfStr, parts[len(parts)-2]) && + if len(parts) > 1 { + if len(parts) > 2 && slices.Contains(tfStr, parts[len(parts)-2]) && slices.Contains(tfStr, parts[len(parts)-1]) && - len(parts)-2 > 1 { + len(parts)-2 > 0 { tfs[0] = parts[len(parts)-2] == "true" tfs[1] = parts[len(parts)-1] == "true" stop = len(parts) - 2 } else if slices.Contains(tfStr, parts[len(parts)-1]) && - len(parts)-1 > 1 { + len(parts)-1 > 0 { tfs[0] = parts[len(parts)-1] == "true" stop = len(parts) - 1 } if stop > 2 { vals := []string{""} - vals = append(vals, parts[2:stop]...) + start := 2 + if tl.Name == "" { + start = 1 + } + vals = append(vals, parts[start:stop]...) tl.Key = inputStrOrErr(vals, false) } } diff --git a/config/default_config.go b/config/default_config.go index ce09bfb..54b8f4b 100644 --- a/config/default_config.go +++ b/config/default_config.go @@ -16,7 +16,7 @@ mouse-support=false # they should show and the key to activate them. # # Available timelines: home, direct, local, federated, special, bookmarks, -# saved, favorited, notifications, lists, tag +# saved, favorited, notifications, lists, mentions, tag # # The one named special are the home timeline with only boosts and/or replies. # @@ -173,11 +173,13 @@ leader-timeout=1000 # of two parts first the action then the shortcut. And they're separated by a # comma. # -# Available commands: home, direct, local, federated, special-all, -# special-boosts, special-replies, clear-notifications, compose, edit, history, -# blocking, bookmarks, refetch, saved, favorited, boosts, favorites, following, -# followers, muting, newer, preferences, profile, notifications, lists, -# stick-to-top, tag, tags, window, list-placement, list-split, proportions +# Available commands: blocking, bookmarks, boosts, clear-notifications, +# close-window, compose, direct, edit, favorited, favorites, federated, +# followers, following, history, home, list-placement, list-split, lists, local, +# mentions, move-window-left, move-window-right, move-window-up, +# move-window-down, move-window-home, move-window-end, muting, newer, +# notifications, preferences, profile, proportions, refetch, saved, special-all, +# special-boosts, special-replies, stick-to-top, switch, tag, tags, window # # The ones named special-* are the home timeline with only boosts and/or # replies. All contains both, -boosts only boosts and -replies only replies. @@ -196,6 +198,9 @@ leader-timeout=1000 # proportions takes the arguments [int] [int], where the first integer is the # list and the other content, e.g. proportions 1 3. See list-proportion above # for more information. +# switch let's you go to a timeline if it already exists, if it doesn't it will +# open the timeline in a new window. The syntax is almost the same as in +# timelines= and is displayed under the examples. # # Some examples: # leader-action=local,lo @@ -209,6 +214,14 @@ leader-timeout=1000 # leader-action=list-split column,c # leader-action=proportions 1 3,3 # +# Syntax for switch: +# leader-action=switch feed,shortcut,[name],[showBoosts],[showReplies] +# showBoosts can be either true or false and they are both optional. Here are +# some examples: +# +# leader-action=switch home,false,true,h +# leader-action=switch tag tut,tt +# [media] diff --git a/config/help.tmpl b/config/help.tmpl index 5123c01..db6e2e1 100644 --- a/config/help.tmpl +++ b/config/help.tmpl @@ -30,10 +30,10 @@ Here's a list of supported commands. {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} :quit{{ Flags "-" }}{{ Color .Style.Text }} Exit the program -{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:timeline{{ Flags "-" }}{{ Color .Style.Text }} home|local|federated|direct|notifications|favorited|special-all|special-boosts|special-replies +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:timeline{{ Flags "-" }}{{ Color .Style.Text }} home|local|federated|direct|notifications|mentions|favorited|special-all|special-boosts|special-replies Open selected timeline -{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:tl{{ Flags "-" }}{{ Color .Style.Text }} h|l|f|d|n|fav|sa|sb|sr +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:tl{{ Flags "-" }}{{ Color .Style.Text }} h|l|f|d|n|m|fav|sa|sb|sr Shorter form of the former command *:timeline* {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:blocking{{ Flags "-" }}{{ Color .Style.Text }} @@ -48,6 +48,9 @@ Here's a list of supported commands. {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:clear-notifications{{ Flags "-" }}{{ Color .Style.Text }} Clear all notifications +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:close-window{{ Flags "-" }}{{ Color .Style.Text }} + Closes the current window + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:compose{{ Flags "-" }}{{ Color .Style.Text }} Compose a new toot @@ -85,6 +88,12 @@ Here's a list of supported commands. {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:list-split{{ Flags "-" }}{{ Color .Style.Text }} row|column Split lists as rows or columns +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:move-window{{ Flags "-" }}{{ Color .Style.Text }} left|right|up|down|home|end + Moves window in selected direction + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:mv{{ Flags "-" }}{{ Color .Style.Text }} l|r|u|d|h|e + Shorter form of the former command *:move-window* + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:muting{{ Flags "-" }}{{ Color .Style.Text }} Lists users that you have muted diff --git a/feed/feed.go b/feed/feed.go index cc865ab..a3a55f7 100644 --- a/feed/feed.go +++ b/feed/feed.go @@ -2,6 +2,7 @@ package feed import ( "context" + "errors" "log" "strings" "sync" @@ -134,6 +135,9 @@ func (f *Feed) Item(index int) (api.Item, error) { } */ filtered := f.filteredList() + if len(filtered) == 0 { + return nil, errors.New("item out of range") + } return filtered[index], nil } @@ -718,7 +722,7 @@ func (f *Feed) startStream(rec *api.Receiver, timeline string, err error) { }() } -func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err error) { +func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err error, mentions bool) { if err != nil { log.Fatalln("Couldn't open stream") } @@ -729,35 +733,35 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e case *mastodon.NotificationEvent: switch t.Notification.Type { case "follow": - if slices.Contains(f.config.General.NotificationsToHide, config.HideFollow) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideFollow) || mentions { continue } case "follow_request": - if slices.Contains(f.config.General.NotificationsToHide, config.HideFollowRequest) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideFollowRequest) || mentions { continue } case "favourite": - if slices.Contains(f.config.General.NotificationsToHide, config.HideFavorite) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideFavorite) || mentions { continue } case "reblog": - if slices.Contains(f.config.General.NotificationsToHide, config.HideBoost) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideBoost) || mentions { continue } case "mention": - if slices.Contains(f.config.General.NotificationsToHide, config.HideMention) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideMention) && !mentions { continue } case "update": - if slices.Contains(f.config.General.NotificationsToHide, config.HideEdited) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideEdited) || mentions { continue } case "status": - if slices.Contains(f.config.General.NotificationsToHide, config.HideStatus) { + if slices.Contains(f.config.General.NotificationsToHide, config.HideStatus) || mentions { continue } case "poll": - if slices.Contains(f.config.General.NotificationsToHide, config.HidePoll) { + if slices.Contains(f.config.General.NotificationsToHide, config.HidePoll) || mentions { continue } } @@ -901,13 +905,32 @@ func NewNotifications(ac *api.AccountClient, cnf *config.Config, showBoosts bool feed.loadOlder = func() { feed.normalOlderNotification(feed.accountClient.GetNotifications, cnf.General.NotificationsToHide) } - feed.startStreamNotification(feed.accountClient.NewHomeStream()) + rec, tl, err := feed.accountClient.NewHomeStream() + feed.startStreamNotification(rec, tl, err, false) feed.close = func() { for _, s := range feed.streams { feed.accountClient.RemoveHomeReceiver(s) } } + return feed +} +func NewNotificationsMentions(ac *api.AccountClient, cnf *config.Config) *Feed { + feed := newFeed(ac, config.Notifications, cnf, true, true) + hide := []config.NotificationToHide{config.HideStatus, config.HideBoost, config.HideFollow, config.HideFollowRequest, config.HideFavorite, config.HidePoll, config.HideEdited} + feed.loadNewer = func() { + feed.normalNewerNotification(feed.accountClient.GetNotifications, hide) + } + feed.loadOlder = func() { + feed.normalOlderNotification(feed.accountClient.GetNotifications, hide) + } + rec, tl, err := feed.accountClient.NewHomeStream() + feed.startStreamNotification(rec, tl, err, true) + feed.close = func() { + for _, s := range feed.streams { + feed.accountClient.RemoveHomeReceiver(s) + } + } return feed } diff --git a/main.go b/main.go index 0518b4c..47afbf5 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "github.com/rivo/tview" ) -const version = "1.0.28" +const version = "1.0.29" func main() { util.SetTerminalTitle("tut") diff --git a/ui/cmdbar.go b/ui/cmdbar.go index 3116d0e..19b17ea 100644 --- a/ui/cmdbar.go +++ b/ui/cmdbar.go @@ -102,6 +102,27 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { case ":clear-notifications": c.tutView.ClearNotificationsCommand() c.Back() + case ":close-window": + c.tutView.CloseWindowCommand() + c.Back() + case ":move-window", ":mv": + if len(parts) < 2 { + break + } + switch parts[1] { + case "left", "up", "l", "u": + c.tutView.MoveWindowLeft() + c.Back() + case "right", "down", "r", "d": + c.tutView.MoveWindowRight() + c.Back() + case "home", "h": + c.tutView.MoveWindowHome() + c.Back() + case "end", "e": + c.tutView.MoveWindowEnd() + c.Back() + } case ":list-placement": if len(parts) < 2 { break @@ -180,6 +201,9 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { case "notifications", "n": c.tutView.NotificationsCommand() c.Back() + case "mentions", "m": + c.tutView.MentionsCommand() + c.Back() case "favorited", "fav": c.tutView.FavoritedCommand() c.Back() @@ -254,16 +278,16 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { func (c *CmdBar) Autocomplete(curr string) []string { var entries []string - words := strings.Split(":blocking,:boosts,:bookmarks,:clear-notifications,:compose,:favorites,:favorited,:follow-tag,:followers,:following,:help,:h,:history,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:refetch,:requests,:saved,:stick-to-top,:tag,:timeline,:tl,:unfollow-tag,:user,:window,:quit,:q", ",") + words := strings.Split(":blocking,:boosts,:bookmarks,:clear-notifications,:compose,:favorites,:favorited,:follow-tag,:followers,:following,:help,:h,:history,:move-window,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:refetch,:requests,:saved,:stick-to-top,:tag,:timeline,:tl,:unfollow-tag,:user,:window,:quit,:q", ",") if curr == "" { return entries } if len(curr) > 2 && curr[:3] == ":tl" { - words = strings.Split(":tl home,:tl notifications,:tl local,:tl federated,:tl direct,:tl favorited,:tl special-all,:tl special-boosts,:tl-special-replies", ",") + words = strings.Split(":tl home,:tl notifications,:tl local,:tl federated,:tl direct,:tl mentions,:tl favorited,:tl special-all,:tl special-boosts,:tl-special-replies", ",") } if len(curr) > 8 && curr[:9] == ":timeline" { - words = strings.Split(":timeline home,:timeline notifications,:timeline local,:timeline federated,:timeline direct,:timeline favorited,:timeline special-all,:timeline special-boosts,:timeline special-replies", ",") + words = strings.Split(":timeline home,:timeline notifications,:timeline local,:timeline federated,:timeline direct,:timeline mentions,:timeline favorited,:timeline special-all,:timeline special-boosts,:timeline special-replies", ",") } if len(curr) > 14 && curr[:15] == ":list-placement" { words = strings.Split(":list-placement top,:list-placement right,:list-placement bottom,:list-placement left", ",") @@ -272,6 +296,13 @@ func (c *CmdBar) Autocomplete(curr string) []string { words = strings.Split(":list-split row,:list-split column", ",") } + if len(curr) > 11 && curr[:12] == ":move-window" { + words = strings.Split(":move-window left,:move-window right,:move-window up,:move-window down,:move-window home,:move-window end", ",") + } + if len(curr) > 2 && curr[:3] == ":mv" { + words = strings.Split(":mv left,:mv right,:mv up,:mv down,:mv home,:mv end", ",") + } + for _, word := range words { if strings.HasPrefix(strings.ToLower(word), strings.ToLower(curr)) { entries = append(entries, word) diff --git a/ui/commands.go b/ui/commands.go index f4fb91a..0113481 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -2,12 +2,15 @@ package ui import ( "fmt" + "os" "strconv" + "strings" "github.com/RasmusLindroth/go-mastodon" "github.com/RasmusLindroth/tut/api" "github.com/RasmusLindroth/tut/config" "github.com/RasmusLindroth/tut/util" + "golang.org/x/exp/slices" ) func (tv *TutView) ComposeCommand() { @@ -95,6 +98,12 @@ func (tv *TutView) NotificationsCommand() { ) } +func (tv *TutView) MentionsCommand() { + tv.Timeline.AddFeed( + NewNotificatioMentionsFeed(tv, true, true), + ) +} + func (tv *TutView) ListsCommand() { tv.Timeline.AddFeed( NewListsFeed(tv), @@ -140,6 +149,118 @@ func (tv *TutView) WindowCommand(index string) { tv.FocusFeed(i) } +func (tv *TutView) MoveWindowLeft() { + tv.Timeline.MoveCurrentWindowLeft() +} + +func (tv *TutView) MoveWindowRight() { + tv.Timeline.MoveCurrentWindowRight() +} + +func (tv *TutView) MoveWindowHome() { + tv.Timeline.MoveCurrentWindowHome() +} + +func (tv *TutView) MoveWindowEnd() { + tv.Timeline.MoveCurrentWindowEnd() +} + +func (tv *TutView) SwitchCommand(s string) { + ft := config.InvalidFeed + + parts := strings.Split(s, ",") + for i, p := range parts { + parts[i] = strings.TrimSpace(p) + } + cmd := parts[0] + var subaction string + if strings.Contains(parts[0], " ") { + p := strings.Split(cmd, " ") + cmd = p[0] + subaction = strings.Join(p[1:], " ") + } + showBoosts := true + showReplies := true + name := "" + if len(parts) > 1 { + tfStr := []string{"true", "false"} + name = parts[1] + if slices.Contains(tfStr, name) { + name = "" + } + if len(parts) > 2 && slices.Contains(tfStr, parts[len(parts)-2]) && + slices.Contains(tfStr, parts[len(parts)-1]) { + showBoosts = parts[len(parts)-2] == "true" + showReplies = parts[len(parts)-1] == "true" + } else if len(parts) > 1 && slices.Contains(tfStr, parts[len(parts)-1]) { + showBoosts = parts[len(parts)-1] == "true" + } else { + fmt.Printf("switch is invalid . Check this for errors: switch %s\n", s) + os.Exit(1) + } + } + var data string + switch cmd { + case "home": + ft = config.TimelineHome + case "direct": + ft = config.Conversations + case "local": + ft = config.TimelineLocal + case "federated": + ft = config.TimelineFederated + case "special": + ft = config.TimelineHomeSpecial + case "special-all": + ft = config.TimelineHomeSpecial + showBoosts = true + showReplies = true + case "special-boosts": + ft = config.TimelineHomeSpecial + showBoosts = true + showReplies = false + case "special-replies": + ft = config.TimelineHomeSpecial + showBoosts = false + showReplies = true + case "bookmarks", "saved": + ft = config.Saved + case "favorited": + ft = config.Favorited + case "notifications": + ft = config.Notifications + case "lists": + ft = config.Lists + case "tag": + ft = config.Tag + data = subaction + case "blocking": + ft = config.Blocking + case "muting": + ft = config.Muting + case "tags": + ft = config.Tags + case "mentions": + ft = config.Mentions + } + found := tv.Timeline.FindAndGoTo(ft, data, showBoosts, showReplies) + if found { + return + } + nf := CreateFeed(tv, ft, data, showBoosts, showReplies) + tv.Timeline.Feeds = append(tv.Timeline.Feeds, &FeedHolder{ + Feeds: []*Feed{nf}, + Name: name, + }) + tv.FocusFeed(len(tv.Timeline.Feeds) - 1) + tv.Shared.Top.SetText(tv.Timeline.GetTitle()) + tv.Timeline.update <- true +} + +func (tv *TutView) CloseWindowCommand() { + tv.Timeline.CloseCurrentWindow() +} + func (tv *TutView) BoostsCommand() { item, itemErr := tv.GetCurrentItem() if itemErr != nil { diff --git a/ui/feed.go b/ui/feed.go index a8e3082..f260651 100644 --- a/ui/feed.go +++ b/ui/feed.go @@ -45,10 +45,12 @@ func outFocus(l *tview.List, style config.Style) { } type Feed struct { - tutView *TutView - Data *feed.Feed - List *FeedList - Content *FeedContent + tutView *TutView + Data *feed.Feed + List *FeedList + Content *FeedContent + ShowBoosts bool + ShowReplies bool } func (f *Feed) ListInFocus() { @@ -143,10 +145,12 @@ func NewHomeFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { f := feed.NewTimelineHome(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -157,10 +161,12 @@ func NewHomeSpecialFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { f := feed.NewTimelineHomeSpecial(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -171,10 +177,12 @@ func NewFederatedFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { f := feed.NewTimelineFederated(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -185,10 +193,12 @@ func NewLocalFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { f := feed.NewTimelineLocal(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -199,10 +209,28 @@ func NewNotificationFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { f := feed.NewNotifications(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, + } + go fd.update() + + return fd +} + +func NewNotificatioMentionsFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { + f := feed.NewNotificationsMentions(tv.tut.Client, tv.tut.Config) + f.LoadNewer() + fd := &Feed{ + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -214,10 +242,12 @@ func NewThreadFeed(tv *TutView, item api.Item) *Feed { f := feed.NewThread(tv.tut.Client, tv.tut.Config, status) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } for i, s := range f.List() { main, symbol := DrawListItem(tv.tut.Config, s) @@ -236,10 +266,12 @@ func NewHistoryFeed(tv *TutView, item api.Item) *Feed { f := feed.NewHistory(tv.tut.Client, tv.tut.Config, status) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } for _, s := range f.List() { main, symbol := DrawListItem(tv.tut.Config, s) @@ -255,10 +287,12 @@ func NewConversationsFeed(tv *TutView) *Feed { f := feed.NewConversations(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -273,10 +307,12 @@ func NewUserFeed(tv *TutView, item api.Item) *Feed { f := feed.NewUserProfile(tv.tut.Client, tv.tut.Config, u) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -287,10 +323,12 @@ func NewUserSearchFeed(tv *TutView, search string) *Feed { f := feed.NewUserSearch(tv.tut.Client, tv.tut.Config, search) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } for _, s := range f.List() { main, symbol := DrawListItem(tv.tut.Config, s) @@ -305,10 +343,12 @@ func NewTagFeed(tv *TutView, search string, showBoosts bool, showReplies bool) * f := feed.NewTag(tv.tut.Client, tv.tut.Config, search, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -319,10 +359,12 @@ func NewTagsFeed(tv *TutView) *Feed { f := feed.NewTags(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -333,10 +375,12 @@ func NewListsFeed(tv *TutView) *Feed { f := feed.NewListList(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -347,10 +391,12 @@ func NewListFeed(tv *TutView, l *mastodon.List, showBoosts bool, showReplies boo f := feed.NewList(tv.tut.Client, tv.tut.Config, l, showBoosts, showReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: showBoosts, + ShowReplies: showReplies, } go fd.update() @@ -361,10 +407,12 @@ func NewUsersInListFeed(tv *TutView, l *mastodon.List) *Feed { f := feed.NewUsersInList(tv.tut.Client, tv.tut.Config, l) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -375,10 +423,12 @@ func NewUsersAddListFeed(tv *TutView, l *mastodon.List) *Feed { f := feed.NewUsersAddList(tv.tut.Client, tv.tut.Config, l) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -389,10 +439,12 @@ func NewFavoritedFeed(tv *TutView) *Feed { f := feed.NewFavorites(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -403,10 +455,12 @@ func NewBookmarksFeed(tv *TutView) *Feed { f := feed.NewBookmarks(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -417,10 +471,12 @@ func NewFavoritesStatus(tv *TutView, id mastodon.ID) *Feed { f := feed.NewFavoritesStatus(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -431,10 +487,12 @@ func NewBoosts(tv *TutView, id mastodon.ID) *Feed { f := feed.NewBoosts(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -445,10 +503,12 @@ func NewFollowers(tv *TutView, id mastodon.ID) *Feed { f := feed.NewFollowers(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -459,10 +519,12 @@ func NewFollowing(tv *TutView, id mastodon.ID) *Feed { f := feed.NewFollowing(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -473,10 +535,12 @@ func NewBlocking(tv *TutView) *Feed { f := feed.NewBlocking(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -487,10 +551,12 @@ func NewMuting(tv *TutView) *Feed { f := feed.NewMuting(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() @@ -501,10 +567,12 @@ func NewFollowRequests(tv *TutView) *Feed { f := feed.NewFollowRequests(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + ShowBoosts: true, + ShowReplies: true, } go fd.update() diff --git a/ui/input.go b/ui/input.go index affbd3c..9698b96 100644 --- a/ui/input.go +++ b/ui/input.go @@ -150,6 +150,8 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { tv.ProfileCommand() case config.LeaderNotifications: tv.NotificationsCommand() + case config.LeaderMentions: + tv.MentionsCommand() case config.LeaderLoadNewer: tv.LoadNewerCommand() case config.LeaderLists: @@ -164,6 +166,18 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { tv.TagsCommand() case config.LeaderWindow: tv.WindowCommand(subaction) + case config.LeaderCloseWindow: + tv.CloseWindowCommand() + case config.LeaderMoveWindowLeft: + tv.MoveWindowLeft() + case config.LeaderMoveWindowRight: + tv.MoveWindowRight() + case config.LeaderMoveWindowHome: + tv.MoveWindowHome() + case config.LeaderMoveWindowEnd: + tv.MoveWindowEnd() + case config.LeaderSwitch: + tv.SwitchCommand(subaction) case config.LeaderListPlacement: switch subaction { case "top": diff --git a/ui/item_status.go b/ui/item_status.go index 2180710..411cdf2 100644 --- a/ui/item_status.go +++ b/ui/item_status.go @@ -23,6 +23,7 @@ type Toot struct { Account string Spoiler bool CWText string + SpoilerText string ShowSpoiler bool CWlabel string ContentText string @@ -163,6 +164,7 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview } toot.CWText = strippedSpoiler + toot.SpoilerText = toot.CWText media := []Media{} for _, att := range status.MediaAttachments { diff --git a/ui/timeline.go b/ui/timeline.go index c290e64..29dfe2b 100644 --- a/ui/timeline.go +++ b/ui/timeline.go @@ -21,6 +21,38 @@ type Timeline struct { scrollSleep *scrollSleep } +func CreateFeed(tv *TutView, ft config.FeedType, data string, showBoosts, showReplies bool) *Feed { + var nf *Feed + switch ft { + case config.TimelineHome: + nf = NewHomeFeed(tv, showBoosts, showReplies) + case config.TimelineHomeSpecial: + nf = NewHomeSpecialFeed(tv, showBoosts, showReplies) + case config.Conversations: + nf = NewConversationsFeed(tv) + case config.TimelineLocal: + nf = NewLocalFeed(tv, showBoosts, showReplies) + case config.TimelineFederated: + nf = NewFederatedFeed(tv, showBoosts, showReplies) + case config.Saved: + nf = NewBookmarksFeed(tv) + case config.Favorited: + nf = NewFavoritedFeed(tv) + case config.Notifications: + nf = NewNotificationFeed(tv, showBoosts, showReplies) + case config.Mentions: + nf = NewNotificatioMentionsFeed(tv, showBoosts, showReplies) + case config.Lists: + nf = NewListsFeed(tv) + case config.Tag: + nf = NewTagFeed(tv, data, showBoosts, showReplies) + default: + fmt.Println("Invalid feed") + tv.CleanExit(1) + } + return nf +} + func NewTimeline(tv *TutView, update chan bool) *Timeline { tl := &Timeline{ tutView: tv, @@ -28,33 +60,8 @@ func NewTimeline(tv *TutView, update chan bool) *Timeline { update: update, } tl.scrollSleep = NewScrollSleep(tl.NextItemFeed, tl.PrevItemFeed) - var nf *Feed for _, f := range tv.tut.Config.General.Timelines { - switch f.FeedType { - case config.TimelineHome: - nf = NewHomeFeed(tv, f.ShowBoosts, f.ShowReplies) - case config.TimelineHomeSpecial: - nf = NewHomeSpecialFeed(tv, f.ShowBoosts, f.ShowReplies) - case config.Conversations: - nf = NewConversationsFeed(tv) - case config.TimelineLocal: - nf = NewLocalFeed(tv, f.ShowBoosts, f.ShowReplies) - case config.TimelineFederated: - nf = NewFederatedFeed(tv, f.ShowBoosts, f.ShowReplies) - case config.Saved: - nf = NewBookmarksFeed(tv) - case config.Favorited: - nf = NewFavoritedFeed(tv) - case config.Notifications: - nf = NewNotificationFeed(tv, f.ShowBoosts, f.ShowReplies) - case config.Lists: - nf = NewListsFeed(tv) - case config.Tag: - nf = NewTagFeed(tv, f.Subaction, f.ShowBoosts, f.ShowReplies) - default: - fmt.Println("Invalid feed") - tl.tutView.CleanExit(1) - } + nf := CreateFeed(tv, f.FeedType, f.Subaction, f.ShowBoosts, f.ShowReplies) tl.Feeds = append(tl.Feeds, &FeedHolder{ Feeds: []*Feed{nf}, Name: f.Name, @@ -99,6 +106,68 @@ func (tl *Timeline) RemoveCurrent(quit bool) bool { return false } +func (tl *Timeline) MoveCurrentWindowLeft() { + length := len(tl.Feeds) + if length < 2 { + return + } + ni := tl.FeedFocusIndex - 1 + if ni < 0 { + return + } + tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] + tl.tutView.FocusFeed(ni) +} + +func (tl *Timeline) MoveCurrentWindowRight() { + length := len(tl.Feeds) + if length < 2 { + return + } + ni := tl.FeedFocusIndex + 1 + if ni > length-1 { + return + } + tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] + tl.tutView.FocusFeed(ni) +} + +func (tl *Timeline) MoveCurrentWindowHome() { + length := len(tl.Feeds) + if length < 2 { + return + } + ni := 0 + tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] + tl.tutView.FocusFeed(ni) +} + +func (tl *Timeline) MoveCurrentWindowEnd() { + length := len(tl.Feeds) + if length < 2 { + return + } + ni := len(tl.Feeds) - 1 + tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] + tl.tutView.FocusFeed(ni) +} + +func (tl *Timeline) CloseCurrentWindow() { + if len(tl.Feeds) == 0 { + return + } + feeds := tl.Feeds[tl.FeedFocusIndex] + for _, f := range feeds.Feeds { + f.Data.Close() + } + tl.Feeds = append(tl.Feeds[:tl.FeedFocusIndex], tl.Feeds[tl.FeedFocusIndex+1:]...) + ni := tl.FeedFocusIndex - 1 + if ni < 0 { + ni = 0 + } + tl.tutView.FocusFeed(ni) +} + func (tl *Timeline) NextFeed() { f := tl.Feeds[tl.FeedFocusIndex] l := len(f.Feeds) @@ -122,6 +191,24 @@ func (tl *Timeline) PrevFeed() { tl.update <- true } +func (tl *Timeline) FindAndGoTo(ft config.FeedType, data string, showBoosts, showReplies bool) bool { + for i, fh := range tl.Feeds { + for j, f := range fh.Feeds { + if f.Data.Type() == ft && f.ShowBoosts == showBoosts && f.ShowReplies == showReplies { + if ft == config.Tag && f.Data.Name() != data { + continue + } + tl.tutView.FocusFeed(i) + fh.FeedIndex = j + tl.tutView.Shared.Top.SetText(tl.GetTitle()) + tl.update <- true + return true + } + } + } + return false +} + func (tl *Timeline) DrawContent() { fh := tl.Feeds[tl.FeedFocusIndex] f := fh.Feeds[fh.FeedIndex] @@ -150,6 +237,8 @@ func (tl *Timeline) GetTitle() string { ct = "favorited" case config.Notifications: ct = "notifications" + case config.Mentions: + ct = "mentions" case config.Tag: parts := strings.Split(name, " ") for i, p := range parts {