Browse Source

support media upload and fixes

pull/8/head 0.0.1
Rasmus Lindroth 6 years ago
parent
commit
9d3bd71a0e
  1. 23
      README.md
  2. 1
      app.go
  3. 29
      config.go
  4. 2
      go.mod
  5. 4
      go.sum
  6. BIN
      images/preview.png
  7. 143
      linkoverlay.go
  8. 269
      main.go
  9. 7
      media.go
  10. 22
      messagebox.go
  11. 32
      statustext.go
  12. 29
      ui.go

23
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)

1
app.go

@ -10,5 +10,6 @@ type App struct {
UI *UI
Me *mastodon.Account
API *API
Config *Config
HaveAccount bool
}

29
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 {

2
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
)

4
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=

BIN
images/preview.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

143
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"))
}

269
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':

7
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)

22
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)

32
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")

29
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
}

Loading…
Cancel
Save