diff --git a/README.md b/README.md index 3c05bc8..4c10d03 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:favorited` lists toots you've favorited * `:favorites` lists users that favorited the toot * `:h` `:help` view help +* `:history` show edits of a toot * `:lists` show a list of your lists * `:list-placement` top, right, bottom, left * `:list-split` row, column diff --git a/api/feed.go b/api/feed.go index b28832e..a31e54d 100644 --- a/api/feed.go +++ b/api/feed.go @@ -98,6 +98,18 @@ func (ac *AccountClient) GetNotifications(pg *mastodon.Pagination) ([]Item, erro return items, nil } +func (ac *AccountClient) GetHistory(status *mastodon.Status) ([]Item, error) { + var items []Item + statuses, err := ac.Client.GetStatusHistory(context.Background(), status.ID) + if err != nil { + return items, err + } + for _, s := range statuses { + items = append(items, NewStatusHistoryItem(s)) + } + return items, nil +} + func (ac *AccountClient) GetThread(status *mastodon.Status) ([]Item, error) { var items []Item statuses, err := ac.Client.GetStatusContext(context.Background(), status.ID) diff --git a/api/item.go b/api/item.go index 6eb4a70..04c9722 100644 --- a/api/item.go +++ b/api/item.go @@ -174,6 +174,57 @@ func (s *StatusItem) Pinned() bool { return s.pinned } +func NewStatusHistoryItem(item *mastodon.StatusHistory) (sitem Item) { + return &StatusHistoryItem{id: newID(), item: item, showSpoiler: false} +} + +type StatusHistoryItem struct { + id uint + item *mastodon.StatusHistory + showSpoiler bool +} + +func (s *StatusHistoryItem) ID() uint { + return s.id +} + +func (s *StatusHistoryItem) Type() MastodonType { + return StatusHistoryType +} + +func (s *StatusHistoryItem) ToggleSpoiler() { + s.showSpoiler = !s.showSpoiler +} + +func (s *StatusHistoryItem) ShowSpoiler() bool { + return s.showSpoiler +} + +func (s *StatusHistoryItem) Raw() interface{} { + return s.item +} + +func (s *StatusHistoryItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) { + status := mastodon.Status{ + Content: s.item.Content, + SpoilerText: s.item.SpoilerText, + Account: s.item.Account, + Sensitive: s.item.Sensitive, + CreatedAt: s.item.CreatedAt, + Emojis: s.item.Emojis, + MediaAttachments: s.item.MediaAttachments, + } + return getUrlsStatus(&status) +} + +func (s *StatusHistoryItem) Filtered() (bool, string) { + return false, "" +} + +func (s *StatusHistoryItem) Pinned() bool { + return false +} + func NewUserItem(item *User, profile bool) Item { return &UserItem{id: newID(), item: item, profile: profile} } @@ -282,12 +333,15 @@ func (n *NotificationItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Ta return getUrlsStatus(nd.Status.Raw().(*mastodon.Status)) case "poll": return getUrlsStatus(nd.Status.Raw().(*mastodon.Status)) + case "update": + return getUrlsStatus(nd.Status.Raw().(*mastodon.Status)) case "follow": return getUrlsUser(nd.User.Raw().(*User).Data) case "follow_request": return getUrlsUser(nd.User.Raw().(*User).Data) + default: + return []util.URL{}, []mastodon.Mention{}, []mastodon.Tag{}, 0 } - return nil, nil, nil, 0 } func (n *NotificationItem) Filtered() (bool, string) { diff --git a/api/stream.go b/api/stream.go index db28f43..bb70645 100644 --- a/api/stream.go +++ b/api/stream.go @@ -11,6 +11,7 @@ type MastodonType uint const ( StatusType MastodonType = iota + StatusHistoryType UserType ProfileType NotificationType diff --git a/auth/add.go b/auth/add.go index ef4aeaf..44adca5 100644 --- a/auth/add.go +++ b/auth/add.go @@ -54,7 +54,7 @@ func AddAccount(ad *AccountData) *mastodon.Client { } util.OpenURL(srv.AuthURI) - fmt.Println("You need to autorize Tut to use your account. Your browser") + fmt.Println("You need to authorize Tut to use your account. Your browser") fmt.Println("should've opened. If not you can use the URL below.") fmt.Printf("\n%s\n\n", srv.AuthURI) diff --git a/config.example.ini b/config.example.ini index bd849c6..eddd8b3 100644 --- a/config.example.ini +++ b/config.example.ini @@ -150,9 +150,9 @@ leader-timeout=1000 # comma. # # Available commands: home, direct, local, federated, clear-notifications, -# compose, blocking, bookmarks, saved, favorited, boosts, favorites, following, -# followers, muting, newer, preferences, profile, notifications, lists, tag, -# window, list-placement, list-split, proportions +# compose, history, blocking, bookmarks, saved, favorited, boosts, favorites, +# following, followers, muting, newer, preferences, profile, notifications, +# lists, tag, window, list-placement, list-split, proportions # # The shortcuts are up to you, but keep them quite short and make sure they # don't collide. If you have one shortcut that is "f" and an other one that is @@ -174,6 +174,7 @@ leader-timeout=1000 # leader-action=lists,li # leader-action=federated,fed # leader-action=direct,d +# leader-action=history,h # leader-action=tag linux,tl # leader-action=window 0,h # leader-action=list-placement bottom,b @@ -309,6 +310,10 @@ favorite=false # default=false mention=false +# Notification when someone edits their toot. +# default=false +update=false + # Notification when someone boosts one of your toots. # default=false boost=false diff --git a/config/config.go b/config/config.go index 4212f4b..ec4747d 100644 --- a/config/config.go +++ b/config/config.go @@ -74,6 +74,7 @@ const ( LeaderNotifications LeaderLists LeaderTag + LeaderHistory LeaderUser LeaderWindow LeaderLoadNewer @@ -226,6 +227,7 @@ const ( NotificationFollower NotificationType = iota NotificationFavorite NotificationMention + NotificationUpdate NotificationBoost NotificationPoll NotificationPost @@ -235,6 +237,7 @@ type Notification struct { NotificationFollower bool NotificationFavorite bool NotificationMention bool + NotificationUpdate bool NotificationBoost bool NotificationPoll bool NotificationPost bool @@ -862,6 +865,8 @@ func parseGeneral(cfg *ini.File) General { la.Command = LeaderSaved case "favorited": la.Command = LeaderFavorited + case "history": + la.Command = LeaderHistory case "boosts": la.Command = LeaderBoosts case "favorites": @@ -1131,6 +1136,7 @@ func parseNotifications(cfg *ini.File) Notification { nc.NotificationFollower = cfg.Section("desktop-notification").Key("followers").MustBool(false) nc.NotificationFavorite = cfg.Section("desktop-notification").Key("favorite").MustBool(false) nc.NotificationMention = cfg.Section("desktop-notification").Key("mention").MustBool(false) + nc.NotificationUpdate = cfg.Section("desktop-notification").Key("update").MustBool(false) nc.NotificationBoost = cfg.Section("desktop-notification").Key("boost").MustBool(false) nc.NotificationPoll = cfg.Section("desktop-notification").Key("poll").MustBool(false) nc.NotificationPost = cfg.Section("desktop-notification").Key("posts").MustBool(false) diff --git a/config/default_config.go b/config/default_config.go index 798c37b..f988a52 100644 --- a/config/default_config.go +++ b/config/default_config.go @@ -152,9 +152,9 @@ leader-timeout=1000 # comma. # # Available commands: home, direct, local, federated, clear-notifications, -# compose, blocking, bookmarks, saved, favorited, boosts, favorites, following, -# followers, muting, newer, preferences, profile, notifications, lists, tag, -# window, list-placement, list-split, proportions +# compose, history, blocking, bookmarks, saved, favorited, boosts, favorites, +# following, followers, muting, newer, preferences, profile, notifications, +# lists, tag, window, list-placement, list-split, proportions # # The shortcuts are up to you, but keep them quite short and make sure they # don't collide. If you have one shortcut that is "f" and an other one that is @@ -176,6 +176,7 @@ leader-timeout=1000 # leader-action=lists,li # leader-action=federated,fed # leader-action=direct,d +# leader-action=history,h # leader-action=tag linux,tl # leader-action=window 0,h # leader-action=list-placement bottom,b @@ -311,6 +312,10 @@ favorite=false # default=false mention=false +# Notification when someone edits their toot. +# default=false +update=false + # Notification when someone boosts one of your toots. # default=false boost=false diff --git a/config/toot.tmpl b/config/toot.tmpl index 0e3613a..a5eda86 100644 --- a/config/toot.tmpl +++ b/config/toot.tmpl @@ -17,7 +17,7 @@ {{ Color .Style.TextSpecial1 }}{{ .Toot.Account }} {{- else -}} {{ Color .Style.TextSpecial2 }}{{- .Toot.Account }} -{{- end }} +{{- end }} {{- if .Toot.Edited -}}{{ Color .Style.Subtle }} (edited toot){{ end }} {{ if .Toot.Spoiler -}} {{ Color .Style.Text }}{{ .Toot.SpoilerText }} diff --git a/feed/feed.go b/feed/feed.go index 7702df1..abb108c 100644 --- a/feed/feed.go +++ b/feed/feed.go @@ -17,6 +17,7 @@ type apiIDFunc func(pg *mastodon.Pagination, id mastodon.ID) ([]api.Item, error) type apiSearchFunc func(search string) ([]api.Item, error) type apiSearchPGFunc func(pg *mastodon.Pagination, search string) ([]api.Item, error) type apiThreadFunc func(status *mastodon.Status) ([]api.Item, error) +type apiHistoryFunc func(status *mastodon.Status) ([]api.Item, error) type FeedType uint @@ -29,6 +30,7 @@ const ( FollowRequests Blocking Muting + History InvalidFeed Notification Saved @@ -52,10 +54,11 @@ type LoadingLock struct { type DesktopNotificationType uint const ( - DekstopNotificationNone DesktopNotificationType = iota + DeskstopNotificationNone DesktopNotificationType = iota DesktopNotificationFollower DesktopNotificationFavorite DesktopNotificationMention + DesktopNotificationUpdate DesktopNotificationBoost DesktopNotificationPoll DesktopNotificationPost @@ -100,14 +103,14 @@ func (f *Feed) Delete(id uint) { } } f.items = items - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } func (f *Feed) Clear() { f.itemsMux.Lock() defer f.itemsMux.Unlock() f.items = []api.Item{} - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } func (f *Feed) Item(index int) (api.Item, error) { @@ -142,7 +145,7 @@ func (f *Feed) LoadNewer() { return } f.loadNewer() - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) f.loadingNewer.last = time.Now() f.loadingNewer.mux.Unlock() } @@ -160,7 +163,7 @@ func (f *Feed) LoadOlder() { return } f.loadOlder() - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) f.loadingOlder.last = time.Now() f.loadingOlder.mux.Unlock() } @@ -191,7 +194,7 @@ func (f *Feed) singleNewerSearch(fn apiSearchFunc, search string) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -204,7 +207,20 @@ func (f *Feed) singleThread(fn apiThreadFunc, status *mastodon.Status) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) + } + f.itemsMux.Unlock() +} + +func (f *Feed) singleHistory(fn apiHistoryFunc, status *mastodon.Status) { + items, err := fn(status) + if err != nil { + return + } + f.itemsMux.Lock() + if len(items) > 0 { + f.items = append(items, f.items...) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -237,7 +253,7 @@ func (f *Feed) normalNewer(fn apiFunc) { } } f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() f.apiDataMux.Unlock() @@ -266,7 +282,7 @@ func (f *Feed) normalOlder(fn apiFunc) { f.apiData.MaxID = item.Item.ID } f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() f.apiDataMux.Unlock() @@ -288,7 +304,7 @@ func (f *Feed) newerSearchPG(fn apiSearchPGFunc, search string) { item := items[0].Raw().(*mastodon.Status) f.apiData.MinID = item.ID f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) if f.apiData.MaxID == mastodon.ID("") { item = items[len(items)-1].Raw().(*mastodon.Status) f.apiData.MaxID = item.ID @@ -317,7 +333,7 @@ func (f *Feed) olderSearchPG(fn apiSearchPGFunc, search string) { item := items[len(items)-1].Raw().(*mastodon.Status) f.apiData.MaxID = item.ID f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() f.apiDataMux.Unlock() @@ -339,7 +355,7 @@ func (f *Feed) normalNewerUser(fn apiIDFunc, id mastodon.ID) { item := items[0].Raw().(*mastodon.Status) f.apiData.MinID = item.ID f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) if f.apiData.MaxID == mastodon.ID("") { item = items[len(items)-1].Raw().(*mastodon.Status) f.apiData.MaxID = item.ID @@ -368,7 +384,7 @@ func (f *Feed) normalOlderUser(fn apiIDFunc, id mastodon.ID) { item := items[len(items)-1].Raw().(*mastodon.Status) f.apiData.MaxID = item.ID f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() f.apiDataMux.Unlock() @@ -390,7 +406,7 @@ func (f *Feed) normalNewerID(fn apiIDFunc, id mastodon.ID) { item := items[0].Raw().(*mastodon.Status) f.apiData.MinID = item.ID f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) if f.apiData.MaxID == mastodon.ID("") { item = items[len(items)-1].Raw().(*mastodon.Status) f.apiData.MaxID = item.ID @@ -419,7 +435,7 @@ func (f *Feed) normalOlderID(fn apiIDFunc, id mastodon.ID) { item := items[len(items)-1].Raw().(*mastodon.Status) f.apiData.MaxID = item.ID f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() f.apiDataMux.Unlock() @@ -433,7 +449,7 @@ func (f *Feed) normalEmpty(fn apiEmptyFunc) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -459,7 +475,7 @@ func (f *Feed) linkNewer(fn apiFunc) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -484,7 +500,7 @@ func (f *Feed) linkOlder(fn apiFunc) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -510,7 +526,7 @@ func (f *Feed) linkNewerID(fn apiIDFunc, id mastodon.ID) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(items, f.items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -535,7 +551,7 @@ func (f *Feed) linkOlderID(fn apiIDFunc, id mastodon.ID) { f.itemsMux.Lock() if len(items) > 0 { f.items = append(f.items, items...) - f.Updated(DekstopNotificationNone) + f.Updated(DeskstopNotificationNone) } f.itemsMux.Unlock() } @@ -584,7 +600,7 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e }, f.accountClient.Filters) f.itemsMux.Lock() f.items = append([]api.Item{s}, f.items...) - nft := DekstopNotificationNone + nft := DeskstopNotificationNone switch t.Notification.Type { case "follow", "follow_request": nft = DesktopNotificationFollower @@ -594,10 +610,14 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e nft = DesktopNotificationBoost case "mention": nft = DesktopNotificationMention + case "update": + nft = DesktopNotificationUpdate case "status": nft = DesktopNotificationPost case "poll": nft = DesktopNotificationPoll + default: + nft = DeskstopNotificationNone } f.Updated(nft) f.itemsMux.Unlock() @@ -716,6 +736,18 @@ func NewThread(ac *api.AccountClient, status *mastodon.Status) *Feed { return feed } +func NewHistory(ac *api.AccountClient, status *mastodon.Status) *Feed { + feed := newFeed(ac, History) + once := true + feed.loadNewer = func() { + if once { + feed.singleHistory(feed.accountClient.GetHistory, status) + once = false + } + } + return feed +} + func NewTag(ac *api.AccountClient, search string) *Feed { feed := newFeed(ac, Tag) feed.name = search diff --git a/go.mod b/go.mod index b1f321e..2835924 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/RasmusLindroth/tut go 1.18 require ( - github.com/RasmusLindroth/go-mastodon v0.0.9 + github.com/RasmusLindroth/go-mastodon v0.0.10 github.com/atotto/clipboard v0.1.4 github.com/gdamore/tcell/v2 v2.5.3 github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6 diff --git a/go.sum b/go.sum index 2123dbb..5a08ded 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,16 @@ -github.com/RasmusLindroth/go-mastodon v0.0.8 h1:t2rrbdNgS4h0JhmPNsmUOQBByDxmUPawnERGn6oR2eA= -github.com/RasmusLindroth/go-mastodon v0.0.8/go.mod h1:4L0oyiNwq1tUoiByczzhSikxR9RiANzELtZgexxKpPM= -github.com/RasmusLindroth/go-mastodon v0.0.9 h1:3Moqcs5mr65SUgofwfB4eEiKgwMpbRl3ncfyxu+DFjw= -github.com/RasmusLindroth/go-mastodon v0.0.9/go.mod h1:Lr6n8V1U2b+9P89YZKsICkNc+oNeJXkygY7raei9SXE= +github.com/RasmusLindroth/go-mastodon v0.0.10 h1:huGNcPn5SASfJDhBL4drKL0PFJ29+hqjCroIrkf2R0E= +github.com/RasmusLindroth/go-mastodon v0.0.10/go.mod h1:Lr6n8V1U2b+9P89YZKsICkNc+oNeJXkygY7raei9SXE= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.5.2 h1:tKzG29kO9p2V++3oBY2W9zUjYu7IK1MENFeY/BzJSVY= -github.com/gdamore/tcell/v2 v2.5.2/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0= github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= -github.com/gen2brain/beeep v0.0.0-20220518085355-d7852edf42fc h1:6ZZLxG+lB+Qbg+chtzAEeetwqjlPnY0BXbhL3lQWYOg= -github.com/gen2brain/beeep v0.0.0-20220518085355-d7852edf42fc/go.mod h1:/WeFVhhxMOGypVKS0w8DUJxUBbHypnWkUVnW7p5c9Pw= github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6 h1:jFEK/SA/7E8lg9T33+y8D4Z0I782+bbiEjmyyklRzRQ= github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6/go.mod h1:/WeFVhhxMOGypVKS0w8DUJxUBbHypnWkUVnW7p5c9Pw= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= @@ -29,81 +23,48 @@ 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.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/icza/gox v0.0.0-20220812133721-0fbf7a534d8e h1:vD4inAaWEbFk4Dpu1Y+m9URk8s6dOif7pBW5pW7fAak= -github.com/icza/gox v0.0.0-20220812133721-0fbf7a534d8e/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= github.com/icza/gox v0.0.0-20221026131554-a08a8cdc726a h1:ctOSka++0Y+9xF7VLtZ8TOJjyXjOGYywzuhbzj3IEHw= github.com/icza/gox v0.0.0-20221026131554-a08a8cdc726a/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= -github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= -github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= -github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/tview v0.0.0-20220812085834-0e6b21a48e96 h1:O435d1KIgG6KxpP7NDdmj7SdaLIzq4F+PG8ZB/BHC4c= -github.com/rivo/tview v0.0.0-20220812085834-0e6b21a48e96/go.mod h1:hyzpnqn4KWzZopTEjL1AxvlzOLMH1IuKo4lTw6vyOQc= -github.com/rivo/tview v0.0.0-20221029100920-c4a7e501810d h1:jKIUJdMcIVGOSHi6LSqJqw9RqblyblE2ZrHvFbWR3S0= -github.com/rivo/tview v0.0.0-20221029100920-c4a7e501810d/go.mod h1:YX2wUZOcJGOIycErz2s9KvDaP0jnWwRCirQMPLPpQ+Y= -github.com/rivo/tview v0.0.0-20221115143349-ed3ea789e9f7 h1:9m4q3Li2CP/O4yfbZYJhySCuUg/VpV4jZhpvUcUYu8E= -github.com/rivo/tview v0.0.0-20221115143349-ed3ea789e9f7/go.mod h1:YX2wUZOcJGOIycErz2s9KvDaP0jnWwRCirQMPLPpQ+Y= github.com/rivo/tview v0.0.0-20221117065207-09f052e6ca98 h1:0nVxhPi+jdqG11c3n4zTcZQbjGy0yi60ym/6B+NITPU= github.com/rivo/tview v0.0.0-20221117065207-09f052e6ca98/go.mod h1:YX2wUZOcJGOIycErz2s9KvDaP0jnWwRCirQMPLPpQ+Y= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= -github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= -github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= diff --git a/main.go b/main.go index 2f96356..1ebc11d 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "github.com/rivo/tview" ) -const version = "1.0.18" +const version = "1.0.19" func main() { util.SetTerminalTitle("tut") diff --git a/ui/cmdbar.go b/ui/cmdbar.go index 288a47a..cdf14da 100644 --- a/ui/cmdbar.go +++ b/ui/cmdbar.go @@ -88,6 +88,9 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { case ":followers": c.tutView.FollowersCommand() c.Back() + case ":history": + c.tutView.HistoryCommand() + c.Back() case ":newer": c.tutView.LoadNewerCommand() c.Back() @@ -208,7 +211,7 @@ 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,:followers,:following,:help,:h,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:requests,:saved,:tag,:timeline,:tl,:user,:window,:quit,:q", ",") + words := strings.Split(":blocking,:boosts,:bookmarks,:clear-notifications,:compose,:favorites,:favorited,:followers,:following,:help,:h,:history,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:requests,:saved,:tag,:timeline,:tl,:user,:window,:quit,:q", ",") if curr == "" { return entries } diff --git a/ui/commands.go b/ui/commands.go index 8429869..9380d88 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -155,6 +155,19 @@ func (tv *TutView) FollowersCommand() { ) } +func (tv *TutView) HistoryCommand() { + item, itemErr := tv.GetCurrentItem() + if itemErr != nil { + return + } + if item.Type() != api.StatusType { + return + } + tv.Timeline.AddFeed( + NewHistoryFeed(tv, item), + ) +} + func (tv *TutView) ProfileCommand() { item, err := tv.tut.Client.GetUserByID(tv.tut.Client.Me.ID) if err != nil { diff --git a/ui/feed.go b/ui/feed.go index 3fc6738..269dfc6 100644 --- a/ui/feed.go +++ b/ui/feed.go @@ -102,6 +102,10 @@ func (f *Feed) update() { if f.tutView.tut.Config.NotificationConfig.NotificationMention { beeep.Notify("Mentioned you", "", "") } + case feed.DesktopNotificationUpdate: + if f.tutView.tut.Config.NotificationConfig.NotificationUpdate { + beeep.Notify("Changed their toot", "", "") + } case feed.DesktopNotificationBoost: if f.tutView.tut.Config.NotificationConfig.NotificationBoost { beeep.Notify("Boosted your toot", "", "") @@ -214,6 +218,27 @@ func NewThreadFeed(tv *TutView, item api.Item) *Feed { return fd } +func NewHistoryFeed(tv *TutView, item api.Item) *Feed { + status := util.StatusOrReblog(item.Raw().(*mastodon.Status)) + f := feed.NewHistory(tv.tut.Client, status) + f.LoadNewer() + fd := &Feed{ + tutView: tv, + Data: f, + ListIndex: 0, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + } + for _, s := range f.List() { + main, symbol := DrawListItem(tv.tut.Config, s) + fd.List.AddItem(main, symbol, s.ID()) + } + fd.List.SetCurrentItem(0) + fd.DrawContent() + + return fd +} + func NewConversationsFeed(tv *TutView) *Feed { f := feed.NewConversations(tv.tut.Client) f.LoadNewer() diff --git a/ui/input.go b/ui/input.go index b2eae75..2987456 100644 --- a/ui/input.go +++ b/ui/input.go @@ -125,6 +125,8 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { tv.BookmarksCommand() case config.LeaderFavorited: tv.FavoritedCommand() + case config.LeaderHistory: + tv.HistoryCommand() case config.LeaderBoosts: tv.BoostsCommand() case config.LeaderFavorites: @@ -299,6 +301,8 @@ func (tv *TutView) InputItem(event *tcell.EventKey) *tcell.EventKey { switch item.Type() { case api.StatusType: return tv.InputStatus(event, item, item.Raw().(*mastodon.Status), nil) + case api.StatusHistoryType: + return tv.InputStatusHistory(event, item, item.Raw().(*mastodon.StatusHistory), nil) case api.UserType, api.ProfileType: if ft == feed.FollowRequests { return tv.InputUser(event, item.Raw().(*api.User), true) @@ -318,6 +322,8 @@ func (tv *TutView) InputItem(event *tcell.EventKey) *tcell.EventKey { return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), user.Data) case "mention": return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil) + case "update": + return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil) case "status": return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil) case "poll": @@ -492,6 +498,56 @@ func (tv *TutView) InputStatus(event *tcell.EventKey, item api.Item, status *mas return event } +func (tv *TutView) InputStatusHistory(event *tcell.EventKey, item api.Item, sr *mastodon.StatusHistory, nAcc *mastodon.Account) *tcell.EventKey { + hasMedia := len(sr.MediaAttachments) > 0 + hasSpoiler := sr.Sensitive + + status := &mastodon.Status{ + Content: sr.Content, + SpoilerText: sr.SpoilerText, + Account: sr.Account, + Sensitive: sr.Sensitive, + CreatedAt: sr.CreatedAt, + Emojis: sr.Emojis, + MediaAttachments: sr.MediaAttachments, + } + + if tv.tut.Config.Input.StatusAvatar.Match(event.Key(), event.Rune()) { + if nAcc != nil { + openAvatar(tv, *nAcc) + } else { + openAvatar(tv, sr.Account) + } + return nil + } + if tv.tut.Config.Input.StatusMedia.Match(event.Key(), event.Rune()) { + if hasMedia { + openMedia(tv, status) + } + return nil + } + if tv.tut.Config.Input.StatusLinks.Match(event.Key(), event.Rune()) { + tv.SetPage(LinkFocus) + return nil + } + if tv.tut.Config.Input.StatusViewFocus.Match(event.Key(), event.Rune()) { + tv.SetPage(ViewFocus) + return nil + } + if tv.tut.Config.Input.StatusToggleSpoiler.Match(event.Key(), event.Rune()) { + if !hasSpoiler { + return nil + } + if !item.ShowSpoiler() { + item.ToggleSpoiler() + tv.RedrawContent() + } + return nil + } + + return event +} + func (tv *TutView) InputUser(event *tcell.EventKey, user *api.User, fr bool) *tcell.EventKey { blocking := user.Relation.Blocking muting := user.Relation.Muting diff --git a/ui/item.go b/ui/item.go index 42fa921..102d048 100644 --- a/ui/item.go +++ b/ui/item.go @@ -34,6 +34,11 @@ func DrawListItem(cfg *config.Config, item api.Item) (string, string) { } d := OutputDate(cfg, s.CreatedAt.Local()) return fmt.Sprintf("%s %s", d, acc), symbol + case api.StatusHistoryType: + s := item.Raw().(*mastodon.StatusHistory) + acc := strings.TrimSpace(s.Account.Acct) + d := OutputDate(cfg, s.CreatedAt.Local()) + return fmt.Sprintf("%s %s", d, acc), "" case api.UserType: a := item.Raw().(*api.User) return strings.TrimSpace(a.Data.Acct), "" @@ -51,6 +56,8 @@ func DrawListItem(cfg *config.Config, item api.Item) (string, string) { symbol = " ♺ " case "mention": symbol = " ⤶ " + case "update": + symbol = " ☢ " case "poll": symbol = " = " case "status": @@ -69,7 +76,20 @@ func DrawListItem(cfg *config.Config, item api.Item) (string, string) { func DrawItem(tv *TutView, item api.Item, main *tview.TextView, controls *tview.Flex, ft feed.FeedType) { switch item.Type() { case api.StatusType: - drawStatus(tv, item, item.Raw().(*mastodon.Status), main, controls, "") + drawStatus(tv, item, item.Raw().(*mastodon.Status), main, controls, false, "") + case api.StatusHistoryType: + s := item.Raw().(*mastodon.StatusHistory) + status := mastodon.Status{ + Content: s.Content, + SpoilerText: s.SpoilerText, + Account: s.Account, + Sensitive: s.Sensitive, + CreatedAt: s.CreatedAt, + Emojis: s.Emojis, + MediaAttachments: s.MediaAttachments, + Visibility: mastodon.VisibilityPublic, + } + drawStatus(tv, item, &status, main, controls, true, "") case api.UserType, api.ProfileType: if ft == feed.FollowRequests { drawUser(tv, item.Raw().(*api.User), main, controls, "", true) @@ -86,7 +106,20 @@ func DrawItem(tv *TutView, item api.Item, main *tview.TextView, controls *tview. func DrawItemControls(tv *TutView, item api.Item, controls *tview.Flex, ft feed.FeedType) { switch item.Type() { case api.StatusType: - drawStatus(tv, item, item.Raw().(*mastodon.Status), nil, controls, "") + drawStatus(tv, item, item.Raw().(*mastodon.Status), nil, controls, false, "") + case api.StatusHistoryType: + s := item.Raw().(*mastodon.StatusHistory) + status := mastodon.Status{ + Content: s.Content, + SpoilerText: s.SpoilerText, + Account: s.Account, + Sensitive: s.Sensitive, + CreatedAt: s.CreatedAt, + Emojis: s.Emojis, + MediaAttachments: s.MediaAttachments, + Visibility: mastodon.VisibilityPublic, + } + drawStatus(tv, item, &status, nil, controls, true, "") case api.UserType, api.ProfileType: if ft == feed.FollowRequests { drawUser(tv, item.Raw().(*api.User), nil, controls, "", true) diff --git a/ui/item_notification.go b/ui/item_notification.go index 086b07d..9f1b946 100644 --- a/ui/item_notification.go +++ b/ui/item_notification.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/RasmusLindroth/tut/api" + "github.com/RasmusLindroth/tut/config" "github.com/RasmusLindroth/tut/util" "github.com/rivo/tview" ) @@ -15,23 +16,27 @@ func drawNotification(tv *TutView, item api.Item, notification *api.Notification fmt.Sprintf("%s started following you", util.FormatUsername(notification.Item.Account)), false, ) case "favourite": - drawStatus(tv, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false, fmt.Sprintf("%s favorited your toot", util.FormatUsername(notification.Item.Account)), ) case "reblog": - drawStatus(tv, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false, fmt.Sprintf("%s boosted your toot", util.FormatUsername(notification.Item.Account)), ) case "mention": - drawStatus(tv, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false, fmt.Sprintf("%s mentioned you", util.FormatUsername(notification.Item.Account)), ) + case "update": + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false, + fmt.Sprintf("%s updated their toot", util.FormatUsername(notification.Item.Account)), + ) case "status": - drawStatus(tv, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false, fmt.Sprintf("%s posted a new toot", util.FormatUsername(notification.Item.Account)), ) case "poll": - drawStatus(tv, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false, "A poll of yours or one you participated in has ended", ) case "follow_request": @@ -39,5 +44,12 @@ func drawNotification(tv *TutView, item api.Item, notification *api.Notification fmt.Sprintf("%s wants to follow you.", util.FormatUsername(notification.Item.Account)), true, ) + default: + controls.Clear() + text := fmt.Sprintf("%s\n", config.SublteText(tv.tut.Config, + fmt.Sprintf("Notification \"%s\" is not implemented. Open an issue at https://github.com/RasmusLindroth/tut", + notification.Item.Type), + )) + main.SetText(text) } } diff --git a/ui/item_status.go b/ui/item_status.go index bd367d0..1766aa5 100644 --- a/ui/item_status.go +++ b/ui/item_status.go @@ -33,6 +33,7 @@ type Toot struct { Replies int Boosts int Favorites int + Edited bool Controls string } @@ -70,7 +71,7 @@ type DisplayTootData struct { Style config.Style } -func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview.TextView, controls *tview.Flex, additional string) { +func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview.TextView, controls *tview.Flex, isHistory bool, additional string) { filtered, phrase := item.Filtered() if filtered { var output string @@ -119,6 +120,7 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview toot.Bookmarked = status.Bookmarked toot.Visibility = status.Visibility toot.Spoiler = status.Sensitive + toot.Edited = status.CreatedAt.Before(status.EditedAt) if status.Poll != nil { p := *status.Poll @@ -188,20 +190,22 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview } var info []Control - if status.Favourited { + if status.Favourited && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusFavorite, false)) - } else { + } else if !status.Favourited && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusFavorite, true)) } - if status.Reblogged { + if status.Reblogged && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBoost, false)) - } else { + } else if !status.Reblogged && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBoost, true)) } - info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusThread, true)) - info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusReply, true)) - info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusViewFocus, true)) - info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusUser, true)) + if !isHistory { + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusThread, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusReply, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusViewFocus, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusUser, true)) + } if len(status.MediaAttachments) > 0 { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusMedia, true)) } @@ -210,16 +214,18 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusLinks, true)) } info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusAvatar, true)) - if status.Account.ID == tv.tut.Client.Me.ID { + if status.Account.ID == tv.tut.Client.Me.ID && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusDelete, true)) } - if !status.Bookmarked { + if !status.Bookmarked && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBookmark, true)) - } else { + } else if status.Bookmarked && !isHistory { info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBookmark, false)) } - info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusYank, true)) + if !isHistory { + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusYank, true)) + } controls.Clear() for i, item := range info { diff --git a/ui/open.go b/ui/open.go index 7c25e1c..c527a89 100644 --- a/ui/open.go +++ b/ui/open.go @@ -106,10 +106,7 @@ func OpenEditor(tv *TutView, content string) (string, error) { cmd.Stderr = os.Stderr var text []byte tv.tut.App.Suspend(func() { - err = cmd.Run() - if err != nil { - log.Fatalln(err) - } + cmd.Run() text, err = os.ReadFile(fname) }) os.Remove(fname) diff --git a/ui/timeline.go b/ui/timeline.go index e3d5a26..67c7a6e 100644 --- a/ui/timeline.go +++ b/ui/timeline.go @@ -151,6 +151,8 @@ func (tl *Timeline) GetTitle() string { ct = fmt.Sprintf("tag #%s", name) case feed.Thread: ct = "thread feed" + case feed.History: + ct = "history feed" case feed.TimelineFederated: ct = "federated" case feed.TimelineHome: