Browse Source

1.0.11 (#152)

* fix filters and add sticky

* upgrade packages

* update version

* Input user on original user, not reblog

* update example config

* update readme
pull/156/head 1.0.11
Rasmus Lindroth 4 years ago committed by GitHub
parent
commit
9b7aaa3a35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      README.md
  2. 64
      api/feed.go
  3. 68
      api/item.go
  4. 10
      config.example.ini
  5. 50
      config/config.go
  6. 10
      config/default_config.go
  7. 78
      feed/feed.go
  8. 6
      go.mod
  9. 12
      go.sum
  10. BIN
      images/preview2.png
  11. 2
      main.go
  12. 2
      ui/cliview.go
  13. 54
      ui/feed.go
  14. 4
      ui/input.go
  15. 11
      ui/item.go
  16. 18
      ui/item_status.go

9
README.md

@ -1,4 +1,9 @@
# Tut - a Mastodon TUI
[![Release](https://badgen.net/github/release/RasmusLindroth/tut)](https://github.com/RasmusLindroth/tut/releases)
[![web](https://badgen.net/badge/web/tut.anv.nu/f92672)](https://tut.anv.nu)
[![tut](https://badgen.net/badge/AUR/tut/08c)](https://aur.archlinux.org/packages/tut)
[![tut-bin](https://badgen.net/badge/AUR/tut-bin/08c)](https://aur.archlinux.org/packages/tut-bin)
[![@tut](https://badgen.net/mastodon/follow/tut@fosstodon.org)](https://fosstodon.org/@tut)
A TUI for Mastodon with vim inspired keys. The program has most of the
features you can find in the web client.
@ -8,6 +13,7 @@ Press `C` to create a new toot and `N` to focus on your notifications.
You can find Linux binaries under [releases](https://github.com/RasmusLindroth/tut/releases).
![Preview](./images/preview.png "Preview")
![Preview 2](./images/preview2.png "Preview 2")
## Table of contents
@ -85,6 +91,7 @@ Head over to https://github.com/RasmusLindroth/tut/releases
You can find it in the Arch User Repository (AUR). I'm the maintainer there.
https://aur.archlinux.org/packages/tut/
https://aur.archlinux.org/packages/tut-bin/
You can also use `tut-mastodon`. Currently `aur/tut` collides with a package
named `tut` if you're running Manjaro ARM. So if you face the same problem you
@ -127,7 +134,7 @@ cd tut
# Build or install
# Install (usally /home/user/go/bin)
# Install (usually /home/user/go/bin)
go install
# Build (same directory i.e. ./ )

64
api/feed.go

@ -15,10 +15,8 @@ func (ac *AccountClient) getStatusSimilar(fn func() ([]*mastodon.Status, error),
return items, err
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, filter)
if !filtered {
items = append(items, item)
}
item := NewStatusItem(s, ac.Filters, filter, false)
items = append(items, item)
}
return items, nil
}
@ -89,12 +87,10 @@ func (ac *AccountClient) GetNotifications(pg *mastodon.Pagination) ([]Item, erro
for _, n := range notifications {
for _, r := range rel {
if n.Account.ID == r.ID {
item, filtered := NewNotificationItem(n, &User{
item := NewNotificationItem(n, &User{
Data: &n.Account, Relation: r,
}, ac.Filters)
if !filtered {
items = append(items, item)
}
items = append(items, item)
break
}
}
@ -102,29 +98,20 @@ func (ac *AccountClient) GetNotifications(pg *mastodon.Pagination) ([]Item, erro
return items, nil
}
func (ac *AccountClient) GetThread(status *mastodon.Status) ([]Item, int, error) {
func (ac *AccountClient) GetThread(status *mastodon.Status) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetStatusContext(context.Background(), status.ID)
if err != nil {
return items, 0, err
return items, err
}
for _, s := range statuses.Ancestors {
item, filtered := NewStatusItem(s, ac.Filters, "thread")
if !filtered {
items = append(items, item)
}
}
item, filtered := NewStatusItem(status, ac.Filters, "thread")
if !filtered {
items = append(items, item)
items = append(items, NewStatusItem(s, ac.Filters, "thread", false))
}
items = append(items, NewStatusItem(status, ac.Filters, "thread", false))
for _, s := range statuses.Descendants {
item, filtered := NewStatusItem(s, ac.Filters, "thread")
if !filtered {
items = append(items, item)
}
items = append(items, NewStatusItem(s, ac.Filters, "thread", false))
}
return items, len(statuses.Ancestors), nil
return items, nil
}
func (ac *AccountClient) GetFavorites(pg *mastodon.Pagination) ([]Item, error) {
@ -148,10 +135,8 @@ func (ac *AccountClient) GetConversations(pg *mastodon.Pagination) ([]Item, erro
return items, err
}
for _, c := range conversations {
item, filtered := NewStatusItem(c.LastStatus, ac.Filters, "thread")
if !filtered {
items = append(items, item)
}
item := NewStatusItem(c.LastStatus, ac.Filters, "thread", false)
items = append(items, item)
}
return items, nil
}
@ -240,10 +225,21 @@ func (ac *AccountClient) GetUser(pg *mastodon.Pagination, id mastodon.ID) ([]Ite
return items, err
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "account")
if !filtered {
items = append(items, item)
}
item := NewStatusItem(s, ac.Filters, "account", false)
items = append(items, item)
}
return items, nil
}
func (ac *AccountClient) GetUserPinned(id mastodon.ID) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetAccountPinnedStatuses(context.Background(), id)
if err != nil {
return items, err
}
for _, s := range statuses {
item := NewStatusItem(s, ac.Filters, "account", true)
items = append(items, item)
}
return items, nil
}
@ -267,10 +263,8 @@ func (ac *AccountClient) GetListStatuses(pg *mastodon.Pagination, id mastodon.ID
return items, err
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "home")
if !filtered {
items = append(items, item)
}
item := NewStatusItem(s, ac.Filters, "home", false)
items = append(items, item)
}
return items, nil
}

68
api/item.go

@ -26,12 +26,19 @@ type Item interface {
ShowSpoiler() bool
Raw() interface{}
URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int)
Filtered() (bool, string)
Pinned() bool
}
func NewStatusItem(item *mastodon.Status, filters []*mastodon.Filter, timeline string) (sitem Item, filtered bool) {
filtered = false
type filtered struct {
inUse bool
name string
}
func NewStatusItem(item *mastodon.Status, filters []*mastodon.Filter, timeline string, pinned bool) (sitem Item) {
filtered := filtered{inUse: false}
if item == nil {
return &StatusItem{id: newID(), item: item, showSpoiler: false}, false
return &StatusItem{id: newID(), item: item, showSpoiler: false, filtered: filtered, pinned: pinned}
}
s := util.StatusOrReblog(item)
content := s.Content
@ -67,30 +74,35 @@ func NewStatusItem(item *mastodon.Status, filters []*mastodon.Filter, timeline s
filter := strings.Split(strings.ToLower(f.Phrase), " ")
for i := 0; i+len(filter)-1 < len(stripped); i++ {
if strings.ToLower(f.Phrase) == strings.Join(stripped[i:i+len(filter)], " ") {
filtered = true
filtered.inUse = true
filtered.name = f.Phrase
break
}
}
} else {
if strings.Contains(s.Content, strings.ToLower(f.Phrase)) {
filtered = true
filtered.inUse = true
filtered.name = f.Phrase
}
if strings.Contains(s.SpoilerText, strings.ToLower(f.Phrase)) {
filtered = true
filtered.inUse = true
filtered.name = f.Phrase
}
}
if filtered {
if filtered.inUse {
break
}
}
sitem = &StatusItem{id: newID(), item: item, showSpoiler: false}
return sitem, filtered
sitem = &StatusItem{id: newID(), item: item, showSpoiler: false, filtered: filtered, pinned: pinned}
return sitem
}
type StatusItem struct {
id uint
item *mastodon.Status
showSpoiler bool
filtered filtered
pinned bool
}
func (s *StatusItem) ID() uint {
@ -141,6 +153,14 @@ func (s *StatusItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int
return realUrls, status.Mentions, status.Tags, length
}
func (s *StatusItem) Filtered() (bool, string) {
return s.filtered.inUse, s.filtered.name
}
func (s *StatusItem) Pinned() bool {
return s.pinned
}
func NewUserItem(item *User, profile bool) Item {
return &UserItem{id: newID(), item: item, profile: profile}
}
@ -185,8 +205,16 @@ func (u *UserItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int)
return urls, []mastodon.Mention{}, []mastodon.Tag{}, len(urls)
}
func NewNotificationItem(item *mastodon.Notification, user *User, filters []*mastodon.Filter) (nitem Item, filtred bool) {
status, filtred := NewStatusItem(item.Status, filters, "notifications")
func (s *UserItem) Filtered() (bool, string) {
return false, ""
}
func (u *UserItem) Pinned() bool {
return false
}
func NewNotificationItem(item *mastodon.Notification, user *User, filters []*mastodon.Filter) (nitem Item) {
status := NewStatusItem(item.Status, filters, "notifications", false)
nitem = &NotificationItem{
id: newID(),
item: item,
@ -195,7 +223,7 @@ func NewNotificationItem(item *mastodon.Notification, user *User, filters []*mas
status: status,
}
return nitem, filtred
return nitem
}
type NotificationItem struct {
@ -240,6 +268,14 @@ func (n *NotificationItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Ta
return nil, nil, nil, 0
}
func (n *NotificationItem) Filtered() (bool, string) {
return false, ""
}
func (n *NotificationItem) Pinned() bool {
return false
}
func NewListsItem(item *mastodon.List) Item {
return &ListItem{id: newID(), item: item, showSpoiler: true}
}
@ -272,3 +308,11 @@ func (s *ListItem) Raw() interface{} {
func (s *ListItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
return nil, nil, nil, 0
}
func (s *ListItem) Filtered() (bool, string) {
return false, ""
}
func (n *ListItem) Pinned() bool {
return false
}

10
config.example.ini

@ -107,6 +107,10 @@ show-icons=true
# default=false
short-hints=false
# If you want to display the filter that filtered a toot.
# default=true
show-filter-phrase=true
# If you want to show a message in the cmdbar on how to access the help text.
# default=true
show-help=true
@ -132,7 +136,7 @@ leader-key=
leader-timeout=1000
# You set actions for the leader-key with one or more leader-action. It consists
# of two parts first the action then the shortcut. And they're seperated by a
# of two parts first the action then the shortcut. And they're separated by a
# comma.
#
# Available commands: home, direct, local, federated, compose, blocking,
@ -314,7 +318,7 @@ posts=false
#
# You can also use xrdb colors like this xrdb:color1 The program will use colors
# prefixed with an * first then look for URxvt or XTerm if it can't find any
# color prefixed with an asterik. If you don't want tut to guess the prefix you
# color prefixed with an asterisk. If you don't want tut to guess the prefix you
# can set the prefix yourself. If the xrdb color can't be found a preset color
# will be used. You'll have to set theme=none for this to work.
@ -326,7 +330,7 @@ xrdb-prefix=guess
# available on the URL below. If a theme is named "nord.ini" you just write
# theme=nord
#
# https://github.com/RasmusLindroth/tut/tree/master/themes
# https://github.com/RasmusLindroth/tut/tree/master/config/themes
#
# If you want to use your own theme set theme to none then you can create your
# own theme below

50
config/config.go

@ -85,29 +85,29 @@ type Timeline struct {
}
type General struct {
Confirmation bool
DateTodayFormat string
DateFormat string
DateRelative int
MaxWidth int
StartTimeline feed.FeedType
NotificationFeed bool
QuoteReply bool
CharLimit int
ShortHints bool
ListPlacement ListPlacement
ListSplit ListSplit
HideNotificationText bool
ListProportion int
ContentProportion int
ShowIcons bool
ShowHelp bool
RedrawUI bool
LeaderKey rune
LeaderTimeout int64
LeaderActions []LeaderAction
TimelineName bool
Timelines []Timeline
Confirmation bool
DateTodayFormat string
DateFormat string
DateRelative int
MaxWidth int
StartTimeline feed.FeedType
NotificationFeed bool
QuoteReply bool
CharLimit int
ShortHints bool
ShowFilterPhrase bool
ListPlacement ListPlacement
ListSplit ListSplit
ListProportion int
ContentProportion int
ShowIcons bool
ShowHelp bool
RedrawUI bool
LeaderKey rune
LeaderTimeout int64
LeaderActions []LeaderAction
TimelineName bool
Timelines []Timeline
}
type Style struct {
@ -576,7 +576,7 @@ func parseGeneral(cfg *ini.File) General {
general.CharLimit = cfg.Section("general").Key("char-limit").MustInt(500)
general.MaxWidth = cfg.Section("general").Key("max-width").MustInt(0)
general.ShortHints = cfg.Section("general").Key("short-hints").MustBool(false)
general.HideNotificationText = cfg.Section("general").Key("hide-notification-text").MustBool(false)
general.ShowFilterPhrase = cfg.Section("general").Key("show-filter-phrase").MustBool(true)
general.ShowIcons = cfg.Section("general").Key("show-icons").MustBool(true)
general.ShowHelp = cfg.Section("general").Key("show-help").MustBool(true)
general.RedrawUI = cfg.Section("general").Key("redraw-ui").MustBool(true)
@ -630,7 +630,7 @@ func parseGeneral(cfg *ini.File) General {
for _, l := range lactions {
parts := strings.Split(l, ",")
if len(parts) != 2 {
fmt.Printf("leader-action must consist of two parts seperated by a comma. Your value is: %s\n", strings.Join(parts, ","))
fmt.Printf("leader-action must consist of two parts separated by a comma. Your value is: %s\n", strings.Join(parts, ","))
os.Exit(1)
}
for i, p := range parts {

10
config/default_config.go

@ -109,6 +109,10 @@ show-icons=true
# default=false
short-hints=false
# If you want to display the filter that filtered a toot.
# default=true
show-filter-phrase=true
# If you want to show a message in the cmdbar on how to access the help text.
# default=true
show-help=true
@ -134,7 +138,7 @@ leader-key=
leader-timeout=1000
# You set actions for the leader-key with one or more leader-action. It consists
# of two parts first the action then the shortcut. And they're seperated by a
# of two parts first the action then the shortcut. And they're separated by a
# comma.
#
# Available commands: home, direct, local, federated, compose, blocking,
@ -316,7 +320,7 @@ posts=false
#
# You can also use xrdb colors like this xrdb:color1 The program will use colors
# prefixed with an * first then look for URxvt or XTerm if it can't find any
# color prefixed with an asterik. If you don't want tut to guess the prefix you
# color prefixed with an asterisk. If you don't want tut to guess the prefix you
# can set the prefix yourself. If the xrdb color can't be found a preset color
# will be used. You'll have to set theme=none for this to work.
@ -328,7 +332,7 @@ xrdb-prefix=guess
# available on the URL below. If a theme is named "nord.ini" you just write
# theme=nord
#
# https://github.com/RasmusLindroth/tut/tree/master/themes
# https://github.com/RasmusLindroth/tut/tree/master/config/themes
#
# If you want to use your own theme set theme to none then you can create your
# own theme below

78
feed/feed.go

@ -16,7 +16,7 @@ type apiEmptyFunc func() ([]api.Item, error)
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, int, error)
type apiThreadFunc func(status *mastodon.Status) ([]api.Item, error)
type FeedType uint
@ -64,6 +64,7 @@ const (
type Feed struct {
accountClient *api.AccountClient
feedType FeedType
sticky []api.Item
items []api.Item
itemsMux sync.RWMutex
loadingNewer *LoadingLock
@ -85,7 +86,8 @@ func (f *Feed) Type() FeedType {
func (f *Feed) List() []api.Item {
f.itemsMux.RLock()
defer f.itemsMux.RUnlock()
return f.items
r := f.sticky
return append(r, f.items...)
}
func (f *Feed) Delete(id uint) {
@ -167,6 +169,10 @@ func (f *Feed) Name() string {
return f.name
}
func (f *Feed) StickyCount() int {
return len(f.sticky)
}
func (f *Feed) singleNewerSearch(fn apiSearchFunc, search string) {
items, err := fn(search)
if err != nil {
@ -181,7 +187,7 @@ func (f *Feed) singleNewerSearch(fn apiSearchFunc, search string) {
}
func (f *Feed) singleThread(fn apiThreadFunc, status *mastodon.Status) {
items, _, err := fn(status)
items, err := fn(status)
if err != nil {
return
}
@ -322,12 +328,7 @@ func (f *Feed) normalNewerUser(fn apiIDFunc, id mastodon.ID) {
if len(items) > 0 {
item := items[0].Raw().(*mastodon.Status)
f.apiData.MinID = item.ID
newItems := []api.Item{f.items[0]}
newItems = append(newItems, items...)
if len(f.items) > 1 {
newItems = append(newItems, f.items[1:]...)
}
f.items = newItems
f.items = append(items, f.items...)
f.Updated(DekstopNotificationNone)
if f.apiData.MaxID == mastodon.ID("") {
item = items[len(items)-1].Raw().(*mastodon.Status)
@ -538,13 +539,11 @@ func (f *Feed) startStream(rec *api.Receiver, timeline string, err error) {
for e := range f.stream.Ch {
switch t := e.(type) {
case *mastodon.UpdateEvent:
s, filtered := api.NewStatusItem(t.Status, f.accountClient.Filters, timeline)
if !filtered {
f.itemsMux.Lock()
f.items = append([]api.Item{s}, f.items...)
f.Updated(DesktopNotificationPost)
f.itemsMux.Unlock()
}
s := api.NewStatusItem(t.Status, f.accountClient.Filters, timeline, false)
f.itemsMux.Lock()
f.items = append([]api.Item{s}, f.items...)
f.Updated(DesktopNotificationPost)
f.itemsMux.Unlock()
}
}
}()
@ -567,32 +566,30 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e
log.Fatalln(t.Notification.Account.Acct)
continue
}
s, filtered := api.NewNotificationItem(t.Notification,
s := api.NewNotificationItem(t.Notification,
&api.User{
Data: &t.Notification.Account,
Relation: rel[0],
}, f.accountClient.Filters)
if !filtered {
f.itemsMux.Lock()
f.items = append([]api.Item{s}, f.items...)
nft := DekstopNotificationNone
switch t.Notification.Type {
case "follow", "follow_request":
nft = DesktopNotificationFollower
case "favourite":
nft = DesktopNotificationFollower
case "reblog":
nft = DesktopNotificationBoost
case "mention":
nft = DesktopNotificationMention
case "status":
nft = DesktopNotificationPost
case "poll":
nft = DesktopNotificationPoll
}
f.Updated(nft)
f.itemsMux.Unlock()
f.itemsMux.Lock()
f.items = append([]api.Item{s}, f.items...)
nft := DekstopNotificationNone
switch t.Notification.Type {
case "follow", "follow_request":
nft = DesktopNotificationFollower
case "favourite":
nft = DesktopNotificationFollower
case "reblog":
nft = DesktopNotificationBoost
case "mention":
nft = DesktopNotificationMention
case "status":
nft = DesktopNotificationPost
case "poll":
nft = DesktopNotificationPoll
}
f.Updated(nft)
f.itemsMux.Unlock()
}
}
}()
@ -601,6 +598,7 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e
func newFeed(ac *api.AccountClient, ft FeedType) *Feed {
return &Feed{
accountClient: ac,
sticky: make([]api.Item, 0),
items: make([]api.Item, 0),
feedType: ft,
loadNewer: func() {},
@ -689,7 +687,11 @@ func NewUserSearch(ac *api.AccountClient, search string) *Feed {
func NewUserProfile(ac *api.AccountClient, user *api.User) *Feed {
feed := newFeed(ac, User)
feed.name = user.Data.Acct
feed.items = append(feed.items, api.NewUserItem(user, true))
feed.sticky = append(feed.sticky, api.NewUserItem(user, true))
pinned, err := ac.GetUserPinned(user.Data.ID)
if err == nil {
feed.sticky = append(feed.sticky, pinned...)
}
feed.loadNewer = func() { feed.normalNewerUser(feed.accountClient.GetUser, user.Data.ID) }
feed.loadOlder = func() { feed.normalOlderUser(feed.accountClient.GetUser, user.Data.ID) }

6
go.mod

@ -3,7 +3,7 @@ module github.com/RasmusLindroth/tut
go 1.17
require (
github.com/RasmusLindroth/go-mastodon v0.0.7
github.com/RasmusLindroth/go-mastodon v0.0.8
github.com/atotto/clipboard v0.1.4
github.com/gdamore/tcell/v2 v2.5.1
github.com/gen2brain/beeep v0.0.0-20220518085355-d7852edf42fc
@ -13,8 +13,8 @@ require (
github.com/pelletier/go-toml/v2 v2.0.1
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8
github.com/rivo/uniseg v0.2.0
golang.org/x/net v0.0.0-20220526153639-5463443f8c37
gopkg.in/ini.v1 v1.66.4
golang.org/x/net v0.0.0-20220531201128-c960675eff93
gopkg.in/ini.v1 v1.66.6
)
require (

12
go.sum

@ -1,5 +1,5 @@
github.com/RasmusLindroth/go-mastodon v0.0.7 h1:iGgkkvDrPHTiAyACUehLH5zragSHCUSbhcYdQEBIn48=
github.com/RasmusLindroth/go-mastodon v0.0.7/go.mod h1:4L0oyiNwq1tUoiByczzhSikxR9RiANzELtZgexxKpPM=
github.com/RasmusLindroth/go-mastodon v0.0.8 h1:t2rrbdNgS4h0JhmPNsmUOQBByDxmUPawnERGn6oR2eA=
github.com/RasmusLindroth/go-mastodon v0.0.8/go.mod h1:4L0oyiNwq1tUoiByczzhSikxR9RiANzELtZgexxKpPM=
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=
@ -49,8 +49,8 @@ github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+a
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-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 h1:lUkvobShwKsOesNfWWlCS5q7fnbG1MEliIzwu886fn8=
golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -71,7 +71,7 @@ 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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

BIN
images/preview2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

2
main.go

@ -8,7 +8,7 @@ import (
"github.com/rivo/tview"
)
const version = "1.0.10"
const version = "1.0.11"
func main() {
util.MakeDirs()

2
ui/cliview.go

@ -41,7 +41,7 @@ func CliView(version string) (newUser bool, selectedUser string) {
fmt.Print("\t\tIf two users are named the same. Use full name like tut@fosstodon.org\n\n")
fmt.Print("Configuration:\n")
fmt.Printf("\tThe config is located in XDG_CONFIG_HOME/tut/config.ini which usally equals to ~/.config/tut/config.ini.\n")
fmt.Printf("\tThe config is located in XDG_CONFIG_HOME/tut/config.ini which usually equals to ~/.config/tut/config.ini.\n")
fmt.Printf("\tThe program will generate the file the first time you run tut. The file has comments which exmplains what each configuration option does.\n\n")
fmt.Print("Contact info for issues or questions:\n")

54
ui/feed.go

@ -14,8 +14,9 @@ import (
)
type FeedList struct {
Text *tview.List
Symbol *tview.List
Text *tview.List
Symbol *tview.List
stickyCount int
}
func (fl *FeedList) InFocus(style config.Style) {
@ -136,7 +137,7 @@ func NewHomeFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -151,7 +152,7 @@ func NewFederatedFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -166,7 +167,7 @@ func NewLocalFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -181,7 +182,7 @@ func NewNotificationFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -196,7 +197,7 @@ func NewThreadFeed(tv *TutView, item api.Item) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
for i, s := range f.List() {
@ -218,7 +219,7 @@ func NewConversationsFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -237,7 +238,7 @@ func NewUserFeed(tv *TutView, item api.Item) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -252,7 +253,7 @@ func NewUserSearchFeed(tv *TutView, search string) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
for _, s := range f.List() {
@ -271,7 +272,7 @@ func NewTagFeed(tv *TutView, search string) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -285,7 +286,7 @@ func NewListsFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -300,7 +301,7 @@ func NewListFeed(tv *TutView, l *mastodon.List) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -315,7 +316,7 @@ func NewFavoritedFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
@ -330,7 +331,7 @@ func NewBookmarksFeed(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -345,7 +346,7 @@ func NewFavoritesStatus(tv *TutView, id mastodon.ID) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -360,7 +361,7 @@ func NewBoosts(tv *TutView, id mastodon.ID) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -375,7 +376,7 @@ func NewFollowers(tv *TutView, id mastodon.ID) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -390,7 +391,7 @@ func NewFollowing(tv *TutView, id mastodon.ID) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -405,7 +406,7 @@ func NewBlocking(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -420,7 +421,7 @@ func NewMuting(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -435,7 +436,7 @@ func NewFollowRequests(tv *TutView) *Feed {
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut),
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
@ -443,10 +444,11 @@ func NewFollowRequests(tv *TutView) *Feed {
return fd
}
func NewFeedList(t *Tut) *FeedList {
func NewFeedList(t *Tut, stickyCount int) *FeedList {
fl := &FeedList{
Text: NewList(t.Config),
Symbol: NewList(t.Config),
Text: NewList(t.Config),
Symbol: NewList(t.Config),
stickyCount: stickyCount,
}
return fl
}
@ -476,7 +478,7 @@ func (fl *FeedList) Prev() (loadNewer bool) {
}
fl.Text.SetCurrentItem(ni)
fl.Symbol.SetCurrentItem(ni)
return ni < 4
return ni-fl.stickyCount < 4
}
func (fl *FeedList) Clear() {

4
ui/input.go

@ -452,7 +452,7 @@ func (tv *TutView) InputStatus(event *tcell.EventKey, item api.Item, status *mas
return nil
}
if tv.tut.Config.Input.StatusUser.Match(event.Key(), event.Rune()) {
id := status.Account.ID
id := sr.Account.ID
if nAcc != nil {
id = nAcc.ID
}
@ -468,7 +468,7 @@ func (tv *TutView) InputStatus(event *tcell.EventKey, item api.Item, status *mas
return nil
}
if tv.tut.Config.Input.StatusYank.Match(event.Key(), event.Rune()) {
copyToClipboard(status.URL)
copyToClipboard(sr.URL)
return nil
}
if tv.tut.Config.Input.StatusToggleSpoiler.Match(event.Key(), event.Rune()) {

11
ui/item.go

@ -20,13 +20,20 @@ func DrawListItem(cfg *config.Config, item api.Item) (string, string) {
symbol := ""
status := s
if s.Reblog != nil {
status = s
status = s.Reblog
}
if status.RepliesCount > 0 {
symbol = " ⤶ "
}
if item.Pinned() {
symbol = " ! "
}
acc := strings.TrimSpace(s.Account.Acct)
if s.Reblog != nil && cfg.General.ShowIcons {
acc = fmt.Sprintf("♺ %s", acc)
}
d := OutputDate(cfg, s.CreatedAt.Local())
return fmt.Sprintf("%s %s", d, strings.TrimSpace(s.Account.Acct)), symbol
return fmt.Sprintf("%s %s", d, acc), symbol
case api.UserType:
a := item.Raw().(*api.User)
return strings.TrimSpace(a.Data.Acct), ""

18
ui/item_status.go

@ -71,6 +71,24 @@ type DisplayTootData struct {
}
func drawStatus(tut *Tut, item api.Item, status *mastodon.Status, main *tview.TextView, controls *tview.TextView, additional string) {
filtered, phrase := item.Filtered()
if filtered {
var output string
if tut.Config.General.ShowFilterPhrase {
output = fmt.Sprintf("Filtered by phrase: %s", tview.Escape(phrase))
} else {
output = "Filtered."
}
if main != nil {
if additional != "" {
additional = fmt.Sprintf("%s\n\n", config.SublteText(tut.Config, additional))
}
main.SetText(additional + output)
}
controls.SetText("")
return
}
showSensitive := item.ShowSpoiler()
var strippedContent string

Loading…
Cancel
Save