Browse Source

1.0.8 (#148)

* add leader window

* bump version

* add :window cmd

* update packages

* simplify code

* add support for polls
pull/149/head
Rasmus Lindroth 4 years ago committed by GitHub
parent
commit
10c83edf2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 129
      api/feed.go
  3. 25
      config/config.go
  4. 31
      config/default_config.go
  5. 239
      feed/feed.go
  6. 2
      go.mod
  7. 4
      go.sum
  8. 2
      main.go
  9. 12
      ui/cmdbar.go
  10. 12
      ui/commands.go
  11. 102
      ui/composeview.go
  12. 54
      ui/input.go
  13. 245
      ui/pollview.go
  14. 3
      ui/statusbar.go
  15. 3
      ui/tutview.go
  16. 8
      ui/view.go
  17. 1
      ui/voteview.go

2
README.md

@ -43,7 +43,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t
* `:saved` alias for bookmarks
* `:tag` followed by the hashtag e.g. `:tag linux`
* `:user` followed by a username e.g. `:user rasmus` to narrow a search include
the instance like this `:user rasmus@mastodon.acc.sunet.se`.
* `:window` switch window by index (zero indexed) e.g. `:window 0` for the first window.
Keys without description in tut
* `c` = Compose a new toot

129
api/feed.go

@ -8,14 +8,14 @@ import (
type TimelineType uint
func (ac *AccountClient) GetTimeline(pg *mastodon.Pagination) ([]Item, error) {
func (ac *AccountClient) getStatusSimilar(fn func() ([]*mastodon.Status, error), filter string) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetTimelineHome(context.Background(), pg)
statuses, err := fn()
if err != nil {
return items, err
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "home")
item, filtered := NewStatusItem(s, ac.Filters, filter)
if !filtered {
items = append(items, item)
}
@ -23,36 +23,55 @@ func (ac *AccountClient) GetTimeline(pg *mastodon.Pagination) ([]Item, error) {
return items, nil
}
func (ac *AccountClient) GetTimelineFederated(pg *mastodon.Pagination) ([]Item, error) {
func (ac *AccountClient) getUserSimilar(fn func() ([]*mastodon.Account, error)) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetTimelinePublic(context.Background(), false, pg)
users, err := fn()
if err != nil {
return items, err
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "public")
if !filtered {
items = append(items, item)
}
ids := []string{}
for _, u := range users {
ids = append(ids, string(u.ID))
}
return items, nil
}
func (ac *AccountClient) GetTimelineLocal(pg *mastodon.Pagination) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetTimelinePublic(context.Background(), true, pg)
rel, err := ac.Client.GetAccountRelationships(context.Background(), ids)
if err != nil {
return items, err
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "public")
if !filtered {
items = append(items, item)
for _, u := range users {
for _, r := range rel {
if u.ID == r.ID {
items = append(items, NewUserItem(&User{
Data: u,
Relation: r,
}, false))
break
}
}
}
return items, nil
}
func (ac *AccountClient) GetTimeline(pg *mastodon.Pagination) ([]Item, error) {
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetTimelineHome(context.Background(), pg)
}
return ac.getStatusSimilar(fn, "home")
}
func (ac *AccountClient) GetTimelineFederated(pg *mastodon.Pagination) ([]Item, error) {
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetTimelinePublic(context.Background(), false, pg)
}
return ac.getStatusSimilar(fn, "public")
}
func (ac *AccountClient) GetTimelineLocal(pg *mastodon.Pagination) ([]Item, error) {
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetTimelinePublic(context.Background(), true, pg)
}
return ac.getStatusSimilar(fn, "public")
}
func (ac *AccountClient) GetNotifications(pg *mastodon.Pagination) ([]Item, error) {
var items []Item
notifications, err := ac.Client.GetNotifications(context.Background(), pg)
@ -109,33 +128,17 @@ func (ac *AccountClient) GetThread(status *mastodon.Status) ([]Item, int, error)
}
func (ac *AccountClient) GetFavorites(pg *mastodon.Pagination) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetFavourites(context.Background(), pg)
if err != nil {
return items, err
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetFavourites(context.Background(), pg)
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "home")
if !filtered {
items = append(items, item)
}
}
return items, nil
return ac.getStatusSimilar(fn, "home")
}
func (ac *AccountClient) GetBookmarks(pg *mastodon.Pagination) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetBookmarks(context.Background(), pg)
if err != nil {
return items, err
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetBookmarks(context.Background(), pg)
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "home")
if !filtered {
items = append(items, item)
}
}
return items, nil
return ac.getStatusSimilar(fn, "home")
}
func (ac *AccountClient) GetConversations(pg *mastodon.Pagination) ([]Item, error) {
@ -230,34 +233,6 @@ func (ac *AccountClient) GetFollowRequests(pg *mastodon.Pagination) ([]Item, err
return ac.getUserSimilar(fn)
}
func (ac *AccountClient) getUserSimilar(fn func() ([]*mastodon.Account, error)) ([]Item, error) {
var items []Item
users, err := fn()
if err != nil {
return items, err
}
ids := []string{}
for _, u := range users {
ids = append(ids, string(u.ID))
}
rel, err := ac.Client.GetAccountRelationships(context.Background(), ids)
if err != nil {
return items, err
}
for _, u := range users {
for _, r := range rel {
if u.ID == r.ID {
items = append(items, NewUserItem(&User{
Data: u,
Relation: r,
}, false))
break
}
}
}
return items, nil
}
func (ac *AccountClient) GetUser(pg *mastodon.Pagination, id mastodon.ID) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetAccountStatuses(context.Background(), id, pg)
@ -301,16 +276,8 @@ func (ac *AccountClient) GetListStatuses(pg *mastodon.Pagination, id mastodon.ID
}
func (ac *AccountClient) GetTag(pg *mastodon.Pagination, search string) ([]Item, error) {
var items []Item
statuses, err := ac.Client.GetTimelineHashtag(context.Background(), search, false, pg)
if err != nil {
return items, err
fn := func() ([]*mastodon.Status, error) {
return ac.Client.GetTimelineHashtag(context.Background(), search, false, pg)
}
for _, s := range statuses {
item, filtered := NewStatusItem(s, ac.Filters, "public")
if !filtered {
items = append(items, item)
}
}
return items, nil
return ac.getStatusSimilar(fn, "public")
}

25
config/config.go

@ -70,6 +70,7 @@ const (
LeaderLists
LeaderTag
LeaderUser
LeaderWindow
)
type Timeline struct {
@ -360,6 +361,7 @@ type Input struct {
ComposePost Key
ComposeToggleContentWarning Key
ComposeVisibility Key
ComposePoll Key
MediaDelete Key
MediaEditDesc Key
@ -367,6 +369,12 @@ type Input struct {
VoteVote Key
VoteSelect Key
PollAdd Key
PollEdit Key
PollDelete Key
PollMultiToggle Key
PollExpiration Key
}
func parseColor(input string, def string, xrdb map[string]string) tcell.Color {
@ -661,6 +669,9 @@ func parseGeneral(cfg *ini.File) General {
case "tag":
la.Command = LeaderTag
la.Subaction = subaction
case "window":
la.Command = LeaderWindow
la.Subaction = subaction
default:
fmt.Printf("leader-action %s is invalid\n", parts[0])
os.Exit(1)
@ -1025,6 +1036,7 @@ func parseInput(cfg *ini.File) Input {
ComposePost: inputStrOrErr([]string{"\"[P]ost\"", "'p'", "'P'"}, false),
ComposeToggleContentWarning: inputStrOrErr([]string{"\"[T]oggle CW\"", "'t'", "'T'"}, false),
ComposeVisibility: inputStrOrErr([]string{"\"[V]isibility\"", "'v'", "'V'"}, false),
ComposePoll: inputStrOrErr([]string{"\"P[O]ll\"", "'o'", "'O'"}, false),
MediaDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false),
MediaEditDesc: inputStrOrErr([]string{"\"[E]dit desc\"", "'e'", "'E'"}, false),
@ -1032,6 +1044,12 @@ func parseInput(cfg *ini.File) Input {
VoteVote: inputStrOrErr([]string{"\"[V]ote\"", "'v'", "'V'"}, false),
VoteSelect: inputStrOrErr([]string{"\"[Enter] to select\"", "' '", "\"Enter\""}, false),
PollAdd: inputStrOrErr([]string{"\"[A]dd\"", "'a'", "'A'"}, false),
PollEdit: inputStrOrErr([]string{"\"[E]dit\"", "'e'", "'E'"}, false),
PollDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false),
PollMultiToggle: inputStrOrErr([]string{"\"Toggle [M]ultiple\"", "'m'", "'M'"}, false),
PollExpiration: inputStrOrErr([]string{"\"E[X]pires\"", "'x'", "'X'"}, false),
}
ic.GlobalDown = inputOrErr(cfg, "global-down", false, ic.GlobalDown)
ic.GlobalUp = inputOrErr(cfg, "global-up", false, ic.GlobalUp)
@ -1082,6 +1100,7 @@ func parseInput(cfg *ini.File) Input {
ic.ComposePost = inputOrErr(cfg, "compose-post", false, ic.ComposePost)
ic.ComposeToggleContentWarning = inputOrErr(cfg, "compose-toggle-content-warning", false, ic.ComposeToggleContentWarning)
ic.ComposeVisibility = inputOrErr(cfg, "compose-visibility", false, ic.ComposeVisibility)
ic.ComposePoll = inputOrErr(cfg, "compose-poll", false, ic.ComposePoll)
ic.MediaDelete = inputOrErr(cfg, "media-delete", false, ic.MediaDelete)
ic.MediaEditDesc = inputOrErr(cfg, "media-edit-desc", false, ic.MediaEditDesc)
@ -1089,6 +1108,12 @@ func parseInput(cfg *ini.File) Input {
ic.VoteVote = inputOrErr(cfg, "vote-vote", false, ic.VoteVote)
ic.VoteSelect = inputOrErr(cfg, "vote-select", false, ic.VoteSelect)
ic.PollAdd = inputOrErr(cfg, "poll-add", false, ic.PollAdd)
ic.PollEdit = inputOrErr(cfg, "poll-edit", false, ic.PollEdit)
ic.PollDelete = inputOrErr(cfg, "poll-delete", false, ic.PollDelete)
ic.PollMultiToggle = inputOrErr(cfg, "poll-multi-toggle", false, ic.PollMultiToggle)
ic.PollExpiration = inputOrErr(cfg, "poll-expiration", false, ic.PollExpiration)
return ic
}

31
config/default_config.go

@ -139,19 +139,24 @@ leader-timeout=1000
#
# Available commands: home, direct, local, federated, compose, blocking,
# bookmarks, saved, favorited, boosts, favorites, following, followers, muting,
# profile, notifications, lists, tag
# profile, notifications, lists, tag, window
#
# 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
# "fav", the one with "f" will always run and "fav" will never run. Tag is
# special as you need to add the tag after, see the example below.
#
# Window is also special as it's a shortcut for switching between the timelines
# you've set under general and they are zero indexed. window 0 = your first
# timeline, window 1 = your second and so on.
#
# Some examples:
# leader-action=local,lo
# leader-action=lists,li
# leader-action=federated,fed
# leader-action=direct,d
# leader-action=tag linux,tl
# leader-action=window 0,h
#
@ -593,6 +598,10 @@ compose-toggle-content-warning="[T]oggle CW",'t','T'
# default="[V]isibility",'v','V'
compose-visibility="[V]isibility",'v','V'
# Switch to creating a poll
# default="P[O]ll",'o','O'
compose-poll="P[O]ll",'o','O'
# Delete media file
# default="[D]elete",'d','D'
media-delete="[D]elete",'d','D'
@ -612,4 +621,24 @@ vote-vote="[V]ote",'v','V'
# Select item to vote on
# default="[Enter] to select",' ', "Enter"
vote-select="[Enter] to select",' ', "Enter"
# Add a new poll option
# default="[A]dd",'a','A'
poll-add="[A]dd",'a','A'
# Edit a poll option
# default="[E]dit",'e','E'
poll-edit="[E]dit",'e','E'
# Delete a poll option
# default="[D]elete",'d','D'
poll-delete="[D]elete",'d','D'
# Toggle voting on multiple options
# default="Toggle [M]ultiple",'m','M'
poll-multi-toggle="Toggle [M]ultiple",'m','M'
# Change the expiration of poll
# default="E[X]pires",'x','X'
poll-expiration="E[X]pires",'x','X'
`

239
feed/feed.go

@ -598,18 +598,22 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e
}()
}
func NewTimelineHome(ac *api.AccountClient) *Feed {
feed := &Feed{
func newFeed(ac *api.AccountClient, ft FeedType) *Feed {
return &Feed{
accountClient: ac,
feedType: TimelineHome,
items: make([]api.Item, 0),
feedType: ft,
loadNewer: func() {},
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
}
func NewTimelineHome(ac *api.AccountClient) *Feed {
feed := newFeed(ac, TimelineHome)
feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimeline) }
feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimeline) }
feed.startStream(feed.accountClient.NewHomeStream())
@ -619,17 +623,7 @@ func NewTimelineHome(ac *api.AccountClient) *Feed {
}
func NewTimelineFederated(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: TimelineFederated,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, TimelineFederated)
feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimelineFederated) }
feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimelineFederated) }
feed.startStream(feed.accountClient.NewFederatedStream())
@ -639,17 +633,7 @@ func NewTimelineFederated(ac *api.AccountClient) *Feed {
}
func NewTimelineLocal(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: TimelineLocal,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, TimelineLocal)
feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimelineLocal) }
feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimelineLocal) }
feed.startStream(feed.accountClient.NewLocalStream())
@ -659,17 +643,7 @@ func NewTimelineLocal(ac *api.AccountClient) *Feed {
}
func NewConversations(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Conversations,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Conversations)
feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetConversations) }
feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetConversations) }
feed.startStream(feed.accountClient.NewDirectStream())
@ -679,17 +653,7 @@ func NewConversations(ac *api.AccountClient) *Feed {
}
func NewNotifications(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Notification,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Notification)
feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetNotifications) }
feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetNotifications) }
feed.startStreamNotification(feed.accountClient.NewHomeStream())
@ -699,16 +663,7 @@ func NewNotifications(ac *api.AccountClient) *Feed {
}
func NewFavorites(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Favorited,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Favorited)
feed.loadNewer = func() { feed.linkNewer(feed.accountClient.GetFavorites) }
feed.loadOlder = func() { feed.linkOlder(feed.accountClient.GetFavorites) }
@ -716,16 +671,7 @@ func NewFavorites(ac *api.AccountClient) *Feed {
}
func NewBookmarks(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Saved,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Saved)
feed.loadNewer = func() { feed.linkNewer(feed.accountClient.GetBookmarks) }
feed.loadOlder = func() { feed.linkOlder(feed.accountClient.GetBookmarks) }
@ -733,35 +679,16 @@ func NewBookmarks(ac *api.AccountClient) *Feed {
}
func NewUserSearch(ac *api.AccountClient, search string) *Feed {
feed := &Feed{
accountClient: ac,
feedType: UserList,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
name: search,
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, UserList)
feed.name = search
feed.loadNewer = func() { feed.singleNewerSearch(feed.accountClient.GetUsers, search) }
return feed
}
func NewUserProfile(ac *api.AccountClient, user *api.User) *Feed {
feed := &Feed{
accountClient: ac,
feedType: User,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
name: user.Data.Acct,
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, User)
feed.name = user.Data.Acct
feed.items = append(feed.items, api.NewUserItem(user, true))
feed.loadNewer = func() { feed.normalNewerUser(feed.accountClient.GetUser, user.Data.ID) }
feed.loadOlder = func() { feed.normalOlderUser(feed.accountClient.GetUser, user.Data.ID) }
@ -770,35 +697,15 @@ func NewUserProfile(ac *api.AccountClient, user *api.User) *Feed {
}
func NewThread(ac *api.AccountClient, status *mastodon.Status) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Thread,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Thread)
feed.loadNewer = func() { feed.singleThread(feed.accountClient.GetThread, status) }
return feed
}
func NewTag(ac *api.AccountClient, search string) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Tag,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
name: search,
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Tag)
feed.name = search
feed.loadNewer = func() { feed.newerSearchPG(feed.accountClient.GetTag, search) }
feed.loadOlder = func() { feed.olderSearchPG(feed.accountClient.GetTag, search) }
feed.startStream(feed.accountClient.NewTagStream(search))
@ -808,16 +715,7 @@ func NewTag(ac *api.AccountClient, search string) *Feed {
}
func NewListList(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Lists,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Lists)
once := true
feed.loadNewer = func() {
if once {
@ -830,17 +728,8 @@ func NewListList(ac *api.AccountClient) *Feed {
}
func NewList(ac *api.AccountClient, list *mastodon.List) *Feed {
feed := &Feed{
accountClient: ac,
feedType: List,
items: make([]api.Item, 0),
loadOlder: func() {},
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
name: list.Title,
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, List)
feed.name = list.Title
feed.loadNewer = func() { feed.normalNewerID(feed.accountClient.GetListStatuses, list.ID) }
feed.loadOlder = func() { feed.normalOlderID(feed.accountClient.GetListStatuses, list.ID) }
feed.startStream(feed.accountClient.NewListStream(list.ID))
@ -850,17 +739,7 @@ func NewList(ac *api.AccountClient, list *mastodon.List) *Feed {
}
func NewFavoritesStatus(ac *api.AccountClient, id mastodon.ID) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Favorites,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadOlder: func() {},
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Favorites)
once := true
feed.loadNewer = func() {
if once {
@ -873,17 +752,7 @@ func NewFavoritesStatus(ac *api.AccountClient, id mastodon.ID) *Feed {
}
func NewBoosts(ac *api.AccountClient, id mastodon.ID) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Boosts,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadOlder: func() {},
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Boosts)
once := true
feed.loadNewer = func() {
if once {
@ -896,16 +765,7 @@ func NewBoosts(ac *api.AccountClient, id mastodon.ID) *Feed {
}
func NewFollowers(ac *api.AccountClient, id mastodon.ID) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Followers,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Followers)
once := true
feed.loadNewer = func() {
if once {
@ -919,16 +779,7 @@ func NewFollowers(ac *api.AccountClient, id mastodon.ID) *Feed {
}
func NewFollowing(ac *api.AccountClient, id mastodon.ID) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Following,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Following)
once := true
feed.loadNewer = func() {
if once {
@ -942,19 +793,7 @@ func NewFollowing(ac *api.AccountClient, id mastodon.ID) *Feed {
}
func NewBlocking(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Blocking,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed.loadNewer = func() { feed.linkNewer(feed.accountClient.GetBlocking) }
feed.loadOlder = func() { feed.linkOlder(feed.accountClient.GetBlocking) }
feed := newFeed(ac, Blocking)
once := true
feed.loadNewer = func() {
if once {
@ -968,16 +807,7 @@ func NewBlocking(ac *api.AccountClient) *Feed {
}
func NewMuting(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: Muting,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, Muting)
once := true
feed.loadNewer = func() {
if once {
@ -991,16 +821,7 @@ func NewMuting(ac *api.AccountClient) *Feed {
}
func NewFollowRequests(ac *api.AccountClient) *Feed {
feed := &Feed{
accountClient: ac,
feedType: FollowRequests,
items: make([]api.Item, 0),
apiData: &api.RequestData{},
Update: make(chan DesktopNotificationType, 1),
loadingNewer: &LoadingLock{},
loadingOlder: &LoadingLock{},
}
feed := newFeed(ac, FollowRequests)
once := true
feed.loadNewer = func() {
if once {

2
go.mod

@ -3,7 +3,7 @@ module github.com/RasmusLindroth/tut
go 1.17
require (
github.com/RasmusLindroth/go-mastodon v0.0.5
github.com/RasmusLindroth/go-mastodon v0.0.6
github.com/atotto/clipboard v0.1.4
github.com/gdamore/tcell/v2 v2.5.1
github.com/gen2brain/beeep v0.0.0-20220402123239-6a3042f4b71a

4
go.sum

@ -1,5 +1,5 @@
github.com/RasmusLindroth/go-mastodon v0.0.5 h1:GHyyv2qc4X9XmUfM1IytRjaU5bWqMoPD+wM+Wvkc/4U=
github.com/RasmusLindroth/go-mastodon v0.0.5/go.mod h1:4L0oyiNwq1tUoiByczzhSikxR9RiANzELtZgexxKpPM=
github.com/RasmusLindroth/go-mastodon v0.0.6 h1:dhVXungiJeRKIE2ZRUJrPibBJ7YkeM4eVyeg3+q6Juk=
github.com/RasmusLindroth/go-mastodon v0.0.6/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=

2
main.go

@ -10,7 +10,7 @@ import (
"github.com/rivo/tview"
)
const version = "1.0.7"
const version = "1.0.8"
func main() {
util.MakeDirs()

12
ui/cmdbar.go

@ -133,9 +133,13 @@ func (c *CmdBar) DoneFunc(key tcell.Key) {
if len(tag) == 0 {
break
}
c.tutView.Timeline.AddFeed(
NewTagFeed(c.tutView, tag),
)
c.tutView.TagCommand(tag)
c.Back()
case ":window":
if len(parts) < 2 {
break
}
c.tutView.WindowCommand(parts[1])
c.Back()
case ":user":
if len(parts) < 2 {
@ -162,7 +166,7 @@ func (c *CmdBar) DoneFunc(key tcell.Key) {
func (c *CmdBar) Autocomplete(curr string) []string {
var entries []string
words := strings.Split(":blocking,:boosts,:bookmarks,:compose,:favorites,:favorited,:followers,:following,:help,:h,:lists,:muting,:profile,:requests,:saved,:tag,:timeline,:tl,:user,:quit,:q", ",")
words := strings.Split(":blocking,:boosts,:bookmarks,:compose,:favorites,:favorited,:followers,:following,:help,:h,:lists,:muting,:profile,:requests,:saved,:tag,:timeline,:tl,:user,:window,:quit,:q", ",")
if curr == "" {
return entries
}

12
ui/commands.go

@ -2,6 +2,7 @@ package ui
import (
"fmt"
"strconv"
"github.com/RasmusLindroth/go-mastodon"
"github.com/RasmusLindroth/tut/api"
@ -83,6 +84,17 @@ func (tv *TutView) TagCommand(tag string) {
)
}
func (tv *TutView) WindowCommand(index string) {
i, err := strconv.Atoi(index)
if err != nil {
tv.ShowError(
fmt.Sprintf("couldn't convert str to int. Error %v", err),
)
return
}
tv.FocusFeed(i)
}
func (tv *TutView) BoostsCommand() {
item, itemErr := tv.GetCurrentItem()
if itemErr != nil {

102
ui/composeview.go

@ -68,7 +68,7 @@ func newComposeUI(cv *ComposeView) *tview.Flex {
AddItem(tview.NewBox(), 2, 0, false).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(cv.visibility, 1, 0, false).
AddItem(cv.info, 4, 0, false).
AddItem(cv.info, 5, 0, false).
AddItem(cv.media.View, 0, 1, false), 0, 1, false), 0, 1, false).
AddItem(cv.input.View, 1, 0, false).
AddItem(cv.controls, 1, 0, false).
@ -104,6 +104,7 @@ func (cv *ComposeView) SetControls(ctrl ComposeControls) {
items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeToggleContentWarning, true))
items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeEditSpoiler, true))
items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeMediaFocus, true))
items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposePoll, true))
if cv.msg.Status != nil {
items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeIncludeQuote, true))
}
@ -118,6 +119,8 @@ func (cv *ComposeView) SetControls(ctrl ComposeControls) {
}
func (cv *ComposeView) SetStatus(status *mastodon.Status) {
cv.tutView.PollView.Reset()
cv.media.Reset()
msg := &msgToot{}
if status != nil {
if status.Reblog != nil {
@ -146,7 +149,7 @@ func (cv *ComposeView) SetStatus(status *mastodon.Status) {
cv.visibility.SetOptions(visibilities, cv.visibilitySelected)
cv.visibility.SetCurrentOption(index)
cv.visibility.SetInputCapture(cv.visibilityInput)
cv.updateContent()
cv.UpdateContent()
cv.SetControls(ComposeNormal)
}
@ -178,7 +181,7 @@ func (cv *ComposeView) EditText() {
return
}
cv.msg.Text = text
cv.updateContent()
cv.UpdateContent()
}
func (cv *ComposeView) EditSpoiler() {
@ -190,16 +193,16 @@ func (cv *ComposeView) EditSpoiler() {
return
}
cv.msg.SpoilerText = text
cv.updateContent()
cv.UpdateContent()
}
func (cv *ComposeView) ToggleCW() {
cv.msg.Sensitive = !cv.msg.Sensitive
cv.updateContent()
cv.UpdateContent()
}
func (cv *ComposeView) updateContent() {
cv.info.SetText(fmt.Sprintf("Chars left: %d\nSpoiler: %t\n", cv.msgLength(), cv.msg.Sensitive))
func (cv *ComposeView) UpdateContent() {
cv.info.SetText(fmt.Sprintf("Chars left: %d\nSpoiler: %t\nHas poll: %t\n", cv.msgLength(), cv.msg.Sensitive, cv.tutView.PollView.HasPoll()))
normal := config.ColorMark(cv.tutView.tut.Config.Style.Text)
subtleColor := config.ColorMark(cv.tutView.tut.Config.Style.Subtle)
warningColor := config.ColorMark(cv.tutView.tut.Config.Style.WarningText)
@ -252,26 +255,24 @@ func (cv *ComposeView) IncludeQuote() {
t += "\n"
cv.msg.Text = t
cv.msg.QuoteIncluded = true
cv.updateContent()
cv.UpdateContent()
}
func (cv *ComposeView) HasMedia() bool {
return len(cv.media.Files) > 0
}
func (cv *ComposeView) visibilityInput(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyRune {
switch event.Rune() {
case 'j', 'J':
return tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
case 'k', 'K':
return tcell.NewEventKey(tcell.KeyUp, 0, tcell.ModNone)
case 'q', 'Q':
cv.exitVisibility()
return nil
}
} else {
switch event.Key() {
case tcell.KeyEsc:
cv.exitVisibility()
return nil
}
if cv.tutView.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
return tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
}
if cv.tutView.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
return tcell.NewEventKey(tcell.KeyUp, 0, tcell.ModNone)
}
if cv.tutView.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) ||
cv.tutView.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) {
cv.exitVisibility()
return nil
}
return event
}
@ -306,32 +307,37 @@ func (cv *ComposeView) Post() {
send.SpoilerText = toot.SpoilerText
}
attachments := cv.media.Files
for _, ap := range attachments {
f, err := os.Open(ap.Path)
if err != nil {
cv.tutView.ShowError(
fmt.Sprintf("Couldn't upload media. Error: %v\n", err),
)
f.Close()
return
}
media := &mastodon.Media{
File: f,
}
if ap.Description != "" {
media.Description = ap.Description
}
a, err := cv.tutView.tut.Client.Client.UploadMediaFromMedia(context.Background(), media)
if err != nil {
cv.tutView.ShowError(
fmt.Sprintf("Couldn't upload media. Error: %v\n", err),
)
if cv.HasMedia() {
attachments := cv.media.Files
for _, ap := range attachments {
f, err := os.Open(ap.Path)
if err != nil {
cv.tutView.ShowError(
fmt.Sprintf("Couldn't upload media. Error: %v\n", err),
)
f.Close()
return
}
media := &mastodon.Media{
File: f,
}
if ap.Description != "" {
media.Description = ap.Description
}
a, err := cv.tutView.tut.Client.Client.UploadMediaFromMedia(context.Background(), media)
if err != nil {
cv.tutView.ShowError(
fmt.Sprintf("Couldn't upload media. Error: %v\n", err),
)
f.Close()
return
}
f.Close()
return
send.MediaIDs = append(send.MediaIDs, a.ID)
}
f.Close()
send.MediaIDs = append(send.MediaIDs, a.ID)
}
if cv.tutView.PollView.HasPoll() && !cv.HasMedia() {
send.Poll = cv.tutView.PollView.GetPoll()
}
send.Visibility = cv.msg.Visibility

54
ui/input.go

@ -36,6 +36,8 @@ func (tv *TutView) Input(event *tcell.EventKey) *tcell.EventKey {
return tv.InputViewItem(event)
case ComposeFocus:
return tv.InputComposeView(event)
case PollFocus:
return tv.InputPollView(event)
case LinkFocus:
return tv.InputLinkView(event)
case CmdFocus:
@ -128,7 +130,8 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey {
tv.ListsCommand()
case config.LeaderTag:
tv.TagCommand(subaction)
case config.LeaderWindow:
tv.WindowCommand(subaction)
}
tv.Leader.ResetInactive()
return nil
@ -614,9 +617,21 @@ func (tv *TutView) InputComposeView(event *tcell.EventKey) *tcell.EventKey {
return nil
}
if tv.tut.Config.Input.ComposeMediaFocus.Match(event.Key(), event.Rune()) {
if tv.PollView.HasPoll() {
tv.ShowError("Can't add media when you have a poll")
return nil
}
tv.SetPage(MediaFocus)
return nil
}
if tv.tut.Config.Input.ComposePoll.Match(event.Key(), event.Rune()) {
if tv.ComposeView.HasMedia() {
tv.ShowError("Can't add poll when you have added media")
return nil
}
tv.SetPage(PollFocus)
return nil
}
if tv.tut.Config.Input.ComposePost.Match(event.Key(), event.Rune()) {
tv.ComposeView.Post()
return nil
@ -696,6 +711,43 @@ func (tv *TutView) InputMediaAdd(event *tcell.EventKey) *tcell.EventKey {
return event
}
func (tv *TutView) InputPollView(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.PollAdd.Match(event.Key(), event.Rune()) {
tv.PollView.Add()
return nil
}
if tv.tut.Config.Input.PollEdit.Match(event.Key(), event.Rune()) {
tv.PollView.Edit()
return nil
}
if tv.tut.Config.Input.PollDelete.Match(event.Key(), event.Rune()) {
tv.PollView.Delete()
return nil
}
if tv.tut.Config.Input.PollMultiToggle.Match(event.Key(), event.Rune()) {
tv.PollView.ToggleMultiple()
return nil
}
if tv.tut.Config.Input.PollExpiration.Match(event.Key(), event.Rune()) {
tv.PollView.FocusExpiration()
return nil
}
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.PollView.Next()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.PollView.Prev()
return nil
}
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.SetPage(ComposeFocus)
return nil
}
return event
}
func (tv *TutView) InputVote(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.VoteView.Next()

245
ui/pollview.go

@ -0,0 +1,245 @@
package ui
import (
"fmt"
"strings"
"unicode/utf8"
"github.com/RasmusLindroth/go-mastodon"
"github.com/RasmusLindroth/tut/config"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
var durations = []string{
"5 minutes",
"30 minutes",
"1 hour",
"6 hours",
"1 day",
"3 days",
"7 days",
}
var durationsTime = map[string]int64{
"5 minutes": 60 * 50,
"30 minutes": 60 * 30,
"1 hour": 60 * 60,
"6 hours": 60 * 60 * 6,
"1 day": 60 * 60 * 24,
"3 days": 60 * 60 * 24 * 3,
"7 days": 60 * 60 * 24 * 7,
}
type PollView struct {
tutView *TutView
shared *Shared
View *tview.Flex
info *tview.TextView
expiration *tview.DropDown
controls *tview.TextView
list *tview.List
poll *mastodon.TootPoll
}
func NewPollView(tv *TutView) *PollView {
p := &PollView{
tutView: tv,
shared: tv.Shared,
info: NewTextView(tv.tut.Config),
expiration: NewDropDown(tv.tut.Config),
controls: NewTextView(tv.tut.Config),
list: NewList(tv.tut.Config),
}
p.Reset()
p.View = pollViewUI(p)
return p
}
func pollViewUI(p *PollView) *tview.Flex {
var items []string
items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollAdd, true))
items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollEdit, true))
items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollDelete, true))
items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollMultiToggle, true))
items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollExpiration, true))
p.controls.SetText(strings.Join(items, " "))
p.expiration.SetLabel("Expiration: ")
p.expiration.SetOptions(durations, p.expirationSelected)
p.expiration.SetCurrentOption(4)
return tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(p.shared.Top.View, 1, 0, false).
AddItem(tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(p.list, 0, 10, false), 0, 2, false).
AddItem(tview.NewBox(), 2, 0, false).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(p.expiration, 1, 0, false).
AddItem(p.info, 3, 0, false), 0, 1, false), 0, 1, false).
AddItem(p.controls, 1, 0, false).
AddItem(p.shared.Bottom.View, 2, 0, false)
}
func (p *PollView) Reset() {
p.poll = &mastodon.TootPoll{
Options: []string{},
ExpiresInSeconds: durationsTime[durations[4]],
Multiple: false,
HideTotals: false,
}
p.list.Clear()
p.redrawInfo()
}
func (p *PollView) HasPoll() bool {
return p.list.GetItemCount() > 1
}
func (p *PollView) GetPoll() *mastodon.TootPoll {
options := []string{}
for i := 0; i < p.list.GetItemCount(); i++ {
m, _ := p.list.GetItemText(i)
options = append(options, m)
}
return &mastodon.TootPoll{
Options: options,
ExpiresInSeconds: p.poll.ExpiresInSeconds,
Multiple: p.poll.Multiple,
HideTotals: false,
}
}
func (p *PollView) redrawInfo() {
content := fmt.Sprintf("Multiple answers: %v", p.poll.Multiple)
p.info.SetText(content)
}
func (p *PollView) Prev() {
index := p.list.GetCurrentItem()
if index-1 >= 0 {
p.list.SetCurrentItem(index - 1)
}
}
func (p *PollView) Next() {
index := p.list.GetCurrentItem()
if index+1 < p.list.GetItemCount() {
p.list.SetCurrentItem(index + 1)
}
}
func checkOption(s string) (string, bool) {
s = strings.TrimSpace(s)
if len(s) == 0 {
return "", false
}
if utf8.RuneCountInString(s) > 25 {
ns := ""
i := 0
for _, r := range s {
if i >= 25 {
break
}
ns += string(r)
i++
}
s = ns
}
return s, true
}
func (p *PollView) Add() {
if p.list.GetItemCount() > 3 {
p.tutView.ShowError("You can only have a maximum of 4 options.")
return
}
text, err := OpenEditor(p.tutView, "")
if err != nil {
p.tutView.ShowError(
fmt.Sprintf("Couldn't open editor. Error: %v", err),
)
return
}
text, valid := checkOption(text)
if !valid {
return
}
p.list.AddItem(text, "", 0, nil)
p.list.SetCurrentItem(
p.list.GetItemCount() - 1,
)
}
func (p *PollView) Edit() {
if p.list.GetItemCount() == 0 {
return
}
text, _ := p.list.GetItemText(p.list.GetCurrentItem())
text, err := OpenEditor(p.tutView, text)
if err != nil {
p.tutView.ShowError(
fmt.Sprintf("Couldn't open editor. Error: %v", err),
)
return
}
text, valid := checkOption(text)
if !valid {
return
}
p.list.SetItemText(p.list.GetCurrentItem(), text, "")
}
func (p *PollView) Delete() {
if p.list.GetItemCount() == 0 {
return
}
item := p.list.GetCurrentItem()
p.list.RemoveItem(item)
if p.list.GetCurrentItem() < 0 && p.list.GetItemCount() > 0 {
p.list.SetCurrentItem(0)
}
}
func (p *PollView) ToggleMultiple() {
p.poll.Multiple = !p.poll.Multiple
p.redrawInfo()
}
func (p *PollView) expirationSelected(s string, index int) {
_, v := p.expiration.GetCurrentOption()
for k, dur := range durationsTime {
if v == k {
p.poll.ExpiresInSeconds = dur
break
}
}
p.exitExpiration()
}
func (p *PollView) expirationInput(event *tcell.EventKey) *tcell.EventKey {
if p.tutView.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
return tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
}
if p.tutView.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
return tcell.NewEventKey(tcell.KeyUp, 0, tcell.ModNone)
}
if p.tutView.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) ||
p.tutView.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) {
p.exitExpiration()
return nil
}
return event
}
func (p *PollView) FocusExpiration() {
p.tutView.tut.App.SetInputCapture(p.expirationInput)
p.tutView.tut.App.SetFocus(p.expiration)
ev := tcell.NewEventKey(tcell.KeyDown, 0, tcell.ModNone)
p.tutView.tut.App.QueueEvent(ev)
}
func (p *PollView) exitExpiration() {
p.tutView.tut.App.SetInputCapture(p.tutView.Input)
p.tutView.tut.App.SetFocus(p.tutView.View)
}

3
ui/statusbar.go

@ -30,6 +30,7 @@ const (
ScrollMode
UserMode
VoteMode
PollMode
)
func (sb *StatusBar) SetMode(m ViewMode) {
@ -58,5 +59,7 @@ func (sb *StatusBar) SetMode(m ViewMode) {
sb.View.SetText("-- VIEW --")
case UserMode:
sb.View.SetText("-- SELECT USER --")
case PollMode:
sb.View.SetText("-- CREATE POLL --")
}
}

3
ui/tutview.go

@ -50,6 +50,7 @@ type TutView struct {
LinkView *LinkView
ComposeView *ComposeView
VoteView *VoteView
PollView *PollView
HelpView *HelpView
ModalView *ModalView
@ -161,6 +162,7 @@ func (tv *TutView) loggedIn(acc auth.Account) {
tv.MainView = NewMainView(tv, update)
tv.ComposeView = NewComposeView(tv)
tv.VoteView = NewVoteView(tv)
tv.PollView = NewPollView(tv)
tv.HelpView = NewHelpView(tv)
tv.ModalView = NewModalView(tv)
@ -169,6 +171,7 @@ func (tv *TutView) loggedIn(acc auth.Account) {
tv.View.AddPage("compose", tv.ComposeView.View, true, false)
tv.View.AddPage("vote", tv.VoteView.View, true, false)
tv.View.AddPage("help", tv.HelpView.View, true, false)
tv.View.AddPage("poll", tv.PollView.View, true, false)
tv.View.AddPage("modal", tv.ModalView.View, true, false)
tv.SetPage(MainFocus)
}

8
ui/view.go

@ -19,6 +19,7 @@ const (
CmdFocus
VoteFocus
HelpFocus
PollFocus
)
func (tv *TutView) GetCurrentFeed() *Feed {
@ -106,6 +107,7 @@ func (tv *TutView) SetPage(f PageFocusAt) {
tv.View.SwitchToPage("compose")
tv.Shared.Bottom.StatusBar.SetMode(ComposeMode)
tv.Shared.Top.SetText("write a toot")
tv.ComposeView.UpdateContent()
tv.ComposeView.SetControls(ComposeNormal)
tv.tut.App.SetFocus(tv.ComposeView.content)
case MediaFocus:
@ -135,6 +137,12 @@ func (tv *TutView) SetPage(f PageFocusAt) {
tv.PageFocus = ModalFocus
tv.View.SwitchToPage("modal")
tv.tut.App.SetFocus(tv.ModalView.View)
case PollFocus:
tv.PageFocus = PollFocus
tv.View.SwitchToPage("poll")
tv.tut.App.SetFocus(tv.View)
tv.Shared.Bottom.StatusBar.SetMode(PollMode)
tv.Shared.Top.SetText("create a poll")
}
tv.ShouldSync()

1
ui/voteview.go

@ -36,7 +36,6 @@ func NewVoteView(tv *TutView) *VoteView {
func voteViewUI(v *VoteView) *tview.Flex {
var items []string
items = append(items, config.ColorFromKey(v.tutView.tut.Config, v.tutView.tut.Config.Input.VoteSelect, true))
items = append(items, config.ColorFromKey(v.tutView.tut.Config, v.tutView.tut.Config.Input.VoteSelect, true))
v.controls.SetText(strings.Join(items, " "))
return tview.NewFlex().SetDirection(tview.FlexRow).

Loading…
Cancel
Save