diff --git a/README.md b/README.md index 6ea33a3..c6caa31 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ # Tut - a Mastodon TUI -A TUI for Mastodon under development. +A TUI for Mastodon with vim inspired keys. The program misses some features but they will be added when I get time. + +![Preview](./images/preview.png "Preview") + +Currently supported commands +* `:q` `:quit` exit +* `:timeline` home, local, federated, direct + +Explanation of the non obvious keys when viewing a toot +* `V` = view. In this mode you can scroll throught the text of the toot if it doesn't fit the screen +* `O` = open. Gives you a list of all URLs in the toot. Opens them in your default browser. +* `M` = media. Opens the media with `xdg-open`. + +On my TODO-list: +* Support for config files (theme, default image/video viewer) +* Multiple accounts +* View users profiles +* Support search +* Support tags +* Support lists +* Notifications +* Better error handling (in other words, don't crash the whole program) diff --git a/app.go b/app.go index a4350ac..58f49d2 100644 --- a/app.go +++ b/app.go @@ -10,5 +10,6 @@ type App struct { UI *UI Me *mastodon.Account API *API + Config *Config HaveAccount bool } diff --git a/config.go b/config.go index 8e2008e..c82a152 100644 --- a/config.go +++ b/config.go @@ -3,10 +3,39 @@ package main import ( "os" + "github.com/gdamore/tcell" "github.com/kyoh86/xdg" ) type Config struct { + Style StyleConfig + Media MediaConfig +} + +type StyleConfig struct { + Background tcell.Color + Text tcell.Color + + Subtle tcell.Color + WarningText tcell.Color + + TextSpecial1 tcell.Color + TextSpecial2 tcell.Color + + TopBarBackground tcell.Color + TopBarText tcell.Color + + StatusBarBackground tcell.Color + StatusBarText tcell.Color + + ListSelectedBackground tcell.Color + ListSelectedText tcell.Color +} + +type MediaConfig struct { + ImageViewer string + ImageSingle bool + VideoViewer string } func CreateConfigDir() error { diff --git a/go.mod b/go.mod index 50dc060..851576c 100644 --- a/go.mod +++ b/go.mod @@ -9,5 +9,5 @@ require ( github.com/microcosm-cc/bluemonday v1.0.2 github.com/pelletier/go-toml v1.6.0 github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498 - golang.org/x/net v0.0.0-20200301022130-244492dfa37a + golang.org/x/net v0.0.0-20200320220750-118fecf932d8 ) diff --git a/go.sum b/go.sum index c6371ce..5a9f229 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/images/preview.png b/images/preview.png new file mode 100644 index 0000000..451a575 Binary files /dev/null and b/images/preview.png differ diff --git a/linkoverlay.go b/linkoverlay.go index c862984..11da908 100644 --- a/linkoverlay.go +++ b/linkoverlay.go @@ -1,118 +1,79 @@ package main import ( - "fmt" - "log" - "strings" - + "github.com/gdamore/tcell" "github.com/rivo/tview" ) -func NewLinkOverlay(app *App, view *tview.TextView, controls *Controls) *LinkOverlay { - return &LinkOverlay{ - app: app, - View: view, - Controls: controls, - hints: generateHints(), - Scroll: false, +func NewLinkOverlay(app *App) *LinkOverlay { + l := &LinkOverlay{ + app: app, + Flex: tview.NewFlex(), + TextBottom: tview.NewTextView(), + List: tview.NewList(), } + l.TextBottom.SetText("[O]pen") + return l } type LinkOverlay struct { - app *App - View *tview.TextView - Controls *Controls - URLs []URL - hints []string - input string - Scroll bool + app *App + Flex *tview.Flex + TextBottom *tview.TextView + List *tview.List + urls []URL } -func generateHints() []string { - //TODO: REMOVE T - chars := strings.Split("asdfghjkl", "") - if len(chars) == 0 { - chars = strings.Split("asdfghjkl", "") +func (l *LinkOverlay) SetURLs(urls []URL) { + l.urls = urls + l.List.Clear() + for _, url := range urls { + l.List.AddItem(url.Text, "", 0, nil) } +} - var one []string - var two []string - var three []string - - for _, a := range chars { - one = append(one, a) - for _, b := range chars { - if b == a { - continue - } - two = append(two, a+b) - for _, c := range chars { - if c == b { - continue - } - three = append(three, a+b+c) - } - } +func (l *LinkOverlay) Prev() { + index := l.List.GetCurrentItem() + if index-1 >= 0 { + l.List.SetCurrentItem(index - 1) } - return append(one, append(two, three...)...) } -func (l *LinkOverlay) SetURLs(urls []URL) { - l.DisableScroll() - l.input = "" - l.URLs = urls +func (l *LinkOverlay) Next() { + index := l.List.GetCurrentItem() + if index+1 < l.List.GetItemCount() { + l.List.SetCurrentItem(index + 1) + } } -func (l *LinkOverlay) Draw() { - if len(l.hints) < len(l.URLs) { - log.Fatalln("No hints") +func (l *LinkOverlay) Open() { + index := l.List.GetCurrentItem() + if len(l.urls) == 0 || index >= len(l.urls) { return } - var output string - for i, url := range l.URLs { - hint := tview.Escape( - fmt.Sprintf("[%s]", l.hints[i]), - ) - output += fmt.Sprintf("%s %s\n", hint, - tview.Escape(url.URL)) - } - - l.View.SetText(output) - if l.Scroll { - l.Controls.View.SetText(tview.Escape("\n--SCROLL-- [t]oggle scroll")) - } else { - l.Controls.View.SetText(tview.Escape("\n--OPEN-- [t]oggle scroll")) - } + openURL(l.urls[index].URL) } -func (l *LinkOverlay) AddRune(r rune) { - l.input += string(r) - l.Controls.View.SetText(tview.Escape(l.input + "\n--OPEN-- [t]oggle scroll")) - for i, key := range l.hints { - if key == l.input && i < len(l.URLs) { - openURL(l.URLs[i].URL) - l.Clear() +func (l *LinkOverlay) InputHandler(event *tcell.EventKey) { + if event.Key() == tcell.KeyRune { + switch event.Rune() { + case 'j', 'J': + l.Next() + case 'k', 'K': + l.Prev() + case 'o', 'O': + l.Open() + case 'q', 'Q': + l.app.UI.SetFocus(LeftPaneFocus) + } + } else { + switch event.Key() { + case tcell.KeyUp: + l.Prev() + case tcell.KeyDown: + l.Next() + case tcell.KeyEsc: l.app.UI.SetFocus(LeftPaneFocus) } } } - -func (l *LinkOverlay) ActivateScroll() { - l.Scroll = true - l.Controls.View.SetText(tview.Escape("\n--SCROLL-- [t]oggle scroll")) -} - -func (l *LinkOverlay) DisableScroll() { - l.Scroll = false - l.input = "" - l.Controls.View.SetText(tview.Escape("\n--OPEN-- [t]oggle scroll")) -} - -func (l *LinkOverlay) HasInput() bool { - return l.input != "" -} - -func (l *LinkOverlay) Clear() { - l.input = "" - l.Controls.View.SetText(tview.Escape(l.input + "\n--OPEN-- [t]oggle scroll")) -} diff --git a/main.go b/main.go index c67950e..513dfec 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,23 @@ import ( ) func main() { + config := Config{ + Style: StyleConfig{ + Background: tcell.ColorDefault, + Text: tcell.ColorWhite, + Subtle: tcell.ColorGray, + WarningText: tcell.NewRGBColor(249, 38, 114), + TextSpecial1: tcell.NewRGBColor(174, 129, 255), + TextSpecial2: tcell.NewRGBColor(166, 226, 46), + TopBarBackground: tcell.NewRGBColor(249, 38, 114), + TopBarText: tcell.ColorWhite, + StatusBarBackground: tcell.NewRGBColor(249, 38, 114), + StatusBarText: tcell.ColorWhite, + ListSelectedBackground: tcell.NewRGBColor(249, 38, 114), + ListSelectedText: tcell.ColorWhite, + }, + } + err := CreateConfigDir() if err != nil { log.Fatalln(err) @@ -22,8 +39,31 @@ func main() { App: tview.NewApplication(), API: &API{}, HaveAccount: false, + Config: &config, } + clearContent := func(screen tcell.Screen, x int, y int, width int, height int) (int, int, int, int) { + for cx := x; cx < width+x; cx++ { + for cy := y; cy < height+y; cy++ { + screen.SetContent(cx, cy, ' ', nil, tcell.StyleDefault.Background(app.Config.Style.Background)) + } + } + y2 := y + height + for cx := x + 1; cx < width+x; cx++ { + screen.SetContent(cx, y, tview.BoxDrawingsLightHorizontal, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + screen.SetContent(cx, y2, tview.BoxDrawingsLightHorizontal, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + } + x2 := x + width + for cy := y + 1; cy < height+y; cy++ { + screen.SetContent(x, cy, tview.BoxDrawingsLightVertical, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + screen.SetContent(x2, cy, tview.BoxDrawingsLightVertical, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + } + screen.SetContent(x, y, tview.BoxDrawingsLightDownAndRight, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + screen.SetContent(x, y+height, tview.BoxDrawingsLightUpAndRight, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + screen.SetContent(x+width, y, tview.BoxDrawingsLightDownAndLeft, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + screen.SetContent(x+width, y+height, tview.BoxDrawingsLightUpAndLeft, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) + return x + 1, y + 1, width - 1, height - 1 + } if exists { accounts, err := GetAccounts(path) if err != nil { @@ -52,16 +92,17 @@ func main() { tview.Borders.BottomRightFocus = tview.BoxDrawingsLightUpAndLeft top := tview.NewTextView() - top.SetBackgroundColor(tcell.ColorGreen) + top.SetBackgroundColor(app.Config.Style.TopBarBackground) + top.SetTextColor(app.Config.Style.TopBarText) app.UI = &UI{app: app, Top: top, Timeline: TimelineHome} app.UI.TootList = NewTootList(app, tview.NewList()) - app.UI.TootList.View.SetSelectedTextColor(tcell.ColorWhite) - app.UI.TootList.View.SetSelectedBackgroundColor(tcell.ColorRed) + app.UI.TootList.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.TootList.View.SetSelectedTextColor(app.Config.Style.ListSelectedText) + app.UI.TootList.View.SetSelectedBackgroundColor(app.Config.Style.ListSelectedBackground) app.UI.TootList.View.ShowSecondaryText(false) app.UI.TootList.View.SetHighlightFullLine(true) - app.UI.TootList.View.SetBackgroundColor(tcell.ColorDefault) app.UI.TootList.View.SetChangedFunc(func(index int, _ string, _ string, _ rune) { if app.HaveAccount { @@ -69,36 +110,34 @@ func main() { } }) - lo := NewLinkOverlay(app, tview.NewTextView(), - NewControls(app, tview.NewTextView()), - ) - lo.View.SetBorderPadding(0, 0, 0, 0) - app.UI.StatusText = NewStatusText(app, tview.NewTextView(), - NewControls(app, tview.NewTextView()), lo, + NewControls(app, tview.NewTextView()), NewLinkOverlay(app), ) app.UI.StatusText.View.SetWordWrap(true).SetDynamicColors(true) - app.UI.StatusText.View.SetBackgroundColor(tcell.ColorDefault) + app.UI.StatusText.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.StatusText.View.SetTextColor(app.Config.Style.Text) app.UI.StatusText.Controls.View.SetDynamicColors(true) - app.UI.StatusText.Controls.View.SetBackgroundColor(tcell.ColorDefault) + app.UI.StatusText.Controls.View.SetBackgroundColor(app.Config.Style.Background) app.UI.CmdBar = NewCmdBar(app, tview.NewInputField(), ) - app.UI.CmdBar.View.SetFieldBackgroundColor(tcell.ColorDefault) + app.UI.CmdBar.View.SetFieldBackgroundColor(app.Config.Style.Background) + app.UI.CmdBar.View.SetFieldTextColor(app.Config.Style.Text) app.UI.Status = tview.NewTextView() - app.UI.Status.SetBackgroundColor(tcell.ColorBrown) + app.UI.Status.SetBackgroundColor(app.Config.Style.StatusBarBackground) + app.UI.Status.SetTextColor(app.Config.Style.StatusBarText) - verticalLine := tview.NewBox().SetBackgroundColor(tcell.ColorDefault) + verticalLine := tview.NewBox().SetBackgroundColor(app.Config.Style.Background) verticalLine.SetDrawFunc(func(screen tcell.Screen, x int, y int, width int, height int) (int, int, int, int) { for cy := y; cy < y+height; cy++ { - screen.SetContent(x, cy, tview.BoxDrawingsLightVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) + screen.SetContent(x, cy, tview.BoxDrawingsLightVertical, nil, tcell.StyleDefault.Foreground(app.Config.Style.Subtle)) } return 0, 0, 0, 0 }) app.UI.Pages = tview.NewPages() - app.UI.Pages.SetBackgroundColor(tcell.ColorDefault) + app.UI.Pages.SetBackgroundColor(app.Config.Style.Background) app.UI.Pages.AddPage("main", tview.NewFlex(). AddItem(tview.NewFlex().SetDirection(tview.FlexRow). @@ -106,7 +145,7 @@ func main() { AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). AddItem(app.UI.TootList.View, 0, 2, false). AddItem(verticalLine, 1, 0, false). - AddItem(tview.NewBox().SetBackgroundColor(tcell.ColorDefault), 1, 0, false). + AddItem(tview.NewBox().SetBackgroundColor(app.Config.Style.Background), 1, 0, false). AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(app.UI.StatusText.View, 0, 9, false). AddItem(app.UI.StatusText.Controls.View, 1, 0, false), @@ -146,24 +185,33 @@ func main() { app.UI.MessageBox = NewMessageBox(app, tview.NewTextView(), NewControls(app, tview.NewTextView()), ) - //app.UI.MessageBox.View.SetBorder(true).SetTitle("New toot") - app.UI.MessageBox.View.SetBackgroundColor(tcell.ColorDefault) + + app.UI.MessageBox.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.MessageBox.View.SetTextColor(app.Config.Style.Text) app.UI.MessageBox.View.SetDynamicColors(true) app.UI.MessageBox.Controls.View.SetDynamicColors(true) - app.UI.MessageBox.Controls.View.SetBackgroundColor(tcell.ColorDefault) + app.UI.MessageBox.Controls.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.MessageBox.Controls.View.SetTextColor(app.Config.Style.Text) app.UI.Pages.AddPage("toot", modal(flToot, app.UI.MessageBox.View, app.UI.MessageBox.Controls.View), true, false) - //app.UI.StatusText.LinkOverlay.View.SetBorder(true).SetTitle("Follow link") - app.UI.StatusText.LinkOverlay.View.SetBackgroundColor(tcell.ColorDefault) - app.UI.StatusText.LinkOverlay.View.SetDynamicColors(true) - app.UI.StatusText.LinkOverlay.Controls.View.SetDynamicColors(true) - app.UI.StatusText.LinkOverlay.Controls.View.SetBackgroundColor(tcell.ColorDefault) - - links := modal(flLinks, app.UI.StatusText.LinkOverlay.View, app.UI.StatusText.LinkOverlay.Controls.View) + app.UI.Pages.AddPage("links", tview.NewFlex().AddItem(nil, 0, 1, false). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(nil, 0, 1, false). + AddItem(app.UI.StatusText.LinkOverlay.Flex.SetDirection(tview.FlexRow). + AddItem(app.UI.StatusText.LinkOverlay.List, 0, 10, true). + AddItem(app.UI.StatusText.LinkOverlay.TextBottom, 1, 1, true), 0, 8, false). + AddItem(nil, 0, 1, false), 0, 8, true). + AddItem(nil, 0, 1, false), true, false) - app.UI.Pages.AddPage("links", - links, true, false) + app.UI.StatusText.LinkOverlay.Flex.SetDrawFunc(clearContent) + app.UI.StatusText.LinkOverlay.TextBottom.SetBackgroundColor(app.Config.Style.Background) + app.UI.StatusText.LinkOverlay.List.SetBackgroundColor(app.Config.Style.Background) + app.UI.StatusText.LinkOverlay.List.SetMainTextColor(app.Config.Style.Text) + app.UI.StatusText.LinkOverlay.List.SetSelectedBackgroundColor(app.Config.Style.ListSelectedBackground) + app.UI.StatusText.LinkOverlay.List.SetSelectedTextColor(app.Config.Style.ListSelectedText) + app.UI.StatusText.LinkOverlay.List.ShowSecondaryText(false) + app.UI.StatusText.LinkOverlay.List.SetHighlightFullLine(true) app.UI.AuthOverlay = NewAuthoverlay(app, tview.NewFlex(), tview.NewInputField(), NewControls(app, tview.NewTextView())) @@ -171,11 +219,12 @@ func main() { app.UI.Pages.AddPage("login", authModal(app.UI.AuthOverlay.Flex, app.UI.AuthOverlay.View, app.UI.AuthOverlay.Controls.View), true, false) app.UI.AuthOverlay.Flex.SetDrawFunc(clearContent) - app.UI.AuthOverlay.Flex.SetBackgroundColor(tcell.ColorDefault) - app.UI.AuthOverlay.View.SetBackgroundColor(tcell.ColorDefault) - app.UI.AuthOverlay.View.SetFieldBackgroundColor(tcell.ColorDefault) - app.UI.AuthOverlay.View.SetFieldTextColor(tcell.ColorWhite) - app.UI.AuthOverlay.Controls.View.SetBackgroundColor(tcell.ColorDefault) + app.UI.AuthOverlay.Flex.SetBackgroundColor(app.Config.Style.Background) + app.UI.AuthOverlay.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.AuthOverlay.View.SetFieldBackgroundColor(app.Config.Style.Background) + app.UI.AuthOverlay.View.SetFieldTextColor(app.Config.Style.Text) + app.UI.AuthOverlay.Controls.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.AuthOverlay.Controls.View.SetTextColor(app.Config.Style.Text) app.UI.AuthOverlay.Draw() app.UI.MediaOverlay = NewMediaView(app) @@ -183,24 +232,28 @@ func main() { AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(nil, 0, 1, false). AddItem(app.UI.MediaOverlay.Flex.SetDirection(tview.FlexRow). - AddItem(app.UI.MediaOverlay.TextTop, 0, 1, true). + AddItem(app.UI.MediaOverlay.TextTop, 1, 1, true). AddItem(app.UI.MediaOverlay.FileList, 0, 10, true). - AddItem(app.UI.MediaOverlay.TextBottom, 0, 1, true). + AddItem(app.UI.MediaOverlay.TextBottom, 1, 1, true). AddItem(app.UI.MediaOverlay.InputField.View, 2, 1, false), 0, 8, false). AddItem(nil, 0, 1, false), 0, 8, true). AddItem(nil, 0, 1, false), true, false) - app.UI.MediaOverlay.FileList.SetSelectedTextColor(tcell.ColorWhite) - app.UI.MediaOverlay.FileList.SetSelectedBackgroundColor(tcell.ColorRed) + app.UI.MediaOverlay.FileList.SetBackgroundColor(app.Config.Style.Background) + app.UI.MediaOverlay.FileList.SetMainTextColor(app.Config.Style.Text) + app.UI.MediaOverlay.FileList.SetSelectedBackgroundColor(app.Config.Style.ListSelectedBackground) + app.UI.MediaOverlay.FileList.SetSelectedTextColor(app.Config.Style.ListSelectedText) app.UI.MediaOverlay.FileList.ShowSecondaryText(false) app.UI.MediaOverlay.FileList.SetHighlightFullLine(true) - app.UI.MediaOverlay.FileList.SetBackgroundColor(tcell.ColorDefault) - app.UI.MediaOverlay.Flex.SetBackgroundColor(tcell.ColorDefault) - app.UI.MediaOverlay.TextTop.SetBackgroundColor(tcell.ColorDefault) - app.UI.MediaOverlay.TextBottom.SetBackgroundColor(tcell.ColorDefault) - app.UI.MediaOverlay.InputField.View.SetBackgroundColor(tcell.ColorDefault) - app.UI.MediaOverlay.InputField.View.SetFieldBackgroundColor(tcell.ColorDefault) + app.UI.MediaOverlay.Flex.SetBackgroundColor(app.Config.Style.Background) + app.UI.MediaOverlay.TextTop.SetBackgroundColor(app.Config.Style.Background) + app.UI.MediaOverlay.TextTop.SetTextColor(app.Config.Style.Text) + app.UI.MediaOverlay.TextBottom.SetBackgroundColor(app.Config.Style.Background) + app.UI.MediaOverlay.TextBottom.SetTextColor(app.Config.Style.Text) + app.UI.MediaOverlay.InputField.View.SetBackgroundColor(app.Config.Style.Background) + app.UI.MediaOverlay.InputField.View.SetFieldBackgroundColor(app.Config.Style.Background) + app.UI.MediaOverlay.InputField.View.SetFieldTextColor(app.Config.Style.Text) app.UI.MediaOverlay.Flex.SetDrawFunc(clearContent) if !app.HaveAccount { @@ -219,55 +272,8 @@ func main() { } if app.UI.Focus == LinkOverlayFocus { - if !app.UI.StatusText.LinkOverlay.Scroll { - if event.Key() == tcell.KeyRune { - switch event.Rune() { - case 't': - app.UI.StatusText.LinkOverlay.ActivateScroll() - default: - app.UI.StatusText.LinkOverlay.AddRune(event.Rune()) - } - } else { - switch event.Key() { - case tcell.KeyEsc: - if app.UI.StatusText.LinkOverlay.HasInput() { - app.UI.StatusText.LinkOverlay.Clear() - } else { - app.UI.SetFocus(LeftPaneFocus) - app.UI.StatusText.LinkOverlay.DisableScroll() - } - return nil - } - } - return nil - } else { - switch event.Key() { - case tcell.KeyEsc: - app.UI.StatusText.LinkOverlay.DisableScroll() - return nil - } - - } - - if event.Key() == tcell.KeyRune { - switch event.Rune() { - case 't': - app.UI.StatusText.LinkOverlay.DisableScroll() - return nil - case 'q': - app.UI.SetFocus(LeftPaneFocus) - app.UI.StatusText.LinkOverlay.DisableScroll() - return nil - } - } else { - switch event.Key() { - case tcell.KeyEsc: - app.UI.SetFocus(LeftPaneFocus) - app.UI.StatusText.LinkOverlay.DisableScroll() - return nil - } - } - return event + app.UI.StatusText.LinkOverlay.InputHandler(event) + return nil } if app.UI.Focus == CmdBarFocus { @@ -284,22 +290,22 @@ func main() { if app.UI.Focus == MessageFocus { if event.Key() == tcell.KeyRune { switch event.Rune() { - case 'p': + case 'p', 'P': app.UI.MessageBox.Post() return nil - case 'e': + case 'e', 'E': app.UI.MessageBox.EditText() return nil - case 'c': + case 'c', 'C': app.UI.MessageBox.EditSpoiler() return nil - case 't': + case 't', 'T': app.UI.MessageBox.ToggleSpoiler() return nil - case 'm': + case 'm', 'M': app.UI.SetFocus(MessageAttachmentFocus) return nil - case 'q': + case 'q', 'Q': app.UI.SetFocus(LeftPaneFocus) return nil } @@ -316,20 +322,24 @@ func main() { if app.UI.Focus == MessageAttachmentFocus && app.UI.MediaOverlay.Focus == MediaFocusOverview { if event.Key() == tcell.KeyRune { switch event.Rune() { - case 'j': + case 'j', 'J': app.UI.MediaOverlay.Next() - case 'k': + case 'k', 'K': app.UI.MediaOverlay.Prev() - case 'd': + case 'd', 'D': app.UI.MediaOverlay.Delete() - case 'a': + case 'a', 'A': app.UI.MediaOverlay.SetFocus(MediaFocusAdd) - case 'q': + case 'q', 'Q': app.UI.SetFocus(MessageFocus) return nil } } else { switch event.Key() { + case tcell.KeyUp: + app.UI.MediaOverlay.Prev() + case tcell.KeyDown: + app.UI.MediaOverlay.Next() case tcell.KeyEsc: app.UI.SetFocus(MessageFocus) return nil @@ -342,20 +352,19 @@ func main() { if event.Key() == tcell.KeyRune { app.UI.MediaOverlay.InputField.AddRune(event.Rune()) return nil - } else { - switch event.Key() { - case tcell.KeyTab, tcell.KeyDown: - app.UI.MediaOverlay.InputField.AutocompleteNext() - return nil - case tcell.KeyBacktab, tcell.KeyUp: - app.UI.MediaOverlay.InputField.AutocompletePrev() - return nil - case tcell.KeyEnter: - app.UI.MediaOverlay.InputField.CheckDone() - return nil - case tcell.KeyEsc: - app.UI.MediaOverlay.SetFocus(MediaFocusOverview) - } + } + switch event.Key() { + case tcell.KeyTab, tcell.KeyDown: + app.UI.MediaOverlay.InputField.AutocompleteNext() + return nil + case tcell.KeyBacktab, tcell.KeyUp: + app.UI.MediaOverlay.InputField.AutocompletePrev() + return nil + case tcell.KeyEnter: + app.UI.MediaOverlay.InputField.CheckDone() + return nil + case tcell.KeyEsc: + app.UI.MediaOverlay.SetFocus(MediaFocusOverview) } return event } @@ -363,16 +372,16 @@ func main() { if app.UI.Focus == LeftPaneFocus { if event.Key() == tcell.KeyRune { switch event.Rune() { - case 'v': + case 'v', 'V': app.UI.SetFocus(RightPaneFocus) return nil - case 'k': + case 'k', 'K': app.UI.TootList.Prev() return nil - case 'j': + case 'j', 'J': app.UI.TootList.Next() return nil - case 'q': + case 'q', 'Q': app.App.Stop() return nil } @@ -410,19 +419,19 @@ func main() { app.UI.CmdBar.View.SetText(":") app.UI.SetFocus(CmdBarFocus) return nil - case 't': + case 't', 'T': app.UI.ShowThread() - case 's': + case 's', 'S': app.UI.ShowSensetive() - case 'c': + case 'c', 'C': app.UI.NewToot() - case 'o': + case 'o', 'O': app.UI.ShowLinks() - case 'r': + case 'r', 'R': app.UI.Reply() - case 'm': + case 'm', 'M': app.UI.OpenMedia() - case 'f': + case 'f', 'F': //TODO UPDATE TOOT IN LIST app.UI.FavoriteEvent() case 'b': diff --git a/media.go b/media.go index 40eb3b1..334ed53 100644 --- a/media.go +++ b/media.go @@ -39,6 +39,13 @@ type MediaView struct { Files []string } +func (m *MediaView) Reset() { + m.Files = nil + m.FileList.Clear() + m.Focus = MediaFocusOverview + m.Draw() +} + func (m *MediaView) AddFile(f string) { m.Files = append(m.Files, f) m.FileList.AddItem(filepath.Base(f), "", 0, nil) diff --git a/messagebox.go b/messagebox.go index ee1d28c..1a5b83a 100644 --- a/messagebox.go +++ b/messagebox.go @@ -92,6 +92,15 @@ func (m *MessageBox) Post() { send.SpoilerText = toot.SpoilerText } + attachments := m.app.UI.MediaOverlay.Files + for _, ap := range attachments { + a, err := m.app.API.Client.UploadMedia(context.Background(), ap) + if err != nil { + log.Fatalln(err) + } + send.MediaIDs = append(send.MediaIDs, a.ID) + } + _, err := m.app.API.Client.PostStatus(context.Background(), &send) if err != nil { log.Fatalln(err) @@ -107,6 +116,8 @@ func (m *MessageBox) Draw() { var outputHead string var output string + subtleColor := fmt.Sprintf("[#%x]", m.app.Config.Style.Subtle.Hex()) + warningColor := fmt.Sprintf("[#%x]", m.app.Config.Style.WarningText.Hex()) if m.currentToot.Status != nil { var acct string if m.currentToot.Status.Account.DisplayName != "" { @@ -114,21 +125,20 @@ func (m *MessageBox) Draw() { } else { acct = fmt.Sprintf("%s\n", m.currentToot.Status.Account.Acct) } - outputHead += "[gray]Replying to " + tview.Escape(acct) + "\n" + outputHead += subtleColor + "Replying to " + tview.Escape(acct) + "\n" } - if m.currentToot.SpoilerText != "" && !m.currentToot.Sensitive { - outputHead += "[red]You have entered spoiler text, but haven't set an content warning. Do it by pressing " + tview.Escape("[T]") + "\n\n" + outputHead += warningColor + "You have entered spoiler text, but haven't set an content warning. Do it by pressing " + tview.Escape("[T]") + "\n\n" } if m.currentToot.Sensitive && m.currentToot.SpoilerText == "" { - outputHead += "[red]You have added an content warning, but haven't set any text above the hidden text. Do it by pressing " + tview.Escape("[C]") + "\n\n" + outputHead += warningColor + "You have added an content warning, but haven't set any text above the hidden text. Do it by pressing " + tview.Escape("[C]") + "\n\n" } if m.currentToot.Sensitive && m.currentToot.SpoilerText != "" { - outputHead += "[gray]Content warning\n\n" + outputHead += subtleColor + "Content warning\n\n" outputHead += tview.Escape(m.currentToot.SpoilerText) - outputHead += "\n\n[gray]---hidden content below---\n\n" + outputHead += "\n\n" + subtleColor + "---hidden content below---\n\n" } output = outputHead + tview.Escape(m.currentToot.Text) diff --git a/statustext.go b/statustext.go index 0613204..ec08eea 100644 --- a/statustext.go +++ b/statustext.go @@ -66,21 +66,25 @@ func (s *StatusText) ShowTootOptions(index int, showSensitive bool) { } s.LinkOverlay.SetURLs(urls) + subtleColor := fmt.Sprintf("[#%x]", s.app.Config.Style.Subtle.Hex()) + special1 := fmt.Sprintf("[#%x]", s.app.Config.Style.TextSpecial1.Hex()) + special2 := fmt.Sprintf("[#%x]", s.app.Config.Style.TextSpecial2.Hex()) var head string if status.Reblog != nil { if status.Account.DisplayName != "" { - head += fmt.Sprintf("[gray]%s (%s)\n", status.Account.DisplayName, status.Account.Acct) + head += fmt.Sprintf(subtleColor+"%s (%s)\n", status.Account.DisplayName, status.Account.Acct) } else { - head += fmt.Sprintf("[gray]%s\n", status.Account.Acct) + head += fmt.Sprintf(subtleColor+"%s\n", status.Account.Acct) } - head += "[gray]Boosted\n" - head += "[gray]" + line + head += subtleColor + "Boosted\n" + head += subtleColor + line status = status.Reblog } + if status.Account.DisplayName != "" { - head += fmt.Sprintf("[tomato]%s\n", status.Account.DisplayName) + head += fmt.Sprintf(special2+"%s\n", status.Account.DisplayName) } - head += fmt.Sprintf("[yellow]%s\n\n", status.Account.Acct) + head += fmt.Sprintf(special1+"%s\n\n", status.Account.Acct) output := head content := tview.Escape(stripped) if content != "" { @@ -89,8 +93,8 @@ func (s *StatusText) ShowTootOptions(index int, showSensitive bool) { var poll string if status.Poll != nil { - poll += "[gray]Poll\n" - poll += "[gray]" + line + poll += subtleColor + "Poll\n" + poll += subtleColor + line poll += fmt.Sprintf("Number of votes: %d\n\n", status.Poll.VotesCount) votes := float64(status.Poll.VotesCount) for _, o := range status.Poll.Options { @@ -105,15 +109,15 @@ func (s *StatusText) ShowTootOptions(index int, showSensitive bool) { var media string for _, att := range status.MediaAttachments { - media += "[gray]" + line - media += fmt.Sprintf("[gray]Attached %s\n", att.Type) + media += subtleColor + line + media += fmt.Sprintf(subtleColor+"Attached %s\n", att.Type) media += fmt.Sprintf("%s\n", att.URL) } var card string if status.Card != nil { - card += "[gray]Card type: " + status.Card.Type + "\n" - card += "[gray]" + line + card += subtleColor + "Card type: " + status.Card.Type + "\n" + card += subtleColor + line if status.Card.Title != "" { card += status.Card.Title + "\n\n" } @@ -145,7 +149,9 @@ func (s *StatusText) ShowTootOptions(index int, showSensitive bool) { if len(status.MediaAttachments) > 0 { info = append(info, "[M]edia") } - info = append(info, "[O]ther") + if len(urls) > 0 { + info = append(info, "[O]pen") + } if status.Account.ID == s.app.Me.ID { info = append(info, "[D]elete") diff --git a/ui.go b/ui.go index 22fcb7d..7d21de2 100644 --- a/ui.go +++ b/ui.go @@ -5,7 +5,6 @@ import ( "fmt" "log" - "github.com/gdamore/tcell" "github.com/mattn/go-mastodon" "github.com/rivo/tview" ) @@ -56,7 +55,7 @@ func (ui *UI) SetFocus(f FocusAt) { case LinkOverlayFocus: ui.Status.SetText("-- LINK --") ui.Pages.ShowPage("links") - ui.app.App.SetFocus(ui.StatusText.LinkOverlay.View) + ui.app.App.SetFocus(ui.StatusText.LinkOverlay.List) case AuthOverlayFocus: ui.Status.SetText("-- LOGIN --") ui.Pages.ShowPage("login") @@ -103,6 +102,7 @@ func (ui *UI) ShowSensetive() { func (ui *UI) NewToot() { ui.app.App.SetFocus(ui.MessageBox.View) + ui.app.UI.MediaOverlay.Reset() ui.MessageBox.NewToot() ui.MessageBox.Draw() ui.SetFocus(MessageFocus) @@ -116,13 +116,13 @@ func (ui *UI) Reply() { if status.Reblog != nil { status = status.Reblog } + ui.app.UI.MediaOverlay.Reset() ui.MessageBox.Reply(status) ui.MessageBox.Draw() ui.SetFocus(MessageFocus) } func (ui *UI) ShowLinks() { - ui.StatusText.LinkOverlay.Draw() ui.SetFocus(LinkOverlayFocus) } @@ -226,26 +226,3 @@ func (ui *UI) DeleteStatus() { log.Fatalln(err) } } - -func clearContent(screen tcell.Screen, x int, y int, width int, height int) (int, int, int, int) { - for cx := x; cx < width+x; cx++ { - for cy := y; cy < height+y; cy++ { - screen.SetContent(cx, cy, ' ', nil, tcell.StyleDefault.Background(tcell.ColorDefault)) - } - } - y2 := y + height - for cx := x + 1; cx < width+x; cx++ { - screen.SetContent(cx, y, tview.BoxDrawingsLightHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - screen.SetContent(cx, y2, tview.BoxDrawingsLightHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - } - x2 := x + width - for cy := y + 1; cy < height+y; cy++ { - screen.SetContent(x, cy, tview.BoxDrawingsLightVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - screen.SetContent(x2, cy, tview.BoxDrawingsLightVertical, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - } - screen.SetContent(x, y, tview.BoxDrawingsLightDownAndRight, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - screen.SetContent(x, y+height, tview.BoxDrawingsLightUpAndRight, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - screen.SetContent(x+width, y, tview.BoxDrawingsLightDownAndLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - screen.SetContent(x+width, y+height, tview.BoxDrawingsLightUpAndLeft, nil, tcell.StyleDefault.Foreground(tcell.ColorGray)) - return x + 1, y + 1, width - 1, height - 1 -}