You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1320 lines
34 KiB

package ui
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/RasmusLindroth/go-mastodon"
"github.com/RasmusLindroth/tut/api"
"github.com/RasmusLindroth/tut/config"
"github.com/RasmusLindroth/tut/feed"
"github.com/RasmusLindroth/tut/util"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func (tv *TutView) Input(event *tcell.EventKey) *tcell.EventKey {
if tv.PageFocus != LoginFocus {
switch event.Rune() {
case ':':
tv.SetPage(CmdFocus)
case '?':
tv.SetPage(HelpFocus)
}
}
if tv.PageFocus != LoginFocus && tv.PageFocus != CmdFocus {
event = tv.InputLeaderKey(event)
if event == nil {
return nil
}
}
switch tv.PageFocus {
case LoginFocus:
return tv.InputLoginView(event)
case MainFocus:
return tv.InputMainView(event)
case ViewFocus:
return tv.InputViewItem(event)
case ComposeFocus:
return tv.InputComposeView(event)
case PollFocus:
return tv.InputPollView(event)
case LinkFocus:
return tv.InputLinkView(event)
case CmdFocus:
return tv.InputCmdView(event)
case MediaFocus:
return tv.InputMedia(event)
case MediaAddFocus:
return tv.InputMediaAdd(event)
case VoteFocus:
return tv.InputVote(event)
case HelpFocus:
return tv.InputHelp(event)
case PreferenceFocus:
return tv.InputPreference(event)
default:
return event
}
}
func (tv *TutView) InputLoginView(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.LoginView.Next()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.LoginView.Prev()
return nil
}
if tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) {
tv.LoginView.Selected()
return nil
}
if tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.tut.App.Stop()
tv.CleanExit(0)
return nil
}
return event
}
func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.General.LeaderKey == rune(0) {
return event
}
if event.Rune() == tv.tut.Config.General.LeaderKey {
tv.Leader.Reset()
return nil
} else if tv.Leader.IsActive() {
if event.Rune() != rune(0) {
tv.Leader.AddRune(event.Rune())
}
action := config.LeaderNone
var subaction string
content := tv.Leader.Content()
for _, la := range tv.tut.Config.General.LeaderActions {
if la.Shortcut == content {
action = la.Command
subaction = la.Subaction
break
}
}
if action == config.LeaderNone {
return nil
}
switch action {
case config.LeaderHome:
tv.HomeCommand()
case config.LeaderDirect:
tv.DirectCommand()
case config.LeaderLocal:
tv.LocalCommand()
case config.LeaderFederated:
tv.FederatedCommand()
case config.LeaderClearNotifications:
tv.ClearNotificationsCommand()
case config.LeaderCompose:
tv.ComposeCommand()
case config.LeaderEdit:
tv.EditCommand()
case config.LeaderBlocking:
tv.BlockingCommand()
case config.LeaderBookmarks, config.LeaderSaved:
tv.BookmarksCommand()
case config.LeaderFavorited:
tv.FavoritedCommand()
case config.LeaderHistory:
tv.HistoryCommand()
case config.LeaderBoosts:
tv.BoostsCommand()
case config.LeaderFavorites:
tv.FavoritesCommand()
case config.LeaderFollowing:
tv.FollowingCommand()
case config.LeaderFollowers:
tv.FollowersCommand()
case config.LeaderMuting:
tv.MutingCommand()
case config.LeaderPreferences:
tv.PreferencesCommand()
case config.LeaderProfile:
tv.ProfileCommand()
case config.LeaderNotifications:
tv.NotificationsCommand()
case config.LeaderLoadNewer:
tv.LoadNewerCommand()
case config.LeaderLists:
tv.ListsCommand()
case config.LeaderTag:
tv.TagCommand(subaction)
case config.LeaderWindow:
tv.WindowCommand(subaction)
case config.LeaderListPlacement:
switch subaction {
case "top":
tv.ListPlacementCommand(config.ListPlacementTop)
case "right":
tv.ListPlacementCommand(config.ListPlacementRight)
case "bottom":
tv.ListPlacementCommand(config.ListPlacementBottom)
case "left":
tv.ListPlacementCommand(config.ListPlacementLeft)
}
case config.LeaderListSplit:
switch subaction {
case "row":
tv.ListSplitCommand(config.ListRow)
case "column":
tv.ListSplitCommand(config.ListColumn)
}
case config.LeaderProportions:
parts := strings.Split(subaction, " ")
if len(parts) == 2 {
tv.ProportionsCommand(parts[0], parts[1])
}
}
tv.Leader.ResetInactive()
return nil
}
return event
}
func (tv *TutView) InputMainView(event *tcell.EventKey) *tcell.EventKey {
switch tv.SubFocus {
case ListFocus:
return tv.InputMainViewFeed(event)
case ContentFocus:
return tv.InputMainViewContent(event)
default:
return event
}
}
func (tv *TutView) InputMainViewFeed(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.MainHome.Match(event.Key(), event.Rune()) {
tv.Timeline.HomeItemFeed()
return nil
}
if tv.tut.Config.Input.MainEnd.Match(event.Key(), event.Rune()) {
tv.Timeline.EndItemFeed()
return nil
}
if tv.tut.Config.Input.MainPrevFeed.Match(event.Key(), event.Rune()) {
tv.Timeline.PrevFeed()
return nil
}
if tv.tut.Config.Input.MainNextFeed.Match(event.Key(), event.Rune()) {
tv.Timeline.NextFeed()
return nil
}
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.Timeline.NextItemFeed()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.Timeline.PrevItemFeed()
return nil
}
if tv.tut.Config.Input.MainPrevWindow.Match(event.Key(), event.Rune()) {
if tv.tut.Config.General.NotificationFeed {
tv.PrevFeed()
}
return nil
}
if tv.tut.Config.Input.MainNextWindow.Match(event.Key(), event.Rune()) {
if tv.tut.Config.General.NotificationFeed {
tv.NextFeed()
}
return nil
}
if tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
exiting := tv.Timeline.RemoveCurrent(false)
if exiting && tv.Timeline.FeedFocusIndex == 0 {
tv.ModalView.Run("Do you want to exit tut?",
func() {
tv.Timeline.RemoveCurrent(true)
})
return nil
} else if exiting && tv.Timeline.FeedFocusIndex != 0 {
tv.FocusFeed(0)
}
return nil
}
for i, tl := range tv.tut.Config.General.Timelines {
if tl.Key.Match(event.Key(), event.Rune()) {
tv.FocusFeed(i)
}
}
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) {
exiting := tv.Timeline.RemoveCurrent(false)
if exiting && tv.Timeline.FeedFocusIndex != 0 {
tv.FocusFeed(0)
}
return nil
}
return tv.InputItem(event)
}
func (tv *TutView) InputMainViewContent(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.Timeline.ScrollDown()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.Timeline.ScrollDown()
return nil
}
return tv.InputItem(event)
}
func (tv *TutView) InputHelp(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.PrevFocus()
return nil
}
return event
}
func (tv *TutView) InputViewItem(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.FocusMainNoHistory()
return nil
}
return event
}
func (tv *TutView) InputItem(event *tcell.EventKey) *tcell.EventKey {
fd := tv.GetCurrentFeed()
ft := fd.Data.Type()
item, err := tv.GetCurrentItem()
if err != nil {
return event
}
if tv.tut.Config.Input.MainCompose.Match(event.Key(), event.Rune()) {
tv.InitPost(nil, nil)
return nil
}
switch item.Type() {
case api.StatusType:
return tv.InputStatus(event, item, item.Raw().(*mastodon.Status), nil)
case api.StatusHistoryType:
return tv.InputStatusHistory(event, item, item.Raw().(*mastodon.StatusHistory), nil)
case api.UserType, api.ProfileType:
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), InputUserNormal)
case "favourite":
user := nd.User.Raw().(*api.User)
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), user.Data)
case "reblog":
user := nd.User.Raw().(*api.User)
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), user.Data)
case "mention":
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil)
case "update":
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil)
case "status":
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil)
case "poll":
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status), nil)
case "follow_request":
return tv.InputUser(event, nd.User.Raw().(*api.User), InputUserFollowRequest)
}
case api.ListsType:
ld := item.Raw().(*mastodon.List)
return tv.InputList(event, ld)
}
return event
}
func (tv *TutView) InputStatus(event *tcell.EventKey, item api.Item, status *mastodon.Status, nAcc *mastodon.Account) *tcell.EventKey {
sr := util.StatusOrReblog(status)
hasMedia := len(sr.MediaAttachments) > 0
hasPoll := sr.Poll != nil
hasSpoiler := sr.Sensitive
isMine := sr.Account.ID == tv.tut.Client.Me.ID
boosted := sr.Reblogged
favorited := sr.Favourited
bookmarked := sr.Bookmarked
if tv.tut.Config.Input.StatusAvatar.Match(event.Key(), event.Rune()) {
if nAcc != nil {
openAvatar(tv, *nAcc)
} else {
openAvatar(tv, sr.Account)
}
return nil
}
if tv.tut.Config.Input.StatusBoost.Match(event.Key(), event.Rune()) {
txt := "boost"
if boosted {
txt = "unboost"
}
tv.ModalView.Run(
fmt.Sprintf("Do you want to %s this toot?", txt), func() {
ns, err := tv.tut.Client.BoostToggle(status)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't boost toot. Error: %v\n", err),
)
return
}
*status = *ns
tv.RedrawControls()
})
return nil
}
if tv.tut.Config.Input.StatusDelete.Match(event.Key(), event.Rune()) {
if !isMine {
return nil
}
tv.ModalView.Run("Do you want to delete this toot?", func() {
err := tv.tut.Client.DeleteStatus(sr)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't delete toot. Error: %v\n", err),
)
return
}
status.Card = nil
status.Sensitive = false
status.SpoilerText = ""
status.Favourited = false
status.MediaAttachments = nil
status.Reblogged = false
status.Content = "Deleted"
tv.RedrawContent()
})
return nil
}
if tv.tut.Config.Input.StatusEdit.Match(event.Key(), event.Rune()) {
tv.EditCommand()
return nil
}
if tv.tut.Config.Input.StatusFavorite.Match(event.Key(), event.Rune()) {
txt := "favorite"
if favorited {
txt = "unfavorite"
}
tv.ModalView.Run(fmt.Sprintf("Do you want to %s this toot?", txt),
func() {
ns, err := tv.tut.Client.FavoriteToogle(status)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't favorite toot. Error: %v\n", err),
)
return
}
*status = *ns
tv.RedrawControls()
})
return nil
}
if tv.tut.Config.Input.StatusMedia.Match(event.Key(), event.Rune()) {
if hasMedia {
openMedia(tv, sr)
}
return nil
}
if tv.tut.Config.Input.StatusLinks.Match(event.Key(), event.Rune()) {
tv.SetPage(LinkFocus)
return nil
}
if tv.tut.Config.Input.StatusPoll.Match(event.Key(), event.Rune()) {
if !hasPoll {
return nil
}
tv.VoteView.SetPoll(sr.Poll)
tv.SetPage(VoteFocus)
return nil
}
if tv.tut.Config.Input.StatusReply.Match(event.Key(), event.Rune()) {
tv.InitPost(status, nil)
return nil
}
if tv.tut.Config.Input.StatusBookmark.Match(event.Key(), event.Rune()) {
txt := "save"
if bookmarked {
txt = "unsave"
}
tv.ModalView.Run(fmt.Sprintf("Do you want to %s this toot?", txt),
func() {
ns, err := tv.tut.Client.BookmarkToogle(status)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't bookmark toot. Error: %v\n", err),
)
return
}
*status = *ns
tv.RedrawControls()
})
return nil
}
if tv.tut.Config.Input.StatusThread.Match(event.Key(), event.Rune()) {
tv.Timeline.AddFeed(NewThreadFeed(tv, item))
return nil
}
if tv.tut.Config.Input.StatusUser.Match(event.Key(), event.Rune()) {
id := sr.Account.ID
if nAcc != nil {
id = nAcc.ID
}
user, err := tv.tut.Client.GetUserByID(id)
if err != nil {
return nil
}
tv.Timeline.AddFeed(NewUserFeed(tv, user))
return nil
}
if tv.tut.Config.Input.StatusViewFocus.Match(event.Key(), event.Rune()) {
tv.SetPage(ViewFocus)
return nil
}
if tv.tut.Config.Input.StatusYank.Match(event.Key(), event.Rune()) {
copyToClipboard(sr.URL)
return nil
}
if tv.tut.Config.Input.StatusToggleSpoiler.Match(event.Key(), event.Rune()) {
if !hasSpoiler {
return nil
}
if !item.ShowSpoiler() {
item.ToggleSpoiler()
tv.RedrawContent()
}
return nil
}
return event
}
func (tv *TutView) InputStatusHistory(event *tcell.EventKey, item api.Item, sr *mastodon.StatusHistory, nAcc *mastodon.Account) *tcell.EventKey {
hasMedia := len(sr.MediaAttachments) > 0
hasSpoiler := sr.Sensitive
status := &mastodon.Status{
Content: sr.Content,
SpoilerText: sr.SpoilerText,
Account: sr.Account,
Sensitive: sr.Sensitive,
CreatedAt: sr.CreatedAt,
Emojis: sr.Emojis,
MediaAttachments: sr.MediaAttachments,
}
if tv.tut.Config.Input.StatusAvatar.Match(event.Key(), event.Rune()) {
if nAcc != nil {
openAvatar(tv, *nAcc)
} else {
openAvatar(tv, sr.Account)
}
return nil
}
if tv.tut.Config.Input.StatusMedia.Match(event.Key(), event.Rune()) {
if hasMedia {
openMedia(tv, status)
}
return nil
}
if tv.tut.Config.Input.StatusLinks.Match(event.Key(), event.Rune()) {
tv.SetPage(LinkFocus)
return nil
}
if tv.tut.Config.Input.StatusViewFocus.Match(event.Key(), event.Rune()) {
tv.SetPage(ViewFocus)
return nil
}
if tv.tut.Config.Input.StatusToggleSpoiler.Match(event.Key(), event.Rune()) {
if !hasSpoiler {
return nil
}
if !item.ShowSpoiler() {
item.ToggleSpoiler()
tv.RedrawContent()
}
return nil
}
return event
}
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 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)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't accept follow request. Error: %v\n", err),
)
return
}
f := tv.GetCurrentFeed()
f.Delete()
tv.RedrawContent()
},
func() {
err := tv.tut.Client.FollowRequestDeny(user.Data)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't deny follow request. Error: %v\n", err),
)
return
}
f := tv.GetCurrentFeed()
f.Delete()
tv.RedrawContent()
})
return nil
}
if tv.tut.Config.Input.UserAvatar.Match(event.Key(), event.Rune()) {
openAvatar(tv, *user.Data)
return nil
}
if tv.tut.Config.Input.UserBlock.Match(event.Key(), event.Rune()) {
txt := "block"
if blocking {
txt = "unblock"
}
tv.ModalView.Run(fmt.Sprintf("Do you want to %s this user?", txt),
func() {
rel, err := tv.tut.Client.BlockToggle(user)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't block user. Error: %v\n", err),
)
return
}
user.Relation = rel
tv.RedrawControls()
})
return nil
}
if tv.tut.Config.Input.UserFollow.Match(event.Key(), event.Rune()) {
txt := "follow"
if following {
txt = "unfollow"
}
tv.ModalView.Run(fmt.Sprintf("Do you want to %s this user?", txt),
func() {
rel, err := tv.tut.Client.FollowToggle(user)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't follow user. Error: %v\n", err),
)
return
}
user.Relation = rel
tv.RedrawControls()
})
return nil
}
if tv.tut.Config.Input.UserMute.Match(event.Key(), event.Rune()) {
txt := "mute"
if muting {
txt = "unmute"
}
tv.ModalView.Run(fmt.Sprintf("Do you want to %s this user?", txt),
func() {
rel, err := tv.tut.Client.MuteToggle(user)
if err != nil {
tv.ShowError(
fmt.Sprintf("Couldn't follow user. Error: %v\n", err),
)
return
}
user.Relation = rel
tv.RedrawControls()
})
return nil
}
if tv.tut.Config.Input.UserLinks.Match(event.Key(), event.Rune()) {
tv.SetPage(LinkFocus)
return nil
}
if tv.tut.Config.Input.UserUser.Match(event.Key(), event.Rune()) {
tv.Timeline.AddFeed(NewUserFeed(tv, api.NewUserItem(user, true)))
return nil
}
if tv.tut.Config.Input.UserViewFocus.Match(event.Key(), event.Rune()) {
tv.SetPage(ViewFocus)
return nil
}
if tv.tut.Config.Input.UserYank.Match(event.Key(), event.Rune()) {
copyToClipboard(user.Data.URL)
return nil
}
if tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) {
tv.Timeline.AddFeed(NewUserFeed(tv, api.NewUserItem(user, true)))
return nil
}
return event
}
func (tv *TutView) InputList(event *tcell.EventKey, list *mastodon.List) *tcell.EventKey {
if tv.tut.Config.Input.ListOpenFeed.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) {
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
}
func (tv *TutView) InputLinkView(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.LinkView.Next()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.LinkView.Prev()
return nil
}
if tv.tut.Config.Input.LinkOpen.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) {
tv.LinkView.Open()
return nil
}
if tv.tut.Config.Input.LinkYank.Match(event.Key(), event.Rune()) {
tv.LinkView.Yank()
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(MainFocus)
return nil
}
if event.Key() == tcell.KeyRune {
switch event.Rune() {
case '1', '2', '3', '4', '5':
s := string(event.Rune())
i, _ := strconv.Atoi(s)
tv.LinkView.OpenCustom(i)
return nil
}
}
return event
}
func (tv *TutView) InputComposeView(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.ComposeEditSpoiler.Match(event.Key(), event.Rune()) {
tv.ComposeView.EditSpoiler()
return nil
}
if tv.tut.Config.Input.ComposeEditText.Match(event.Key(), event.Rune()) {
tv.ComposeView.EditText()
return nil
}
if tv.tut.Config.Input.ComposeIncludeQuote.Match(event.Key(), event.Rune()) {
tv.ComposeView.IncludeQuote()
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
}
if tv.tut.Config.Input.ComposeToggleContentWarning.Match(event.Key(), event.Rune()) {
tv.ComposeView.ToggleCW()
return nil
}
if tv.tut.Config.Input.ComposeVisibility.Match(event.Key(), event.Rune()) {
tv.ComposeView.FocusVisibility()
return nil
}
if tv.tut.Config.Input.ComposeLanguage.Match(event.Key(), event.Rune()) {
tv.ComposeView.FocusLang()
return nil
}
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.ModalView.Run(
"Do you want exit the compose view?", func() {
tv.FocusMainNoHistory()
})
return nil
}
return event
}
func (tv *TutView) InputMedia(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.ComposeView.media.Next()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.ComposeView.media.Prev()
return nil
}
if tv.tut.Config.Input.MediaDelete.Match(event.Key(), event.Rune()) {
tv.ComposeView.media.Delete()
return nil
}
if tv.tut.Config.Input.MediaEditDesc.Match(event.Key(), event.Rune()) {
tv.ComposeView.media.EditDesc()
return nil
}
if tv.tut.Config.Input.MediaAdd.Match(event.Key(), event.Rune()) {
tv.SetPage(MediaAddFocus)
tv.ComposeView.media.SetFocus(false)
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) InputMediaAdd(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyRune {
tv.ComposeView.input.AddRune(event.Rune())
return nil
}
switch event.Key() {
case tcell.KeyTAB:
tv.ComposeView.input.AutocompleteTab()
return nil
case tcell.KeyDown:
tv.ComposeView.input.AutocompleteNext()
return nil
case tcell.KeyBacktab, tcell.KeyUp:
tv.ComposeView.input.AutocompletePrev()
return nil
case tcell.KeyEnter:
tv.ComposeView.input.CheckDone()
return nil
case tcell.KeyEsc:
tv.SetPage(MediaFocus)
tv.ComposeView.media.SetFocus(true)
return nil
}
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()
return nil
}
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.VoteView.Prev()
return nil
}
if tv.tut.Config.Input.VoteVote.Match(event.Key(), event.Rune()) {
tv.VoteView.Vote()
return nil
}
if tv.tut.Config.Input.VoteSelect.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) {
tv.VoteView.ToggleSelect()
return nil
}
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.FocusMainNoHistory()
return nil
}
return event
}
func (tv *TutView) InputPreference(event *tcell.EventKey) *tcell.EventKey {
if tv.PreferenceView.HasFieldFocus() {
return tv.InputPreferenceFields(event)
}
if tv.tut.Config.Input.PreferenceFields.Match(event.Key(), event.Rune()) {
tv.PreferenceView.FieldFocus()
return nil
}
if tv.tut.Config.Input.PreferenceName.Match(event.Key(), event.Rune()) {
tv.PreferenceView.EditDisplayname()
return nil
}
if tv.tut.Config.Input.PreferenceVisibility.Match(event.Key(), event.Rune()) {
tv.PreferenceView.FocusVisibility()
return nil
}
if tv.tut.Config.Input.PreferenceBio.Match(event.Key(), event.Rune()) {
tv.PreferenceView.EditBio()
return nil
}
if tv.tut.Config.Input.PreferenceSave.Match(event.Key(), event.Rune()) {
tv.PreferenceView.Save()
return nil
}
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.ModalView.Run(
"Do you want exit the preference view?", func() {
tv.FocusMainNoHistory()
})
return nil
}
return event
}
func (tv *TutView) InputPreferenceFields(event *tcell.EventKey) *tcell.EventKey {
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) {
tv.PreferenceView.PrevField()
return nil
}
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) {
tv.PreferenceView.NextField()
return nil
}
if tv.tut.Config.Input.PreferenceFieldsAdd.Match(event.Key(), event.Rune()) {
tv.PreferenceView.AddField()
return nil
}
if tv.tut.Config.Input.PreferenceFieldsEdit.Match(event.Key(), event.Rune()) {
tv.PreferenceView.EditField()
return nil
}
if tv.tut.Config.Input.PreferenceFieldsDelete.Match(event.Key(), event.Rune()) {
tv.PreferenceView.DeleteField()
return nil
}
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) ||
tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) {
tv.PreferenceView.MainFocus()
return nil
}
return event
}
func (tv *TutView) InputCmdView(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyEnter:
tv.Shared.Bottom.Cmd.DoneFunc(tcell.KeyEnter)
case tcell.KeyEsc:
tv.Shared.Bottom.Cmd.Back()
tv.Shared.Bottom.Cmd.View.Autocomplete()
return nil
}
return event
}
func (tv *TutView) MouseInput(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
if event == nil {
return nil, action
}
switch action {
case tview.MouseLeftUp, tview.MouseMiddleUp, tview.MouseRightUp:
return event, action
}
switch tv.PageFocus {
case ViewFocus, MainFocus:
return tv.MouseInputMainView(event, action)
case LoginFocus:
tv.MouseInputLoginView(event, action)
case LinkFocus:
return tv.MouseInputLinkView(event, action)
case MediaFocus:
return tv.MouseInputMediaView(event, action)
case VoteFocus:
return tv.MouseInputVoteView(event, action)
case ModalFocus:
tv.MouseInputModalView(event, action)
case PollFocus:
return tv.MouseInputPollView(event, action)
case HelpFocus:
return tv.MouseInputHelpView(event, action)
case ComposeFocus:
return tv.MouseInputComposeView(event, action)
case PreferenceFocus:
return tv.MouseInputPreferenceView(event, action)
}
return nil, action
}
func (tv *TutView) feedListMouse(list *tview.List, i int, event *tcell.EventMouse, action tview.MouseAction) {
tv.SetPage(MainFocus)
tv.FocusFeed(i)
mh := list.MouseHandler()
if mh == nil {
return
}
lastIndex := list.GetCurrentItem()
mh(action, event, func(p tview.Primitive) {})
newIndex := list.GetCurrentItem()
if lastIndex != newIndex {
tv.Timeline.SetItemFeedIndex(newIndex)
}
}
var scrollSleepTime time.Duration = 150
type scrollSleep struct {
mux sync.Mutex
last time.Time
next func()
prev func()
}
func NewScrollSleep(next func(), prev func()) *scrollSleep {
return &scrollSleep{
next: next,
prev: prev,
}
}
func (sc *scrollSleep) Action(list *tview.List, action tview.MouseAction) {
mh := list.MouseHandler()
if mh == nil {
return
}
lock := sc.mux.TryLock()
if !lock {
return
}
if time.Since(sc.last) < (scrollSleepTime * time.Millisecond) {
sc.mux.Unlock()
return
}
if action == tview.MouseScrollDown {
sc.next()
}
if action == tview.MouseScrollUp {
sc.prev()
}
sc.last = time.Now()
sc.mux.Unlock()
}
func (tv *TutView) MouseInputMainView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseScrollDown, tview.MouseScrollUp:
f := tv.GetCurrentFeed()
if f.Content.Main.InRect(x, y) {
if action == tview.MouseScrollDown {
tv.Timeline.ScrollDown()
return nil, action
}
if action == tview.MouseScrollUp {
tv.Timeline.ScrollUp()
return nil, action
}
}
for _, tl := range tv.Timeline.Feeds {
fl := tl.GetFeedList()
if fl.Text.InRect(x, y) {
tv.Timeline.scrollSleep.Action(fl.Text, action)
return nil, action
}
if fl.Symbol.InRect(x, y) {
tv.Timeline.scrollSleep.Action(fl.Symbol, action)
return nil, action
}
}
case tview.MouseLeftClick:
f := tv.GetCurrentFeed()
if f.Content.Main.InRect(x, y) {
tv.SetPage(ViewFocus)
return nil, action
}
if f.Content.Controls.InRect(x, y) {
return event, action
}
for i, tl := range tv.Timeline.Feeds {
fl := tl.GetFeedList()
if fl.Text.InRect(x, y) {
tv.feedListMouse(fl.Text, i, event, action)
return nil, action
}
if fl.Symbol.InRect(x, y) {
tv.feedListMouse(fl.Symbol, i, event, action)
return nil, action
}
}
}
return nil, action
}
func (tv *TutView) MouseInputLoginView(event *tcell.EventMouse, action tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
list := tv.LoginView.list
if !list.InRect(x, y) {
return
}
mh := list.MouseHandler()
if mh == nil {
return
}
mh(action, event, func(p tview.Primitive) {})
tv.LoginView.Selected()
case tview.MouseScrollDown, tview.MouseScrollUp:
tv.LoginView.scrollSleep.Action(tv.LoginView.list, action)
}
}
func (tv *TutView) MouseInputLinkView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.LinkView.controls.InRect(x, y) {
return event, action
}
list := tv.LinkView.list
if !list.InRect(x, y) {
return nil, action
}
mh := list.MouseHandler()
if mh == nil {
return nil, action
}
mh(action, event, func(p tview.Primitive) {})
tv.LinkView.Open()
case tview.MouseScrollDown, tview.MouseScrollUp:
tv.LinkView.scrollSleep.Action(tv.LinkView.list, action)
}
return nil, action
}
func (tv *TutView) MouseInputMediaView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.ComposeView.controls.InRect(x, y) {
return event, action
}
list := tv.ComposeView.media.list
if !list.InRect(x, y) {
return nil, action
}
mh := list.MouseHandler()
if mh == nil {
return nil, action
}
mh(action, event, func(p tview.Primitive) {})
case tview.MouseScrollDown, tview.MouseScrollUp:
tv.ComposeView.media.scrollSleep.Action(tv.ComposeView.media.list, action)
}
return nil, action
}
func (tv *TutView) MouseInputVoteView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.VoteView.controls.InRect(x, y) {
return event, action
}
list := tv.VoteView.list
if !list.InRect(x, y) {
return nil, action
}
mh := list.MouseHandler()
if mh == nil {
return nil, action
}
mh(action, event, func(p tview.Primitive) {})
tv.VoteView.ToggleSelect()
case tview.MouseScrollDown, tview.MouseScrollUp:
tv.VoteView.scrollSleep.Action(tv.VoteView.list, action)
}
return nil, action
}
func (tv *TutView) MouseInputModalView(event *tcell.EventMouse, action tview.MouseAction) {
switch action {
case tview.MouseLeftClick:
modal := tv.ModalView.View
mh := modal.MouseHandler()
if mh == nil {
return
}
mh(action, event, func(p tview.Primitive) {})
}
}
func (tv *TutView) MouseInputPollView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.PollView.controls.InRect(x, y) {
return event, action
}
list := tv.PollView.list
if !list.InRect(x, y) {
return nil, action
}
mh := list.MouseHandler()
if mh == nil {
return nil, action
}
mh(action, event, func(p tview.Primitive) {})
case tview.MouseScrollDown, tview.MouseScrollUp:
tv.PollView.scrollSleep.Action(tv.PollView.list, action)
}
return nil, action
}
func (tv *TutView) MouseInputHelpView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.HelpView.controls.InRect(x, y) {
return event, action
}
}
return nil, action
}
func (tv *TutView) MouseInputPreferenceView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.PreferenceView.controls.InRect(x, y) {
return event, action
}
}
return nil, action
}
func (tv *TutView) MouseInputComposeView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
x, y := event.Position()
switch action {
case tview.MouseLeftClick:
if tv.ComposeView.controls.InRect(x, y) {
return event, action
}
}
return nil, action
}