Browse Source

1.0.20 (#183)

* update version

* only load threads once

* don't crash tut on error from openInTerminal(...)

* add/remove users to list and follow/unfollow tags
pull/184/head 1.0.20
Rasmus Lindroth 3 years ago committed by GitHub
parent
commit
2c8ba2e6b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      README.md
  2. 35
      api/feed.go
  3. 35
      api/tags.go
  4. 5
      api/types.go
  5. 8
      api/user.go
  6. 12
      config.example.ini
  7. 13
      config/config.go
  8. 12
      config/default_config.go
  9. 14
      config/help.tmpl
  10. 92
      feed/feed.go
  11. 2
      go.mod
  12. 2
      go.sum
  13. 2
      main.go
  14. 22
      ui/cmdbar.go
  15. 16
      ui/commands.go
  16. 30
      ui/feed.go
  17. 77
      ui/input.go
  18. 17
      ui/item.go
  19. 14
      ui/item_list.go
  20. 4
      ui/item_notification.go
  21. 11
      ui/item_user.go
  22. 10
      ui/open.go
  23. 6
      ui/timeline.go

4
README.md

@ -45,6 +45,9 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t
* `:compose` compose a new toot
* `:favorited` lists toots you've favorited
* `:favorites` lists users that favorited the toot
* `:follow-tag` followed by the hashtag to follow e.g. `:follow-tag tut`
* `:followers` list of people the account are following. It only works on profiles.
* `:following` list of people following the account. It only works on profiles.
* `:h` `:help` view help
* `:history` show edits of a toot
* `:lists` show a list of your lists
@ -58,6 +61,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t
* `:requests` see following requests
* `:saved` alias for bookmarks
* `:tag` followed by the hashtag e.g. `:tag linux`
* `:unfollow-tag` followed by the hashtag to unfollow e.g. `:unfollow-tag tut`
* `:user` followed by a username e.g. `:user rasmus` to narrow a search include
* `:window` switch window by index (zero indexed) e.g. `:window 0` for the first window.

35
api/feed.go

@ -21,7 +21,7 @@ func (ac *AccountClient) getStatusSimilar(fn func() ([]*mastodon.Status, error),
return items, nil
}
func (ac *AccountClient) getUserSimilar(fn func() ([]*mastodon.Account, error)) ([]Item, error) {
func (ac *AccountClient) getUserSimilar(fn func() ([]*mastodon.Account, error), data interface{}) ([]Item, error) {
var items []Item
users, err := fn()
if err != nil {
@ -39,8 +39,9 @@ func (ac *AccountClient) getUserSimilar(fn func() ([]*mastodon.Account, error))
for _, r := range rel {
if u.ID == r.ID {
items = append(items, NewUserItem(&User{
Data: u,
Relation: r,
Data: u,
Relation: r,
AdditionalData: data,
}, false))
break
}
@ -185,49 +186,49 @@ func (ac *AccountClient) GetBoostsStatus(pg *mastodon.Pagination, id mastodon.ID
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetRebloggedBy(context.Background(), id, pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetFavoritesStatus(pg *mastodon.Pagination, id mastodon.ID) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetFavouritedBy(context.Background(), id, pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetFollowers(pg *mastodon.Pagination, id mastodon.ID) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetAccountFollowers(context.Background(), id, pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetFollowing(pg *mastodon.Pagination, id mastodon.ID) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetAccountFollowing(context.Background(), id, pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetBlocking(pg *mastodon.Pagination) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetBlocks(context.Background(), pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetMuting(pg *mastodon.Pagination) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetMutes(context.Background(), pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetFollowRequests(pg *mastodon.Pagination) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetFollowRequests(context.Background(), pg)
}
return ac.getUserSimilar(fn)
return ac.getUserSimilar(fn, nil)
}
func (ac *AccountClient) GetUser(pg *mastodon.Pagination, id mastodon.ID) ([]Item, error) {
@ -281,6 +282,20 @@ func (ac *AccountClient) GetListStatuses(pg *mastodon.Pagination, id mastodon.ID
return items, nil
}
func (ac *AccountClient) GetFollowingForList(pg *mastodon.Pagination, id mastodon.ID, data interface{}) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetAccountFollowing(context.Background(), id, pg)
}
return ac.getUserSimilar(fn, data)
}
func (ac *AccountClient) GetListUsers(pg *mastodon.Pagination, id mastodon.ID, data interface{}) ([]Item, error) {
fn := func() ([]*mastodon.Account, error) {
return ac.Client.GetListAccounts(context.Background(), id)
}
return ac.getUserSimilar(fn, data)
}
func (ac *AccountClient) GetTag(pg *mastodon.Pagination, search string) ([]Item, error) {
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetTimelineHashtag(context.Background(), search, false, pg)

35
api/tags.go

@ -0,0 +1,35 @@
package api
import (
"context"
"errors"
)
func (ac *AccountClient) FollowTag(tag string) error {
t, err := ac.Client.TagFollow(context.Background(), tag)
if err != nil {
return err
}
if t.Following == nil {
return errors.New("following is set to nil")
}
if t.Following == false {
return errors.New("following is still set to false")
}
return nil
}
func (ac *AccountClient) UnfollowTag(tag string) error {
t, err := ac.Client.TagUnfollow(context.Background(), tag)
if err != nil {
return err
}
if t.Following == nil {
return errors.New("following is set to nil")
}
if t.Following == true {
return errors.New("following is still set to true")
}
return nil
}

5
api/types.go

@ -15,6 +15,7 @@ type AccountClient struct {
}
type User struct {
Data *mastodon.Account
Relation *mastodon.Relationship
Data *mastodon.Account
Relation *mastodon.Relationship
AdditionalData interface{}
}

8
api/user.go

@ -87,3 +87,11 @@ func (ac *AccountClient) SavePreferences(p *mastodon.Profile) error {
}
return err
}
func (ac *AccountClient) AddUserToList(u *mastodon.Account, l *mastodon.List) error {
return ac.Client.AddToList(context.Background(), l.ID, u.ID)
}
func (ac *AccountClient) DeleteUserFromList(u *mastodon.Account, l *mastodon.List) error {
return ac.Client.RemoveFromList(context.Background(), l.ID, u.ID)
}

12
config.example.ini

@ -642,6 +642,18 @@ user-yank="[Y]ank",'y','Y'
# default="[O]pen",'o','O'
list-open-feed="[O]pen",'o','O'
# List all users in a list
# default="[U]sers",'u','U'
list-user-list="[U]sers",'u','U'
# Add user to list
# default="[A]dd",'a','A'
list-user-add="[A]dd",'a','A'
# Delete user from list
# default="[D]elete",'d','D'
list-user-delete="[D]elete",'d','D'
# Open URL
# default="[O]pen",'o','O'
link-open="[O]pen",'o','O'

13
config/config.go

@ -380,7 +380,10 @@ type Input struct {
UserViewFocus Key
UserYank Key
ListOpenFeed Key
ListOpenFeed Key
ListUserList Key
ListUserAdd Key
ListUserDelete Key
LinkOpen Key
LinkYank Key
@ -1265,7 +1268,10 @@ func parseInput(cfg *ini.File) Input {
UserViewFocus: inputStrOrErr([]string{"\"[V]iew\"", "'v'", "'V'"}, false),
UserYank: inputStrOrErr([]string{"\"[Y]ank\"", "'y'", "'Y'"}, false),
ListOpenFeed: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false),
ListOpenFeed: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false),
ListUserList: inputStrOrErr([]string{"\"[U]sers\"", "'u'", "'U'"}, false),
ListUserAdd: inputStrOrErr([]string{"\"[A]dd\"", "'a'", "'A'"}, false),
ListUserDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false),
LinkOpen: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false),
LinkYank: inputStrOrErr([]string{"\"[Y]ank\"", "'y'", "'Y'"}, false),
@ -1340,6 +1346,9 @@ func parseInput(cfg *ini.File) Input {
ic.UserYank = inputOrErr(cfg, "user-yank", false, ic.UserYank)
ic.ListOpenFeed = inputOrErr(cfg, "list-open-feed", false, ic.ListOpenFeed)
ic.ListUserList = inputOrErr(cfg, "list-user-list", false, ic.ListUserList)
ic.ListUserAdd = inputOrErr(cfg, "list-user-add", false, ic.ListUserAdd)
ic.ListUserDelete = inputOrErr(cfg, "list-user-delete", false, ic.ListUserDelete)
ic.LinkOpen = inputOrErr(cfg, "link-open", false, ic.LinkOpen)
ic.LinkYank = inputOrErr(cfg, "link-yank", false, ic.LinkYank)

12
config/default_config.go

@ -644,6 +644,18 @@ user-yank="[Y]ank",'y','Y'
# default="[O]pen",'o','O'
list-open-feed="[O]pen",'o','O'
# List all users in a list
# default="[U]sers",'u','U'
list-user-list="[U]sers",'u','U'
# Add user to list
# default="[A]dd",'a','A'
list-user-add="[A]dd",'a','A'
# Delete user from list
# default="[D]elete",'d','D'
list-user-delete="[D]elete",'d','D'
# Open URL
# default="[O]pen",'o','O'
link-open="[O]pen",'o','O'

14
config/help.tmpl

@ -57,6 +57,15 @@ Here's a list of supported commands.
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:favorites{{ Flags "-" }}{{ Color .Style.Text }}
Lists users that favorited the toot
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:follow-tag{{ Flags "-" }}{{ Color .Style.Text }}
Followed by the hashtag to follow e.g. :follow-tag tut
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:followers{{ Flags "-" }}{{ Color .Style.Text }}
List of people the account are following. It only works on profiles.
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:following{{ Flags "-" }}{{ Color .Style.Text }}
List of people following the account. It only works on profiles.
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:h{{ Flags "-" }}{{ Color .Style.Text }} or
{{- Color .Style.TextSpecial2 }}{{ Flags "b" }} :help{{ Flags "-" }}{{ Color .Style.Text }}
View this help message
@ -92,7 +101,10 @@ Here's a list of supported commands.
Alias for :bookmarks
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:tag{{ Flags "-" }}{{ Color .Style.Text }} tagname
See toots for a tag. E.g. :tag linux
See toots for a tag e.g. :tag linux
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:unfollow-tag{{ Flags "-" }}{{ Color .Style.Text }}
Followed by the hashtag to unfollow e.g. :unfollow-tag tut
{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:user{{ Flags "-" }}{{ Color .Style.Text }} username
Go to profile for <username>. E.g. :user rasmus

92
feed/feed.go

@ -14,6 +14,7 @@ import (
type apiFunc func(pg *mastodon.Pagination) ([]api.Item, error)
type apiEmptyFunc func() ([]api.Item, error)
type apiIDFunc func(pg *mastodon.Pagination, id mastodon.ID) ([]api.Item, error)
type apiIDFuncData func(pg *mastodon.Pagination, id mastodon.ID, data interface{}) ([]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)
@ -44,6 +45,8 @@ const (
UserList
Lists
List
ListUsersIn
ListUsersAdd
)
type LoadingLock struct {
@ -556,6 +559,57 @@ func (f *Feed) linkOlderID(fn apiIDFunc, id mastodon.ID) {
f.itemsMux.Unlock()
}
func (f *Feed) linkNewerIDdata(fn apiIDFuncData, id mastodon.ID, data interface{}) {
f.apiDataMux.Lock()
pg := &mastodon.Pagination{}
pg.MinID = f.apiData.MinID
maxTmp := f.apiData.MaxID
items, err := fn(pg, id, data)
if err != nil {
f.apiDataMux.Unlock()
return
}
f.apiData.MinID = pg.MinID
if pg.MaxID == "" {
f.apiData.MaxID = maxTmp
} else {
f.apiData.MaxID = pg.MaxID
}
f.apiDataMux.Unlock()
f.itemsMux.Lock()
if len(items) > 0 {
f.items = append(items, f.items...)
f.Updated(DeskstopNotificationNone)
}
f.itemsMux.Unlock()
}
func (f *Feed) linkOlderIDdata(fn apiIDFuncData, id mastodon.ID, data interface{}) {
f.apiDataMux.Lock()
pg := &mastodon.Pagination{}
pg.MaxID = f.apiData.MaxID
if pg.MaxID == "" {
f.apiDataMux.Unlock()
return
}
items, err := fn(pg, id, data)
if err != nil {
f.apiDataMux.Unlock()
return
}
f.apiData.MaxID = pg.MaxID
f.apiDataMux.Unlock()
f.itemsMux.Lock()
if len(items) > 0 {
f.items = append(f.items, items...)
f.Updated(DeskstopNotificationNone)
}
f.itemsMux.Unlock()
}
func (f *Feed) startStream(rec *api.Receiver, timeline string, err error) {
if err != nil {
log.Fatalln("Couldn't open stream")
@ -731,7 +785,13 @@ func NewUserProfile(ac *api.AccountClient, user *api.User) *Feed {
func NewThread(ac *api.AccountClient, status *mastodon.Status) *Feed {
feed := newFeed(ac, Thread)
feed.loadNewer = func() { feed.singleThread(feed.accountClient.GetThread, status) }
once := true
feed.loadNewer = func() {
if once {
feed.singleThread(feed.accountClient.GetThread, status)
once = false
}
}
return feed
}
@ -783,6 +843,36 @@ func NewList(ac *api.AccountClient, list *mastodon.List) *Feed {
return feed
}
func NewUsersInList(ac *api.AccountClient, list *mastodon.List) *Feed {
feed := newFeed(ac, ListUsersIn)
feed.name = list.Title
once := true
feed.loadNewer = func() {
if once {
feed.linkNewerIDdata(feed.accountClient.GetListUsers, list.ID, list)
}
once = false
}
feed.loadOlder = func() { feed.linkOlderIDdata(feed.accountClient.GetListUsers, list.ID, list) }
return feed
}
func NewUsersAddList(ac *api.AccountClient, list *mastodon.List) *Feed {
feed := newFeed(ac, ListUsersAdd)
feed.name = list.Title
once := true
feed.loadNewer = func() {
if once {
feed.linkNewerIDdata(feed.accountClient.GetFollowingForList, ac.Me.ID, list)
}
once = false
}
feed.loadOlder = func() { feed.linkOlderIDdata(feed.accountClient.GetFollowingForList, ac.Me.ID, list) }
return feed
}
func NewFavoritesStatus(ac *api.AccountClient, id mastodon.ID) *Feed {
feed := newFeed(ac, Favorites)
once := true

2
go.mod

@ -3,7 +3,7 @@ module github.com/RasmusLindroth/tut
go 1.18
require (
github.com/RasmusLindroth/go-mastodon v0.0.10
github.com/RasmusLindroth/go-mastodon v0.0.11
github.com/atotto/clipboard v0.1.4
github.com/gdamore/tcell/v2 v2.5.3
github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6

2
go.sum

@ -1,5 +1,7 @@
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/RasmusLindroth/go-mastodon v0.0.11 h1:Qcad+urrDVrboo13ayoHG3DcwsGK/07qR6IfOPPMilY=
github.com/RasmusLindroth/go-mastodon v0.0.11/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=

2
main.go

@ -8,7 +8,7 @@ import (
"github.com/rivo/tview"
)
const version = "1.0.19"
const version = "1.0.20"
func main() {
util.SetTerminalTitle("tut")

22
ui/cmdbar.go

@ -198,6 +198,26 @@ func (c *CmdBar) DoneFunc(key tcell.Key) {
NewUserSearchFeed(c.tutView, user),
)
c.Back()
case ":follow-tag":
if len(parts) < 2 {
break
}
tag := strings.TrimSpace(parts[1])
if len(tag) == 0 {
break
}
c.tutView.TagFollowCommand(parts[1])
c.Back()
case ":unfollow-tag":
if len(parts) < 2 {
break
}
tag := strings.TrimSpace(parts[1])
if len(tag) == 0 {
break
}
c.tutView.TagUnfollowCommand(parts[1])
c.Back()
case ":lists":
c.tutView.ListsCommand()
c.Back()
@ -211,7 +231,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,:history,: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,:follow-tag,:followers,:following,:help,:h,:history,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:requests,:saved,:tag,:timeline,:tl,:unfollow-tag,:user,:window,:quit,:q", ",")
if curr == "" {
return entries
}

16
ui/commands.go

@ -86,6 +86,22 @@ func (tv *TutView) TagCommand(tag string) {
)
}
func (tv *TutView) TagFollowCommand(tag string) {
err := tv.tut.Client.FollowTag(tag)
if err != nil {
tv.ShowError(fmt.Sprintf("Couldn't follow tag. Error: %v\n", err))
return
}
}
func (tv *TutView) TagUnfollowCommand(tag string) {
err := tv.tut.Client.UnfollowTag(tag)
if err != nil {
tv.ShowError(fmt.Sprintf("Couldn't unfollow tag. Error: %v\n", err))
return
}
}
func (tv *TutView) WindowCommand(index string) {
i, err := strconv.Atoi(index)
if err != nil {

30
ui/feed.go

@ -336,6 +336,36 @@ func NewListFeed(tv *TutView, l *mastodon.List) *Feed {
return fd
}
func NewUsersInListFeed(tv *TutView, l *mastodon.List) *Feed {
f := feed.NewUsersInList(tv.tut.Client, l)
f.LoadNewer()
fd := &Feed{
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
return fd
}
func NewUsersAddListFeed(tv *TutView, l *mastodon.List) *Feed {
f := feed.NewUsersAddList(tv.tut.Client, l)
f.LoadNewer()
fd := &Feed{
tutView: tv,
Data: f,
ListIndex: 0,
List: NewFeedList(tv.tut, f.StickyCount()),
Content: NewFeedContent(tv.tut),
}
go fd.update()
return fd
}
func NewFavoritedFeed(tv *TutView) *Feed {
f := feed.NewFavorites(tv.tut.Client)
f.LoadNewer()

77
ui/input.go

@ -304,16 +304,21 @@ func (tv *TutView) InputItem(event *tcell.EventKey) *tcell.EventKey {
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)
} else {
return tv.InputUser(event, item.Raw().(*api.User), false)
switch ft {
case feed.FollowRequests:
return tv.InputUser(event, item.Raw().(*api.User), InputUserFollowRequest)
case feed.ListUsersAdd:
return tv.InputUser(event, item.Raw().(*api.User), InputUserListAdd)
case feed.ListUsersIn:
return tv.InputUser(event, item.Raw().(*api.User), InputUserListDelete)
default:
return tv.InputUser(event, item.Raw().(*api.User), InputUserNormal)
}
case api.NotificationType:
nd := item.Raw().(*api.NotificationData)
switch nd.Item.Type {
case "follow":
return tv.InputUser(event, nd.User.Raw().(*api.User), false)
return tv.InputUser(event, nd.User.Raw().(*api.User), InputUserNormal)
case "favourite":
user := nd.User.Raw().(*api.User)
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), user.Data)
@ -329,7 +334,7 @@ func (tv *TutView) InputItem(event *tcell.EventKey) *tcell.EventKey {
case "poll":
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil)
case "follow_request":
return tv.InputUser(event, nd.User.Raw().(*api.User), true)
return tv.InputUser(event, nd.User.Raw().(*api.User), InputUserFollowRequest)
}
case api.ListsType:
ld := item.Raw().(*mastodon.List)
@ -548,12 +553,60 @@ func (tv *TutView) InputStatusHistory(event *tcell.EventKey, item api.Item, sr *
return event
}
func (tv *TutView) InputUser(event *tcell.EventKey, user *api.User, fr bool) *tcell.EventKey {
type InputUserType uint
const (
InputUserNormal = iota
InputUserFollowRequest
InputUserListAdd
InputUserListDelete
)
func (tv *TutView) InputUser(event *tcell.EventKey, user *api.User, ut InputUserType) *tcell.EventKey {
blocking := user.Relation.Blocking
muting := user.Relation.Muting
following := user.Relation.Following
if tv.tut.Config.Input.UserFollowRequestDecide.Match(event.Key(), event.Rune()) {
if ut == InputUserListAdd {
if tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.ListUserAdd.Match(event.Key(), event.Rune()) {
ad := user.AdditionalData
switch ad.(type) {
case *mastodon.List:
l := user.AdditionalData.(*mastodon.List)
err := tv.tut.Client.AddUserToList(user.Data, l)
if err != nil {
tv.ShowError(fmt.Sprintf("Couldn't add user to list. Error: %v", err))
}
return nil
default:
return event
}
}
return event
}
if ut == InputUserListDelete {
if tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.ListUserDelete.Match(event.Key(), event.Rune()) {
ad := user.AdditionalData
switch ad.(type) {
case *mastodon.List:
l := user.AdditionalData.(*mastodon.List)
err := tv.tut.Client.DeleteUserFromList(user.Data, l)
if err != nil {
tv.ShowError(fmt.Sprintf("Couldn't remove user from list. Error: %v", err))
}
return nil
default:
return event
}
}
return event
}
if ut == InputUserFollowRequest && tv.tut.Config.Input.UserFollowRequestDecide.Match(event.Key(), event.Rune()) {
tv.ModalView.RunDecide("Do you want accept the follow request?",
func() {
err := tv.tut.Client.FollowRequestAccept(user.Data)
@ -671,6 +724,14 @@ func (tv *TutView) InputList(event *tcell.EventKey, list *mastodon.List) *tcell.
tv.Timeline.AddFeed(NewListFeed(tv, list))
return nil
}
if tv.tut.Config.Input.ListUserList.Match(event.Key(), event.Rune()) {
tv.Timeline.AddFeed(NewUsersInListFeed(tv, list))
return nil
}
if tv.tut.Config.Input.ListUserAdd.Match(event.Key(), event.Rune()) {
tv.Timeline.AddFeed(NewUsersAddListFeed(tv, list))
return nil
}
return event
}

17
ui/item.go

@ -91,10 +91,15 @@ func DrawItem(tv *TutView, item api.Item, main *tview.TextView, controls *tview.
}
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)
} else {
drawUser(tv, item.Raw().(*api.User), main, controls, "", false)
switch ft {
case feed.FollowRequests:
drawUser(tv, item.Raw().(*api.User), main, controls, "", InputUserFollowRequest)
case feed.ListUsersAdd:
drawUser(tv, item.Raw().(*api.User), main, controls, "", InputUserListAdd)
case feed.ListUsersIn:
drawUser(tv, item.Raw().(*api.User), main, controls, "", InputUserListDelete)
default:
drawUser(tv, item.Raw().(*api.User), main, controls, "", InputUserFollowRequest)
}
case api.NotificationType:
drawNotification(tv, item, item.Raw().(*api.NotificationData), main, controls)
@ -122,9 +127,9 @@ func DrawItemControls(tv *TutView, item api.Item, controls *tview.Flex, ft feed.
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)
drawUser(tv, item.Raw().(*api.User), nil, controls, "", InputUserFollowRequest)
} else {
drawUser(tv, item.Raw().(*api.User), nil, controls, "", false)
drawUser(tv, item.Raw().(*api.User), nil, controls, "", InputUserNormal)
}
case api.NotificationType:
drawNotification(tv, item, item.Raw().(*api.NotificationData), nil, controls)

14
ui/item_list.go

@ -11,9 +11,19 @@ type List struct {
}
func drawList(tv *TutView, data *mastodon.List, main *tview.TextView, controls *tview.Flex) {
btn := NewControl(tv.tut.Config, tv.tut.Config.Input.ListOpenFeed, true)
controls.Clear()
controls.AddItem(NewControlButton(tv, btn), btn.Len, 0, false)
var items []Control
items = append(items, NewControl(tv.tut.Config, tv.tut.Config.Input.ListOpenFeed, true))
items = append(items, NewControl(tv.tut.Config, tv.tut.Config.Input.ListUserList, true))
items = append(items, NewControl(tv.tut.Config, tv.tut.Config.Input.ListUserAdd, true))
controls.Clear()
for i, item := range items {
if i < len(items)-1 {
controls.AddItem(NewControlButton(tv, item), item.Len+1, 0, false)
} else {
controls.AddItem(NewControlButton(tv, item), item.Len, 0, false)
}
}
main.SetText(fmt.Sprintf("List %s", tview.Escape(data.Title)))
}

4
ui/item_notification.go

@ -13,7 +13,7 @@ func drawNotification(tv *TutView, item api.Item, notification *api.Notification
switch notification.Item.Type {
case "follow":
drawUser(tv, notification.User.Raw().(*api.User), main, controls,
fmt.Sprintf("%s started following you", util.FormatUsername(notification.Item.Account)), false,
fmt.Sprintf("%s started following you", util.FormatUsername(notification.Item.Account)), InputUserNormal,
)
case "favourite":
drawStatus(tv, notification.Status, notification.Item.Status, main, controls, false,
@ -42,7 +42,7 @@ func drawNotification(tv *TutView, item api.Item, notification *api.Notification
case "follow_request":
drawUser(tv, notification.User.Raw().(*api.User), main, controls,
fmt.Sprintf("%s wants to follow you.", util.FormatUsername(notification.Item.Account)),
true,
InputUserFollowRequest,
)
default:
controls.Clear()

11
ui/item_user.go

@ -43,7 +43,7 @@ type DisplayUserData struct {
Style config.Style
}
func drawUser(tv *TutView, data *api.User, main *tview.TextView, controls *tview.Flex, additional string, fr bool) {
func drawUser(tv *TutView, data *api.User, main *tview.TextView, controls *tview.Flex, additional string, ut InputUserType) {
user := data.Data
relation := data.Relation
showUserControl := true
@ -79,7 +79,7 @@ func drawUser(tv *TutView, data *api.User, main *tview.TextView, controls *tview
u.Fields = fields
var controlItems []Control
if fr {
if ut == InputUserFollowRequest {
controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserFollowRequestDecide, false))
}
if tv.tut.Client.Me.ID != user.ID {
@ -108,6 +108,13 @@ func drawUser(tv *TutView, data *api.User, main *tview.TextView, controls *tview
controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserAvatar, true))
controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserYank, true))
// Clear controls and only have add and delete for lists.
if ut == InputUserListAdd {
controlItems = []Control{NewControl(tv.tut.Config, tv.tut.Config.Input.ListUserAdd, true)}
} else if ut == InputUserListDelete {
controlItems = []Control{NewControl(tv.tut.Config, tv.tut.Config.Input.ListUserDelete, true)}
}
controls.Clear()
for i, item := range controlItems {
if i < len(controlItems)-1 {

10
ui/open.go

@ -1,7 +1,7 @@
package ui
import (
"log"
"fmt"
"os"
"os/exec"
"strings"
@ -36,11 +36,11 @@ func openInTerminal(tv *TutView, command string, args ...string) error {
var err error
tv.tut.App.Suspend(func() {
err = cmd.Run()
if err != nil {
log.Fatalln(err)
}
})
return err
if err != nil {
tv.ShowError(fmt.Sprintf("Eroror while opening: %v", err))
}
return nil
}
func openCustom(tv *TutView, program string, args []string, terminal bool, url string) {

6
ui/timeline.go

@ -183,8 +183,10 @@ func (tl *Timeline) GetTitle() string {
ct = "follow requests"
case feed.Blocking:
ct = "blocking"
case feed.Muting:
ct = "muting"
case feed.ListUsersAdd:
ct = fmt.Sprintf("Add users to %s", name)
case feed.ListUsersIn:
ct = fmt.Sprintf("Delete users from %s", name)
}
return fmt.Sprintf("%s (%d/%d)", ct, index+1, total)
}

Loading…
Cancel
Save