267 lines
6.0 KiB
Go
Raw Normal View History

2016-09-11 17:55:19 +02:00
package components
2016-09-24 20:18:09 +02:00
import (
2016-10-30 14:26:12 +01:00
"fmt"
2016-10-19 14:45:04 +02:00
"html"
2016-10-30 14:26:12 +01:00
"sort"
2016-09-24 20:18:09 +02:00
"strings"
"time"
2016-09-24 20:18:09 +02:00
2017-09-23 13:56:45 +02:00
"github.com/erroneousboat/termui"
2016-10-01 14:07:42 +02:00
2016-10-30 14:26:12 +01:00
"github.com/erroneousboat/slack-term/config"
2016-09-24 20:18:09 +02:00
)
type Message struct {
Time time.Time
Name string
Content string
}
2017-12-03 21:43:33 +01:00
func (m Message) ToString(timeAttr string, nameAttr string, contentAttr string) string {
if (m.Time != time.Time{} && m.Name != "") {
return html.UnescapeString(
fmt.Sprintf(
"[[%s]](fg-red,fg-bold) [<%s>](fg-blue,fg-bold) %s",
m.Time.Format("15:04"),
m.Name,
m.Content,
),
)
} else {
return html.UnescapeString(
fmt.Sprintf("%s", m.Content),
)
}
}
2016-10-02 14:53:00 +02:00
// Chat is the definition of a Chat component
2016-09-24 20:18:09 +02:00
type Chat struct {
List *termui.List
Offset int
2016-09-24 20:18:09 +02:00
}
2016-09-29 19:09:30 +02:00
// CreateChat is the constructor for the Chat struct
2017-12-01 23:52:25 +01:00
func CreateChatComponent(inputHeight int) *Chat {
2016-09-24 20:18:09 +02:00
chat := &Chat{
2016-09-30 16:36:41 +02:00
List: termui.NewList(),
Offset: 0,
2016-09-24 20:18:09 +02:00
}
chat.List.Height = termui.TermHeight() - inputHeight
chat.List.Overflow = "wrap"
2016-09-28 22:10:04 +02:00
2016-09-24 20:18:09 +02:00
return chat
}
// Buffer implements interface termui.Bufferer
func (c *Chat) Buffer() termui.Buffer {
// Build cells, after every item put a newline
cells := termui.DefaultTxBuilder.Build(
strings.Join(c.List.Items, "\n"),
c.List.ItemFgColor, c.List.ItemBgColor,
)
// We will create an array of Line structs, this allows us
// to more easily render the items in a list. We will range
// over the cells we've created and create a Line within
// the bounds of the Chat pane
2016-09-24 20:18:09 +02:00
type Line struct {
cells []termui.Cell
}
lines := []Line{}
line := Line{}
x := 0
for _, cell := range cells {
if cell.Ch == '\n' {
lines = append(lines, line)
line = Line{}
x = 0
continue
}
if x+cell.Width() > c.List.InnerBounds().Dx() {
lines = append(lines, line)
line = Line{}
x = 0
}
line.cells = append(line.cells, cell)
x++
}
lines = append(lines, line)
2016-09-24 20:18:09 +02:00
// We will print lines bottom up, it will loop over the lines
2016-09-30 16:36:41 +02:00
// backwards and for every line it'll set the cell in that line.
// Offset is the number which allows us to begin printing the
// line above the last line.
2016-09-24 20:18:09 +02:00
buf := c.List.Buffer()
linesHeight := len(lines)
paneMinY := c.List.InnerBounds().Min.Y
paneMaxY := c.List.InnerBounds().Max.Y
2016-09-24 20:18:09 +02:00
currentY := paneMaxY - 1
2016-09-30 16:36:41 +02:00
for i := (linesHeight - 1) - c.Offset; i >= 0; i-- {
if currentY < paneMinY {
2016-09-24 20:18:09 +02:00
break
}
x := c.List.InnerBounds().Min.X
for _, cell := range lines[i].cells {
buf.Set(x, currentY, cell)
x += cell.Width()
}
// When we're not at the end of the pane, fill it up
// with empty characters
for x < c.List.InnerBounds().Max.X {
2016-10-11 19:28:37 +02:00
buf.Set(
x, currentY,
termui.Cell{
Ch: ' ',
Fg: c.List.ItemFgColor,
Bg: c.List.ItemBgColor,
},
)
x++
}
currentY--
}
// If the space above currentY is empty we need to fill
// it up with blank lines, otherwise the List object will
// render the items top down, and the result will mix.
for currentY >= paneMinY {
x := c.List.InnerBounds().Min.X
for x < c.List.InnerBounds().Max.X {
2016-10-11 19:28:37 +02:00
buf.Set(
x, currentY,
termui.Cell{
Ch: ' ',
Fg: c.List.ItemFgColor,
Bg: c.List.ItemBgColor,
},
)
x++
}
2016-09-24 20:18:09 +02:00
currentY--
}
return buf
}
// GetHeight implements interface termui.GridBufferer
func (c *Chat) GetHeight() int {
return c.List.Block.GetHeight()
}
// SetWidth implements interface termui.GridBufferer
func (c *Chat) SetWidth(w int) {
c.List.SetWidth(w)
}
// SetX implements interface termui.GridBufferer
func (c *Chat) SetX(x int) {
c.List.SetX(x)
}
// SetY implements interface termui.GridBufferer
func (c *Chat) SetY(y int) {
c.List.SetY(y)
}
2017-12-01 23:52:25 +01:00
// GetMaxItems return the maximal amount of items can fit in the Chat
// component
func (c *Chat) GetMaxItems() int {
return c.List.InnerBounds().Max.Y - c.List.InnerBounds().Min.Y
}
2016-09-24 20:18:09 +02:00
2017-12-01 23:52:25 +01:00
// SetMessages will put the provided messages into the Items field of the
// Chat view
func (c *Chat) SetMessages(messages []string) {
for _, msg := range messages {
c.List.Items = append(c.List.Items, html.UnescapeString(msg))
2016-09-24 20:18:09 +02:00
}
}
2016-09-29 19:09:30 +02:00
// AddMessage adds a single message to List.Items
2016-09-25 22:34:02 +02:00
func (c *Chat) AddMessage(message string) {
2016-10-19 14:45:04 +02:00
c.List.Items = append(c.List.Items, html.UnescapeString(message))
2016-09-24 20:18:09 +02:00
}
2016-09-29 19:09:30 +02:00
// ClearMessages clear the List.Items
func (c *Chat) ClearMessages() {
c.List.Items = []string{}
}
2016-09-30 16:36:41 +02:00
// ScrollUp will render the chat messages based on the Offset of the Chat
// pane.
//
// Offset is 0 when scrolled down. (we loop backwards over the array, so we
// start with rendering last item in the list at the maximum y of the Chat
// pane). Increasing the Offset will thus result in substracting the offset
// from the len(Chat.List.Items).
2016-09-24 20:18:09 +02:00
func (c *Chat) ScrollUp() {
2016-09-30 16:36:41 +02:00
c.Offset = c.Offset + 10
// Protect overscrolling
if c.Offset > len(c.List.Items)-1 {
c.Offset = len(c.List.Items) - 1
}
2016-09-24 20:18:09 +02:00
}
2016-09-30 16:36:41 +02:00
// ScrollDown will render the chat messages based on the Offset of the Chat
// pane.
//
// Offset is 0 when scrolled down. (we loop backwards over the array, so we
// start with rendering last item in the list at the maximum y of the Chat
// pane). Increasing the Offset will thus result in substracting the offset
// from the len(Chat.List.Items).
func (c *Chat) ScrollDown() {
c.Offset = c.Offset - 10
// Protect overscrolling
if c.Offset < 0 {
c.Offset = 0
}
}
2016-09-28 22:10:04 +02:00
2016-09-29 19:09:30 +02:00
// SetBorderLabel will set Label of the Chat pane to the specified string
func (c *Chat) SetBorderLabel(channelName string) {
c.List.BorderLabel = channelName
2016-09-28 22:10:04 +02:00
}
2016-10-30 14:26:12 +01:00
// Help shows the usage and key bindings in the chat pane
func (c *Chat) Help(cfg *config.Config) {
help := []string{
"slack-term - slack client for your terminal",
"",
"USAGE:",
" slack-term -config [path-to-config]",
"",
"KEY BINDINGS:",
"",
}
for mode, mapping := range cfg.KeyMap {
help = append(help, fmt.Sprintf(" %s", strings.ToUpper(mode)))
help = append(help, "")
var keys []string
for k := range mapping {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
help = append(help, fmt.Sprintf(" %-12s%-15s", k, mapping[k]))
}
help = append(help, "")
}
c.List.Items = help
}