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.
617 lines
16 KiB
617 lines
16 KiB
package ui |
|
|
|
import ( |
|
"fmt" |
|
"strconv" |
|
|
|
"github.com/RasmusLindroth/go-mastodon" |
|
"github.com/RasmusLindroth/tut/api" |
|
"github.com/RasmusLindroth/tut/util" |
|
"github.com/gdamore/tcell/v2" |
|
) |
|
|
|
func (tv *TutView) Input(event *tcell.EventKey) *tcell.EventKey { |
|
if tv.PageFocus != LoginFocus { |
|
switch event.Rune() { |
|
case ':': |
|
tv.SetPage(CmdFocus) |
|
case '?': |
|
tv.SetPage(HelpFocus) |
|
} |
|
} |
|
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 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) |
|
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 |
|
} |
|
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 { |
|
mainFocus := tv.TimelineFocus == FeedFocus |
|
|
|
if tv.tut.Config.Input.MainHome.Match(event.Key(), event.Rune()) { |
|
tv.Timeline.HomeItemFeed(mainFocus) |
|
return nil |
|
} |
|
if tv.tut.Config.Input.MainEnd.Match(event.Key(), event.Rune()) { |
|
tv.Timeline.EndItemFeed(mainFocus) |
|
return nil |
|
} |
|
if tv.tut.Config.Input.MainPrevFeed.Match(event.Key(), event.Rune()) { |
|
if mainFocus { |
|
tv.Timeline.PrevFeed() |
|
} |
|
return nil |
|
} |
|
if tv.tut.Config.Input.MainNextFeed.Match(event.Key(), event.Rune()) { |
|
if mainFocus { |
|
tv.Timeline.NextFeed() |
|
} |
|
return nil |
|
} |
|
if tv.tut.Config.Input.GlobalDown.Match(event.Key(), event.Rune()) { |
|
tv.Timeline.NextItemFeed(mainFocus) |
|
return nil |
|
} |
|
if tv.tut.Config.Input.GlobalUp.Match(event.Key(), event.Rune()) { |
|
tv.Timeline.PrevItemFeed(mainFocus) |
|
return nil |
|
} |
|
if tv.tut.Config.Input.MainNotificationFocus.Match(event.Key(), event.Rune()) { |
|
if tv.tut.Config.General.NotificationFeed { |
|
tv.FocusNotification() |
|
} |
|
return nil |
|
} |
|
if tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) { |
|
if mainFocus { |
|
tv.Timeline.RemoveCurrent(true) |
|
} else { |
|
tv.FocusFeed() |
|
} |
|
return nil |
|
} |
|
if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) { |
|
if mainFocus { |
|
tv.Timeline.RemoveCurrent(false) |
|
} else { |
|
tv.FocusFeed() |
|
} |
|
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 { |
|
item, err := tv.GetCurrentItem() |
|
if err != nil { |
|
return event |
|
} |
|
if tv.tut.Config.Input.MainCompose.Match(event.Key(), event.Rune()) { |
|
tv.InitPost(nil) |
|
return nil |
|
} |
|
switch item.Type() { |
|
case api.StatusType: |
|
return tv.InputStatus(event, item, item.Raw().(*mastodon.Status)) |
|
case api.UserType, api.ProfileType: |
|
return tv.InputUser(event, item.Raw().(*api.User)) |
|
case api.NotificationType: |
|
nd := item.Raw().(*api.NotificationData) |
|
switch nd.Item.Type { |
|
case "follow": |
|
return tv.InputUser(event, nd.User.Raw().(*api.User)) |
|
case "favourite": |
|
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status)) |
|
case "reblog": |
|
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status)) |
|
case "mention": |
|
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status)) |
|
case "status": |
|
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status)) |
|
case "poll": |
|
return tv.InputStatus(event, nd.Status, nd.Status.Raw().(*mastodon.Status)) |
|
case "follow_request": |
|
return tv.InputUser(event, nd.User.Raw().(*api.User)) |
|
} |
|
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) *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()) { |
|
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.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) |
|
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()) { |
|
user, err := tv.tut.Client.GetUserByID(status.Account.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(status.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) InputUser(event *tcell.EventKey, user *api.User) *tcell.EventKey { |
|
blocking := user.Relation.Blocking |
|
muting := user.Relation.Muting |
|
following := user.Relation.Following |
|
|
|
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 |
|
} |
|
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()) { |
|
tv.SetPage(MediaFocus) |
|
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.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) 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) 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 |
|
}
|
|
|