From 64c2257df020d4dc2767621c1c584c355522f540 Mon Sep 17 00:00:00 2001 From: erroneousboat Date: Sun, 3 Dec 2017 21:43:33 +0100 Subject: [PATCH 1/3] Start with adding colors for messages --- components/chat.go | 24 +++++++++++++++--------- handlers/event.go | 23 ++++++++++++++++++----- service/slack.go | 37 ++++++++++++++++++++++--------------- views/view.go | 10 ++++++++-- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/components/chat.go b/components/chat.go index b772e36..24178bc 100644 --- a/components/chat.go +++ b/components/chat.go @@ -18,15 +18,21 @@ type Message struct { Content string } -func (m Message) ToString() string { - return html.UnescapeString( - fmt.Sprintf( - "[%s] <%s> %s", - m.Time.Format("15:04"), - m.Name, - m.Content, - ), - ) +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), + ) + } } // Chat is the definition of a Chat component diff --git a/handlers/event.go b/handlers/event.go index fd1121d..e49eb8d 100644 --- a/handlers/event.go +++ b/handlers/event.go @@ -109,7 +109,9 @@ func messageHandler(ctx *context.AppContext) { // reverse order of messages, mainly done // when attachments are added to message for i := len(msg) - 1; i >= 0; i-- { - ctx.View.Chat.AddMessage(msg[i]) + ctx.View.Chat.AddMessage( + msg[i].ToString(), + ) } termui.Render(ctx.View.Chat) @@ -259,11 +261,17 @@ func actionSearchMode(ctx *context.AppContext) { } func actionGetMessages(ctx *context.AppContext) { - messages := ctx.Service.GetMessages( + msgs := ctx.Service.GetMessages( ctx.Service.Channels[ctx.View.Channels.SelectedChannel], ctx.View.Chat.GetMaxItems(), ) - ctx.View.Chat.SetMessages(messages) + + var strMsgs []string + for _, msg := range msgs { + strMsgs = append(strMsgs, msg.ToString()) + } + + ctx.View.Chat.SetMessages(strMsgs) termui.Render(ctx.View.Chat) } @@ -324,13 +332,18 @@ func actionChangeChannel(ctx *context.AppContext) { // Get messages of the SelectedChannel, and get the count of messages // that fit into the Chat component - messages := ctx.Service.GetMessages( + msgs := ctx.Service.GetMessages( ctx.Service.GetSlackChannel(ctx.View.Channels.SelectedChannel), ctx.View.Chat.GetMaxItems(), ) + var strMsgs []string + for _, msg := range msgs { + strMsgs = append(strMsgs, msg.ToString()) + } + // Set messages for the channel - ctx.View.Chat.SetMessages(messages) + ctx.View.Chat.SetMessages(strMsgs) // FIXME // Set channel name for the Chat pane diff --git a/service/slack.go b/service/slack.go index 98e4d58..e674871 100644 --- a/service/slack.go +++ b/service/slack.go @@ -271,7 +271,7 @@ func (s *SlackService) SendMessage(channelID int, message string) { // GetMessages will get messages for a channel, group or im channel delimited // by a count. -func (s *SlackService) GetMessages(channel interface{}, count int) []string { +func (s *SlackService) GetMessages(channel interface{}, count int) []components.Message { // https://api.slack.com/methods/channels.history historyParams := slack.HistoryParameters{ Count: count, @@ -301,7 +301,7 @@ func (s *SlackService) GetMessages(channel interface{}, count int) []string { } // Construct the messages - var messages []string + var messages []components.Message for _, message := range history.Messages { msg := s.CreateMessage(message) messages = append(messages, msg...) @@ -309,7 +309,7 @@ func (s *SlackService) GetMessages(channel interface{}, count int) []string { // Reverse the order of the messages, we want the newest in // the last place - var messagesReversed []string + var messagesReversed []components.Message for i := len(messages) - 1; i >= 0; i-- { messagesReversed = append(messagesReversed, messages[i]) } @@ -324,8 +324,8 @@ func (s *SlackService) GetMessages(channel interface{}, count int) []string { // // This returns an array of string because we will try to uncover attachments // associated with messages. -func (s *SlackService) CreateMessage(message slack.Message) []string { - var msgs []string +func (s *SlackService) CreateMessage(message slack.Message) []components.Message { + var msgs []components.Message var name string // Get username from cache @@ -377,14 +377,14 @@ func (s *SlackService) CreateMessage(message slack.Message) []string { Content: parseMessage(s, message.Text), } - msgs = append(msgs, msg.ToString()) + msgs = append(msgs, msg) return msgs } -func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent) []string { +func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent) []components.Message { - var msgs []string + var msgs []components.Message var name string // Append (edited) when an edited message is received @@ -442,7 +442,7 @@ func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent Content: parseMessage(s, message.Text), } - msgs = append(msgs, msg.ToString()) + msgs = append(msgs, msg) return msgs } @@ -524,25 +524,32 @@ func parseEmoji(msg string) string { // createMessageFromAttachments will construct a array of string of the Field // values of Attachments from a Message. -func createMessageFromAttachments(atts []slack.Attachment) []string { - var msgs []string +func createMessageFromAttachments(atts []slack.Attachment) []components.Message { + var msgs []components.Message for _, att := range atts { for i := len(att.Fields) - 1; i >= 0; i-- { - msgs = append(msgs, - fmt.Sprintf( + msgs = append(msgs, components.Message{ + Content: fmt.Sprintf( "%s %s", att.Fields[i].Title, att.Fields[i].Value, ), + }, ) } if att.Text != "" { - msgs = append(msgs, att.Text) + msgs = append( + msgs, + components.Message{Content: fmt.Sprintf("%s", att.Text)}, + ) } if att.Title != "" { - msgs = append(msgs, att.Title) + msgs = append( + msgs, + components.Message{Content: fmt.Sprintf("%s", att.Title)}, + ) } } diff --git a/views/view.go b/views/view.go index baf5949..af84fe9 100644 --- a/views/view.go +++ b/views/view.go @@ -30,11 +30,17 @@ func CreateView(svc *service.SlackService) *View { chat := components.CreateChatComponent(input.Par.Height) // Chat: fill the component - slackMsgs := svc.GetMessages( + msgs := svc.GetMessages( svc.GetSlackChannel(channels.SelectedChannel), chat.GetMaxItems(), ) - chat.SetMessages(slackMsgs) + + var strMsgs []string + for _, msg := range msgs { + strMsgs = append(strMsgs, msg.ToString()) + } + + chat.SetMessages(strMsgs) chat.SetBorderLabel(svc.Channels[channels.SelectedChannel].GetChannelName()) // Debug: create the component From 6ebeb7b5fc29b21d1a95f6621d0b46bb27d2b021 Mon Sep 17 00:00:00 2001 From: erroneousboat Date: Sat, 16 Dec 2017 22:54:00 +0100 Subject: [PATCH 2/3] Start with theming functionality --- components/chat.go | 10 +++++++--- config/config.go | 41 ++++++++++++++++++++++++++--------------- config/theme.go | 18 ++++++++++++++++++ context/context.go | 4 ++-- handlers/event.go | 24 +++++++++++++++++++++--- service/channel.go | 17 +++++++++++------ service/slack.go | 24 ++++++++++++++++++++---- views/view.go | 14 ++++++++++++-- 8 files changed, 117 insertions(+), 35 deletions(-) create mode 100644 config/theme.go diff --git a/components/chat.go b/components/chat.go index 24178bc..c1f4b24 100644 --- a/components/chat.go +++ b/components/chat.go @@ -18,19 +18,23 @@ type Message struct { Content string } -func (m Message) ToString(timeAttr string, nameAttr string, contentAttr string) string { +func (m Message) ToString(stlTime string, stlName string, stlContent 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", + "[[%s]](%s) [<%s>](%s) [%s](%s)", m.Time.Format("15:04"), + stlTime, m.Name, + stlName, m.Content, + stlContent, ), ) } else { return html.UnescapeString( - fmt.Sprintf("%s", m.Content), + fmt.Sprintf("[%s](%s)", m.Content, stlContent), ) } } diff --git a/config/config.go b/config/config.go index cd7f0d3..bb9f01a 100644 --- a/config/config.go +++ b/config/config.go @@ -4,17 +4,16 @@ import ( "encoding/json" "errors" "os" - - "github.com/erroneousboat/termui" ) // Config is the definition of a Config struct type Config struct { - SlackToken string `json:"slack_token"` - Theme string `json:"theme"` + SlackToken string `json:"slack_token"` + // Theme string `json:"theme"` SidebarWidth int `json:"sidebar_width"` MainWidth int `json:"-"` KeyMap map[string]keyMapping `json:"key_map"` + Theme Theme `json:"theme"` } type keyMapping map[string]string @@ -22,7 +21,7 @@ type keyMapping map[string]string // NewConfig loads the config file and returns a Config struct func NewConfig(filepath string) (*Config, error) { cfg := Config{ - Theme: "dark", + // Theme: "dark", SidebarWidth: 1, MainWidth: 11, KeyMap: map[string]keyMapping{ @@ -63,6 +62,18 @@ func NewConfig(filepath string) (*Config, error) { "": "space", }, }, + Theme: Theme{ + Message: Message{ + Time: "fg-red,fg-bold", + Name: "fg-blue,fg-bold", + Content: "", + }, + Channel: Channel{ + Prefix: "", + Icon: "fg-red", + Name: "", + }, + }, } file, err := os.Open(filepath) @@ -84,16 +95,16 @@ func NewConfig(filepath string) (*Config, error) { cfg.MainWidth = 12 - cfg.SidebarWidth - if cfg.Theme == "light" { - termui.ColorMap = map[string]termui.Attribute{ - "fg": termui.ColorBlack, - "bg": termui.ColorWhite, - "border.fg": termui.ColorBlack, - "label.fg": termui.ColorBlue, - "par.fg": termui.ColorYellow, - "par.label.bg": termui.ColorWhite, - } - } + // if cfg.Theme == "light" { + // termui.ColorMap = map[string]termui.Attribute{ + // "fg": termui.ColorBlack, + // "bg": termui.ColorWhite, + // "border.fg": termui.ColorBlack, + // "label.fg": termui.ColorBlue, + // "par.fg": termui.ColorYellow, + // "par.label.bg": termui.ColorWhite, + // } + // } return &cfg, nil } diff --git a/config/theme.go b/config/theme.go new file mode 100644 index 0000000..7068ce1 --- /dev/null +++ b/config/theme.go @@ -0,0 +1,18 @@ +package config + +type Theme struct { + Message Message `json:"message"` + Channel Channel `json:"channel"` +} + +type Message struct { + Time string `json:"time"` + Name string `json:"name"` + Content string `json:"content"` +} + +type Channel struct { + Prefix string `json:"prefix"` + Icon string `json:"icon"` + Name string `json:"name"` +} diff --git a/context/context.go b/context/context.go index ae985c3..8638771 100644 --- a/context/context.go +++ b/context/context.go @@ -44,13 +44,13 @@ func CreateAppContext(flgConfig string, flgDebug bool) (*AppContext, error) { } // Create Service - svc, err := service.NewSlackService(config.SlackToken) + svc, err := service.NewSlackService(config) if err != nil { return nil, err } // Create the main view - view := views.CreateView(svc) + view := views.CreateView(config, svc) // Setup the interface if flgDebug { diff --git a/handlers/event.go b/handlers/event.go index e49eb8d..d93a012 100644 --- a/handlers/event.go +++ b/handlers/event.go @@ -110,7 +110,11 @@ func messageHandler(ctx *context.AppContext) { // when attachments are added to message for i := len(msg) - 1; i >= 0; i-- { ctx.View.Chat.AddMessage( - msg[i].ToString(), + msg[i].ToString( + ctx.Config.Theme.Message.Time, + ctx.Config.Theme.Message.Name, + ctx.Config.Theme.Message.Content, + ), ) } @@ -268,7 +272,14 @@ func actionGetMessages(ctx *context.AppContext) { var strMsgs []string for _, msg := range msgs { - strMsgs = append(strMsgs, msg.ToString()) + strMsgs = append( + strMsgs, + msg.ToString( + ctx.Config.Theme.Message.Time, + ctx.Config.Theme.Message.Name, + ctx.Config.Theme.Message.Content, + ), + ) } ctx.View.Chat.SetMessages(strMsgs) @@ -339,7 +350,14 @@ func actionChangeChannel(ctx *context.AppContext) { var strMsgs []string for _, msg := range msgs { - strMsgs = append(strMsgs, msg.ToString()) + strMsgs = append( + strMsgs, + msg.ToString( + ctx.Config.Theme.Message.Time, + ctx.Config.Theme.Message.Name, + ctx.Config.Theme.Message.Content, + ), + ) } // Set messages for the channel diff --git a/service/channel.go b/service/channel.go index bd5a2b7..756076d 100644 --- a/service/channel.go +++ b/service/channel.go @@ -25,7 +25,7 @@ type Channel struct { // ToString will set the label of the channel, how it will be // displayed on screen. Based on the type, different icons are // shown, as well as an optional notification icon. -func (c Channel) ToString() string { +func (c Channel) ToString(stlPrefix string, stlIcon string, stlName string) string { var prefix string if c.Notification { prefix = components.IconNotification @@ -33,14 +33,13 @@ func (c Channel) ToString() string { prefix = " " } - var label string + var icon string switch c.Type { case ChannelTypeChannel: - label = fmt.Sprintf("%s %s %s", prefix, components.IconChannel, c.Name) + icon = components.IconChannel case ChannelTypeGroup: - label = fmt.Sprintf("%s %s %s", prefix, components.IconGroup, c.Name) + icon = components.IconGroup case ChannelTypeIM: - var icon string switch c.Presence { case PresenceActive: icon = components.IconOnline @@ -49,9 +48,15 @@ func (c Channel) ToString() string { default: icon = components.IconIM } - label = fmt.Sprintf("%s %s %s", prefix, icon, c.Name) } + label := fmt.Sprintf( + "[%s](%s) [%s](%s) [%s](%s)", + prefix, stlPrefix, + icon, stlIcon, + c.Name, stlName, + ) + return label } diff --git a/service/slack.go b/service/slack.go index e674871..6f84a69 100644 --- a/service/slack.go +++ b/service/slack.go @@ -22,6 +22,7 @@ const ( ) type SlackService struct { + Config *config.Config Client *slack.Client RTM *slack.RTM SlackChannels []interface{} @@ -33,9 +34,10 @@ type SlackService struct { // NewSlackService is the constructor for the SlackService and will initialize // the RTM and a Client -func NewSlackService(token string) (*SlackService, error) { +func NewSlackService(config *config.Config) (*SlackService, error) { svc := &SlackService{ - Client: slack.New(token), + Config: config, + Client: slack.New(config.SlackToken), UserCache: make(map[string]string), } @@ -153,7 +155,14 @@ func (s *SlackService) GetChannels() []string { var channels []string for _, chn := range s.Channels { - channels = append(channels, chn.ToString()) + channels = append( + channels, + chn.ToString( + s.Config.Theme.Channel.Prefix, + s.Config.Theme.Channel.Icon, + s.Config.Theme.Channel.Name, + ), + ) } return channels } @@ -162,7 +171,14 @@ func (s *SlackService) GetChannels() []string { func (s *SlackService) ChannelsToString() []string { var channels []string for _, chn := range s.Channels { - channels = append(channels, chn.ToString()) + channels = append( + channels, + chn.ToString( + s.Config.Theme.Channel.Prefix, + s.Config.Theme.Channel.Icon, + s.Config.Theme.Channel.Name, + ), + ) } return channels } diff --git a/views/view.go b/views/view.go index af84fe9..811066e 100644 --- a/views/view.go +++ b/views/view.go @@ -4,10 +4,12 @@ import ( "github.com/erroneousboat/termui" "github.com/erroneousboat/slack-term/components" + "github.com/erroneousboat/slack-term/config" "github.com/erroneousboat/slack-term/service" ) type View struct { + Config *config.Config Input *components.Input Chat *components.Chat Channels *components.Channels @@ -15,7 +17,7 @@ type View struct { Debug *components.Debug } -func CreateView(svc *service.SlackService) *View { +func CreateView(config *config.Config, svc *service.SlackService) *View { // Create Input component input := components.CreateInputComponent() @@ -37,7 +39,14 @@ func CreateView(svc *service.SlackService) *View { var strMsgs []string for _, msg := range msgs { - strMsgs = append(strMsgs, msg.ToString()) + strMsgs = append( + strMsgs, + msg.ToString( + config.Theme.Message.Time, + config.Theme.Message.Name, + config.Theme.Message.Content, + ), + ) } chat.SetMessages(strMsgs) @@ -50,6 +59,7 @@ func CreateView(svc *service.SlackService) *View { mode := components.CreateModeComponent() view := &View{ + Config: config, Input: input, Channels: channels, Chat: chat, From d170057b371287e6b456fefd54fe339592f4067c Mon Sep 17 00:00:00 2001 From: erroneousboat Date: Sun, 17 Dec 2017 12:22:04 +0100 Subject: [PATCH 3/3] Implement theming for several components --- components/channels.go | 77 ++++++++++++++++++++++++++++++ components/chat.go | 14 ++++-- components/mode.go | 19 ++++++-- config/config.go | 94 +++++++++++++++++++----------------- config/theme.go | 20 ++++++-- handlers/event.go | 24 ++-------- service/channel.go | 76 ----------------------------- service/slack.go | 105 +++++++++++++++++++++-------------------- views/view.go | 9 +--- 9 files changed, 226 insertions(+), 212 deletions(-) delete mode 100644 service/channel.go diff --git a/components/channels.go b/components/channels.go index 73f5413..df089b0 100644 --- a/components/channels.go +++ b/components/channels.go @@ -1,6 +1,8 @@ package components import ( + "fmt" + "html" "strings" "github.com/erroneousboat/termui" @@ -13,8 +15,82 @@ const ( IconGroup = "☰" IconIM = "●" IconNotification = "*" + + PresenceAway = "away" + PresenceActive = "active" + + ChannelTypeChannel = "channel" + ChannelTypeGroup = "group" + ChannelTypeIM = "im" ) +type ChannelItem struct { + ID string + Name string + Topic string + Type string + UserID string + Presence string + Notification bool + + StylePrefix string + StyleIcon string + StyleText string +} + +// ToString will set the label of the channel, how it will be +// displayed on screen. Based on the type, different icons are +// shown, as well as an optional notification icon. +func (c ChannelItem) ToString() string { + var prefix string + if c.Notification { + prefix = IconNotification + } else { + prefix = " " + } + + var icon string + switch c.Type { + case ChannelTypeChannel: + icon = IconChannel + case ChannelTypeGroup: + icon = IconGroup + case ChannelTypeIM: + switch c.Presence { + case PresenceActive: + icon = IconOnline + case PresenceAway: + icon = IconOffline + default: + icon = IconIM + } + } + + label := fmt.Sprintf( + "[%s](%s) [%s](%s) [%s](%s)", + prefix, c.StylePrefix, + icon, c.StyleIcon, + c.Name, c.StyleText, + ) + + return label +} + +// GetChannelName will return a formatted representation of the +// name of the channel +func (c ChannelItem) GetChannelName() string { + var channelName string + if c.Topic != "" { + channelName = fmt.Sprintf("%s - %s", + html.UnescapeString(c.Name), + html.UnescapeString(c.Topic), + ) + } else { + channelName = c.Name + } + return channelName +} + // Channels is the definition of a Channels component type Channels struct { List *termui.List @@ -51,6 +127,7 @@ func (c *Channels) Buffer() termui.Buffer { break } + // Set the visible cursor var cells []termui.Cell if y == c.CursorPosition { cells = termui.DefaultTxBuilder.Build( diff --git a/components/chat.go b/components/chat.go index c1f4b24..3fbafec 100644 --- a/components/chat.go +++ b/components/chat.go @@ -16,25 +16,29 @@ type Message struct { Time time.Time Name string Content string + + StyleTime string + StyleName string + StyleText string } -func (m Message) ToString(stlTime string, stlName string, stlContent string) string { +func (m Message) ToString() string { if (m.Time != time.Time{} && m.Name != "") { return html.UnescapeString( fmt.Sprintf( "[[%s]](%s) [<%s>](%s) [%s](%s)", m.Time.Format("15:04"), - stlTime, + m.StyleTime, m.Name, - stlName, + m.StyleName, m.Content, - stlContent, + m.StyleText, ), ) } else { return html.UnescapeString( - fmt.Sprintf("[%s](%s)", m.Content, stlContent), + fmt.Sprintf("[%s](%s)", m.Content, m.StyleText), ) } } diff --git a/components/mode.go b/components/mode.go index 799a5eb..36cf8ca 100644 --- a/components/mode.go +++ b/components/mode.go @@ -1,6 +1,14 @@ package components -import "github.com/erroneousboat/termui" +import ( + "github.com/erroneousboat/termui" +) + +const ( + CommandMode = "NORMAL" + InsertMode = "INSERT" + SearchMode = "SEARCH" +) // Mode is the definition of Mode component type Mode struct { @@ -10,10 +18,11 @@ type Mode struct { // CreateMode is the constructor of the Mode struct func CreateModeComponent() *Mode { mode := &Mode{ - Par: termui.NewPar("NORMAL"), + Par: termui.NewPar(CommandMode), } mode.Par.Height = 3 + mode.SetCommandMode() return mode } @@ -82,16 +91,16 @@ func (m *Mode) SetY(y int) { } func (m *Mode) SetInsertMode() { - m.Par.Text = "INSERT" + m.Par.Text = InsertMode termui.Render(m) } func (m *Mode) SetCommandMode() { - m.Par.Text = "NORMAL" + m.Par.Text = CommandMode termui.Render(m) } func (m *Mode) SetSearchMode() { - m.Par.Text = "SEARCH" + m.Par.Text = SearchMode termui.Render(m) } diff --git a/config/config.go b/config/config.go index bb9f01a..b9b430a 100644 --- a/config/config.go +++ b/config/config.go @@ -4,12 +4,13 @@ import ( "encoding/json" "errors" "os" + + "github.com/erroneousboat/termui" ) // Config is the definition of a Config struct type Config struct { - SlackToken string `json:"slack_token"` - // Theme string `json:"theme"` + SlackToken string `json:"slack_token"` SidebarWidth int `json:"sidebar_width"` MainWidth int `json:"-"` KeyMap map[string]keyMapping `json:"key_map"` @@ -20,8 +21,41 @@ type keyMapping map[string]string // NewConfig loads the config file and returns a Config struct func NewConfig(filepath string) (*Config, error) { - cfg := Config{ - // Theme: "dark", + cfg := getDefaultConfig() + + file, err := os.Open(filepath) + if err != nil { + return &cfg, err + } + + if err := json.NewDecoder(file).Decode(&cfg); err != nil { + return &cfg, err + } + + if cfg.SlackToken == "" { + return &cfg, errors.New("couldn't find 'slack_token' parameter") + } + + if cfg.SidebarWidth < 1 || cfg.SidebarWidth > 11 { + return &cfg, errors.New("please specify the 'sidebar_width' between 1 and 11") + } + + cfg.MainWidth = 12 - cfg.SidebarWidth + + termui.ColorMap = map[string]termui.Attribute{ + "fg": termui.StringToAttribute(cfg.Theme.View.Fg), + "bg": termui.StringToAttribute(cfg.Theme.View.Bg), + "border.fg": termui.StringToAttribute(cfg.Theme.View.BorderFg), + "label.fg": termui.StringToAttribute(cfg.Theme.View.LabelFg), + "par.fg": termui.StringToAttribute(cfg.Theme.View.ParFg), + "par.label.bg": termui.StringToAttribute(cfg.Theme.View.ParLabelFg), + } + + return &cfg, nil +} + +func getDefaultConfig() Config { + return Config{ SidebarWidth: 1, MainWidth: 11, KeyMap: map[string]keyMapping{ @@ -63,48 +97,24 @@ func NewConfig(filepath string) (*Config, error) { }, }, Theme: Theme{ - Message: Message{ - Time: "fg-red,fg-bold", - Name: "fg-blue,fg-bold", - Content: "", + View: View{ + Fg: "white", + Bg: "default", + BorderFg: "white", + LabelFg: "green,bold", + ParFg: "white", + ParLabelFg: "white", }, Channel: Channel{ Prefix: "", - Icon: "fg-red", - Name: "", + Icon: "fg-green,fg-bold", + Text: "fg-blue,fg-bold", + }, + Message: Message{ + Time: "fg-red,fg-bold", + Name: "fg-blue,fg-bold", + Text: "", }, }, } - - file, err := os.Open(filepath) - if err != nil { - return &cfg, err - } - - if err := json.NewDecoder(file).Decode(&cfg); err != nil { - return &cfg, err - } - - if cfg.SlackToken == "" { - return &cfg, errors.New("couldn't find 'slack_token' parameter") - } - - if cfg.SidebarWidth < 1 || cfg.SidebarWidth > 11 { - return &cfg, errors.New("please specify the 'sidebar_width' between 1 and 11") - } - - cfg.MainWidth = 12 - cfg.SidebarWidth - - // if cfg.Theme == "light" { - // termui.ColorMap = map[string]termui.Attribute{ - // "fg": termui.ColorBlack, - // "bg": termui.ColorWhite, - // "border.fg": termui.ColorBlack, - // "label.fg": termui.ColorBlue, - // "par.fg": termui.ColorYellow, - // "par.label.bg": termui.ColorWhite, - // } - // } - - return &cfg, nil } diff --git a/config/theme.go b/config/theme.go index 7068ce1..0b113d9 100644 --- a/config/theme.go +++ b/config/theme.go @@ -1,18 +1,28 @@ package config type Theme struct { - Message Message `json:"message"` + View View `json:"view"` Channel Channel `json:"channel"` + Message Message `json:"message"` +} + +type View struct { + Fg string `json:"fg"` + Bg string `json:"bg"` + BorderFg string `json:"border_fg"` + LabelFg string `json:"border_fg"` + ParFg string `json:"par_fg"` + ParLabelFg string `json:"par_label_fg"` } type Message struct { - Time string `json:"time"` - Name string `json:"name"` - Content string `json:"content"` + Time string `json:"time"` + Name string `json:"name"` + Text string `json:"text"` } type Channel struct { Prefix string `json:"prefix"` Icon string `json:"icon"` - Name string `json:"name"` + Text string `json:"text"` } diff --git a/handlers/event.go b/handlers/event.go index d93a012..e49eb8d 100644 --- a/handlers/event.go +++ b/handlers/event.go @@ -110,11 +110,7 @@ func messageHandler(ctx *context.AppContext) { // when attachments are added to message for i := len(msg) - 1; i >= 0; i-- { ctx.View.Chat.AddMessage( - msg[i].ToString( - ctx.Config.Theme.Message.Time, - ctx.Config.Theme.Message.Name, - ctx.Config.Theme.Message.Content, - ), + msg[i].ToString(), ) } @@ -272,14 +268,7 @@ func actionGetMessages(ctx *context.AppContext) { var strMsgs []string for _, msg := range msgs { - strMsgs = append( - strMsgs, - msg.ToString( - ctx.Config.Theme.Message.Time, - ctx.Config.Theme.Message.Name, - ctx.Config.Theme.Message.Content, - ), - ) + strMsgs = append(strMsgs, msg.ToString()) } ctx.View.Chat.SetMessages(strMsgs) @@ -350,14 +339,7 @@ func actionChangeChannel(ctx *context.AppContext) { var strMsgs []string for _, msg := range msgs { - strMsgs = append( - strMsgs, - msg.ToString( - ctx.Config.Theme.Message.Time, - ctx.Config.Theme.Message.Name, - ctx.Config.Theme.Message.Content, - ), - ) + strMsgs = append(strMsgs, msg.ToString()) } // Set messages for the channel diff --git a/service/channel.go b/service/channel.go deleted file mode 100644 index 756076d..0000000 --- a/service/channel.go +++ /dev/null @@ -1,76 +0,0 @@ -package service - -import ( - "fmt" - "html" - - "github.com/erroneousboat/slack-term/components" -) - -const ( - PresenceAway = "away" - PresenceActive = "active" -) - -type Channel struct { - ID string - Name string - Topic string - Type string - UserID string - Presence string - Notification bool -} - -// ToString will set the label of the channel, how it will be -// displayed on screen. Based on the type, different icons are -// shown, as well as an optional notification icon. -func (c Channel) ToString(stlPrefix string, stlIcon string, stlName string) string { - var prefix string - if c.Notification { - prefix = components.IconNotification - } else { - prefix = " " - } - - var icon string - switch c.Type { - case ChannelTypeChannel: - icon = components.IconChannel - case ChannelTypeGroup: - icon = components.IconGroup - case ChannelTypeIM: - switch c.Presence { - case PresenceActive: - icon = components.IconOnline - case PresenceAway: - icon = components.IconOffline - default: - icon = components.IconIM - } - } - - label := fmt.Sprintf( - "[%s](%s) [%s](%s) [%s](%s)", - prefix, stlPrefix, - icon, stlIcon, - c.Name, stlName, - ) - - return label -} - -// GetChannelName will return a formatted representation of the -// name of the channel -func (c Channel) GetChannelName() string { - var channelName string - if c.Topic != "" { - channelName = fmt.Sprintf("%s - %s", - html.UnescapeString(c.Name), - html.UnescapeString(c.Topic), - ) - } else { - channelName = c.Name - } - return channelName -} diff --git a/service/slack.go b/service/slack.go index 6f84a69..56e6383 100644 --- a/service/slack.go +++ b/service/slack.go @@ -26,7 +26,7 @@ type SlackService struct { Client *slack.Client RTM *slack.RTM SlackChannels []interface{} - Channels []Channel + Channels []components.ChannelItem UserCache map[string]string CurrentUserID string CurrentUsername string @@ -79,23 +79,27 @@ func NewSlackService(config *config.Config) (*SlackService, error) { // an []interface as well as to a []Channel which will give us easy access // to the id and name of the Channel. func (s *SlackService) GetChannels() []string { - var chans []Channel + var chans []components.ChannelItem // Channel slackChans, err := s.Client.GetChannels(true) if err != nil { - chans = append(chans, Channel{}) + chans = append(chans, components.ChannelItem{}) } + for _, chn := range slackChans { if chn.IsMember { s.SlackChannels = append(s.SlackChannels, chn) chans = append( - chans, Channel{ - ID: chn.ID, - Name: chn.Name, - Topic: chn.Topic.Value, - Type: ChannelTypeChannel, - UserID: "", + chans, components.ChannelItem{ + ID: chn.ID, + Name: chn.Name, + Topic: chn.Topic.Value, + Type: components.ChannelTypeChannel, + UserID: "", + StylePrefix: s.Config.Theme.Channel.Prefix, + StyleIcon: s.Config.Theme.Channel.Icon, + StyleText: s.Config.Theme.Channel.Text, }, ) } @@ -104,17 +108,20 @@ func (s *SlackService) GetChannels() []string { // Groups slackGroups, err := s.Client.GetGroups(true) if err != nil { - chans = append(chans, Channel{}) + chans = append(chans, components.ChannelItem{}) } for _, grp := range slackGroups { s.SlackChannels = append(s.SlackChannels, grp) chans = append( - chans, Channel{ - ID: grp.ID, - Name: grp.Name, - Topic: grp.Topic.Value, - Type: ChannelTypeGroup, - UserID: "", + chans, components.ChannelItem{ + ID: grp.ID, + Name: grp.Name, + Topic: grp.Topic.Value, + Type: components.ChannelTypeGroup, + UserID: "", + StylePrefix: s.Config.Theme.Channel.Prefix, + StyleIcon: s.Config.Theme.Channel.Icon, + StyleText: s.Config.Theme.Channel.Text, }, ) } @@ -122,7 +129,7 @@ func (s *SlackService) GetChannels() []string { // IM slackIM, err := s.Client.GetIMChannels() if err != nil { - chans = append(chans, Channel{}) + chans = append(chans, components.ChannelItem{}) } for _, im := range slackIM { @@ -138,13 +145,16 @@ func (s *SlackService) GetChannels() []string { if ok { chans = append( chans, - Channel{ - ID: im.ID, - Name: name, - Topic: "", - Type: ChannelTypeIM, - UserID: im.User, - Presence: presence, + components.ChannelItem{ + ID: im.ID, + Name: name, + Topic: "", + Type: components.ChannelTypeIM, + UserID: im.User, + Presence: presence, + StylePrefix: s.Config.Theme.Channel.Prefix, + StyleIcon: s.Config.Theme.Channel.Icon, + StyleText: s.Config.Theme.Channel.Text, }, ) s.SlackChannels = append(s.SlackChannels, im) @@ -155,14 +165,7 @@ func (s *SlackService) GetChannels() []string { var channels []string for _, chn := range s.Channels { - channels = append( - channels, - chn.ToString( - s.Config.Theme.Channel.Prefix, - s.Config.Theme.Channel.Icon, - s.Config.Theme.Channel.Name, - ), - ) + channels = append(channels, chn.ToString()) } return channels } @@ -171,14 +174,7 @@ func (s *SlackService) GetChannels() []string { func (s *SlackService) ChannelsToString() []string { var channels []string for _, chn := range s.Channels { - channels = append( - channels, - chn.ToString( - s.Config.Theme.Channel.Prefix, - s.Config.Theme.Channel.Icon, - s.Config.Theme.Channel.Name, - ), - ) + channels = append(channels, chn.ToString()) } return channels } @@ -376,7 +372,7 @@ func (s *SlackService) CreateMessage(message slack.Message) []components.Message // When there are attachments append them if len(message.Attachments) > 0 { - msgs = append(msgs, createMessageFromAttachments(message.Attachments)...) + msgs = append(msgs, s.CreateMessageFromAttachments(message.Attachments)...) } // Parse time @@ -388,9 +384,12 @@ func (s *SlackService) CreateMessage(message slack.Message) []components.Message // Format message msg := components.Message{ - Time: time.Unix(intTime, 0), - Name: name, - Content: parseMessage(s, message.Text), + Time: time.Unix(intTime, 0), + Name: name, + Content: parseMessage(s, message.Text), + StyleTime: s.Config.Theme.Message.Time, + StyleName: s.Config.Theme.Message.Name, + StyleText: s.Config.Theme.Message.Text, } msgs = append(msgs, msg) @@ -441,7 +440,7 @@ func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent // When there are attachments append them if len(message.Attachments) > 0 { - msgs = append(msgs, createMessageFromAttachments(message.Attachments)...) + msgs = append(msgs, s.CreateMessageFromAttachments(message.Attachments)...) } // Parse time @@ -453,9 +452,12 @@ func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent // Format message msg := components.Message{ - Time: time.Unix(intTime, 0), - Name: name, - Content: parseMessage(s, message.Text), + Time: time.Unix(intTime, 0), + Name: name, + Content: parseMessage(s, message.Text), + StyleTime: s.Config.Theme.Message.Time, + StyleName: s.Config.Theme.Message.Name, + StyleText: s.Config.Theme.Message.Text, } msgs = append(msgs, msg) @@ -538,9 +540,9 @@ func parseEmoji(msg string) string { ) } -// createMessageFromAttachments will construct a array of string of the Field +// CreateMessageFromAttachments will construct a array of string of the Field // values of Attachments from a Message. -func createMessageFromAttachments(atts []slack.Attachment) []components.Message { +func (s *SlackService) CreateMessageFromAttachments(atts []slack.Attachment) []components.Message { var msgs []components.Message for _, att := range atts { for i := len(att.Fields) - 1; i >= 0; i-- { @@ -550,6 +552,9 @@ func createMessageFromAttachments(atts []slack.Attachment) []components.Message att.Fields[i].Title, att.Fields[i].Value, ), + StyleTime: s.Config.Theme.Message.Time, + StyleName: s.Config.Theme.Message.Name, + StyleText: s.Config.Theme.Message.Text, }, ) } diff --git a/views/view.go b/views/view.go index 811066e..3361616 100644 --- a/views/view.go +++ b/views/view.go @@ -39,14 +39,7 @@ func CreateView(config *config.Config, svc *service.SlackService) *View { var strMsgs []string for _, msg := range msgs { - strMsgs = append( - strMsgs, - msg.ToString( - config.Theme.Message.Time, - config.Theme.Message.Name, - config.Theme.Message.Content, - ), - ) + strMsgs = append(strMsgs, msg.ToString()) } chat.SetMessages(strMsgs)