Refactor to get service reference out of components
This commit is contained in:
parent
6d8d25cf1f
commit
23388070de
@ -1,13 +1,9 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/erroneousboat/termui"
|
||||
|
||||
"github.com/erroneousboat/slack-term/service"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -17,9 +13,6 @@ const (
|
||||
IconGroup = "☰"
|
||||
IconIM = "●"
|
||||
IconNotification = "*"
|
||||
|
||||
PresenceAway = "away"
|
||||
PresenceActive = "active"
|
||||
)
|
||||
|
||||
// Channels is the definition of a Channels component
|
||||
@ -123,67 +116,8 @@ func (c *Channels) SetY(y int) {
|
||||
c.List.SetY(y)
|
||||
}
|
||||
|
||||
// SetChannels will set the channels from the service, to the
|
||||
// Items field
|
||||
func (c *Channels) SetChannels(channels []service.Channel) {
|
||||
c.List.Items = make([]string, len(channels))
|
||||
|
||||
// WaitGroup needed, else SetPresenceChannels will start
|
||||
// too early
|
||||
var wg sync.WaitGroup
|
||||
for i, slackChan := range channels {
|
||||
wg.Add(1)
|
||||
go func(i int, slackChan service.Channel) {
|
||||
label := setChannelLabel(slackChan, false)
|
||||
c.List.Items[i] = label
|
||||
wg.Done()
|
||||
}(i, slackChan)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// SetPresenceChannels will set the icon for all the IM channels
|
||||
func (c *Channels) SetPresenceChannels(channels []service.Channel) {
|
||||
for i, slackChan := range channels {
|
||||
go func(i int, slackChan service.Channel) {
|
||||
if slackChan.Type == service.ChannelTypeIM {
|
||||
c.SetPresenceChannel(i, slackChan.Presence)
|
||||
}
|
||||
}(i, slackChan)
|
||||
}
|
||||
}
|
||||
|
||||
// SetPresenceChannel will set the correct icon for one IM channel, on
|
||||
// a Presence change event
|
||||
func (c *Channels) SetPresenceChannelEvent(channels []service.Channel, userID string, presence string) {
|
||||
// Get the correct Channel from svc.Channels
|
||||
var index int
|
||||
for i, channel := range channels {
|
||||
if userID == channel.UserID {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.SetPresenceChannel(index, presence)
|
||||
}
|
||||
|
||||
// SetPresenceChannel will set the correct icon for one IM channel
|
||||
func (c *Channels) SetPresenceChannel(i int, presence string) {
|
||||
switch presence {
|
||||
case PresenceActive:
|
||||
c.List.Items[i] = strings.Replace(
|
||||
c.List.Items[i], IconOffline, IconOnline, 1,
|
||||
)
|
||||
case PresenceAway:
|
||||
c.List.Items[i] = strings.Replace(
|
||||
c.List.Items[i], IconOnline, IconOffline, 1,
|
||||
)
|
||||
default:
|
||||
c.List.Items[i] = strings.Replace(
|
||||
c.List.Items[i], IconOnline, IconOffline, 1,
|
||||
)
|
||||
}
|
||||
func (c *Channels) SetChannels(channels []string) {
|
||||
c.List.Items = channels
|
||||
}
|
||||
|
||||
// SetSelectedChannel sets the SelectedChannel given the index
|
||||
@ -201,7 +135,6 @@ func (c *Channels) MoveCursorUp() {
|
||||
if c.SelectedChannel > 0 {
|
||||
c.SetSelectedChannel(c.SelectedChannel - 1)
|
||||
c.ScrollUp()
|
||||
c.MarkAsRead()
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +143,6 @@ func (c *Channels) MoveCursorDown() {
|
||||
if c.SelectedChannel < len(c.List.Items)-1 {
|
||||
c.SetSelectedChannel(c.SelectedChannel + 1)
|
||||
c.ScrollDown()
|
||||
c.MarkAsRead()
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,67 +230,3 @@ func (c *Channels) Search(term string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarkAsUnread will be called when a new message arrives and will
|
||||
// render an notification icon in front of the channel name
|
||||
func (c *Channels) MarkAsUnread(channels []service.Channel, channelID string) {
|
||||
// Get the correct Channel from svc.Channels
|
||||
var index int
|
||||
for i, channel := range channels {
|
||||
if channelID == channel.ID {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(c.List.Items[index], IconNotification) {
|
||||
// The order of svc.Channels relates to the order of
|
||||
// List.Items, index will be the index of the channel
|
||||
c.List.Items[index] = fmt.Sprintf(
|
||||
"%s %s", IconNotification, strings.TrimSpace(c.List.Items[index]),
|
||||
)
|
||||
}
|
||||
|
||||
// Play terminal bell sound
|
||||
fmt.Print("\a")
|
||||
}
|
||||
|
||||
// MarkAsRead will remove the notification icon in front of a channel that
|
||||
// received a new message. This will happen as one will move up or down the
|
||||
// cursor for Channels
|
||||
func (c *Channels) MarkAsRead() {
|
||||
channelName := strings.Split(
|
||||
c.List.Items[c.SelectedChannel],
|
||||
fmt.Sprintf("%s ", IconNotification),
|
||||
)
|
||||
|
||||
if len(channelName) > 1 {
|
||||
c.List.Items[c.SelectedChannel] = fmt.Sprintf(" %s", channelName[1])
|
||||
} else {
|
||||
c.List.Items[c.SelectedChannel] = channelName[0]
|
||||
}
|
||||
}
|
||||
|
||||
// setChannelLabel will set the label of the channel, meaning, how it
|
||||
// is displayed on screen. Based on the type, different icons are
|
||||
// shown, as well as an optional notification icon.
|
||||
func setChannelLabel(channel service.Channel, notification bool) string {
|
||||
var prefix string
|
||||
if notification {
|
||||
prefix = IconNotification
|
||||
} else {
|
||||
prefix = " "
|
||||
}
|
||||
|
||||
var label string
|
||||
switch channel.Type {
|
||||
case service.ChannelTypeChannel:
|
||||
label = fmt.Sprintf("%s %s %s", prefix, IconChannel, channel.Name)
|
||||
case service.ChannelTypeGroup:
|
||||
label = fmt.Sprintf("%s %s %s", prefix, IconGroup, channel.Name)
|
||||
case service.ChannelTypeIM:
|
||||
label = fmt.Sprintf("%s %s %s", prefix, IconIM, channel.Name)
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
@ -5,13 +5,30 @@ import (
|
||||
"html"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/erroneousboat/termui"
|
||||
|
||||
"github.com/erroneousboat/slack-term/config"
|
||||
"github.com/erroneousboat/slack-term/service"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Time time.Time
|
||||
Name string
|
||||
Content string
|
||||
}
|
||||
|
||||
func (m Message) ToString() string {
|
||||
return html.UnescapeString(
|
||||
fmt.Sprintf(
|
||||
"[%s] <%s> %s",
|
||||
m.Time.Format("15:04"),
|
||||
m.Name,
|
||||
m.Content,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Chat is the definition of a Chat component
|
||||
type Chat struct {
|
||||
List *termui.List
|
||||
@ -207,16 +224,7 @@ func (c *Chat) ScrollDown() {
|
||||
}
|
||||
|
||||
// SetBorderLabel will set Label of the Chat pane to the specified string
|
||||
func (c *Chat) SetBorderLabel(channel service.Channel) {
|
||||
var channelName string
|
||||
if channel.Topic != "" {
|
||||
channelName = fmt.Sprintf("%s - %s",
|
||||
html.UnescapeString(channel.Name),
|
||||
html.UnescapeString(channel.Topic),
|
||||
)
|
||||
} else {
|
||||
channelName = channel.Name
|
||||
}
|
||||
func (c *Chat) SetBorderLabel(channelName string) {
|
||||
c.List.BorderLabel = channelName
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,6 @@ package components
|
||||
import (
|
||||
"github.com/erroneousboat/termui"
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
|
||||
"github.com/erroneousboat/slack-term/service"
|
||||
)
|
||||
|
||||
// Input is the definition of an Input component
|
||||
@ -71,11 +69,6 @@ func (i *Input) SetY(y int) {
|
||||
i.Par.SetY(y)
|
||||
}
|
||||
|
||||
// SendMessage send the input text through the SlackService
|
||||
func (i *Input) SendMessage(svc *service.SlackService, channel string, message string) {
|
||||
svc.SendMessage(channel, message)
|
||||
}
|
||||
|
||||
// Insert will insert a given key at the place of the current CursorPositionText
|
||||
func (i *Input) Insert(key rune) {
|
||||
// Append key to the left side
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -59,7 +60,7 @@ func eventHandler(ctx *context.AppContext) {
|
||||
|
||||
// Place your debugging statements here
|
||||
if ctx.Debug {
|
||||
ctx.View.Debug.Println("hello world")
|
||||
ctx.View.Debug.Println("event received")
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -207,9 +208,8 @@ func actionSend(ctx *context.AppContext) {
|
||||
ctx.View.Input.Clear()
|
||||
ctx.View.Refresh()
|
||||
|
||||
ctx.View.Input.SendMessage(
|
||||
ctx.Service,
|
||||
ctx.Service.Channels[ctx.View.Channels.SelectedChannel].ID,
|
||||
ctx.Service.SendMessage(
|
||||
ctx.View.Channels.SelectedChannel,
|
||||
message,
|
||||
)
|
||||
}
|
||||
@ -330,25 +330,30 @@ func actionChangeChannel(ctx *context.AppContext) {
|
||||
// Set messages for the channel
|
||||
ctx.View.Chat.SetMessages(messages)
|
||||
|
||||
// FIXME
|
||||
// Set channel name for the Chat pane
|
||||
ctx.View.Chat.SetBorderLabel(
|
||||
ctx.Service.Channels[ctx.View.Channels.SelectedChannel],
|
||||
ctx.Service.Channels[ctx.View.Channels.SelectedChannel].GetChannelName(),
|
||||
)
|
||||
|
||||
// Clear notification icon if there is any
|
||||
ctx.View.Channels.MarkAsRead()
|
||||
ctx.Service.MarkAsRead(ctx.View.Channels.SelectedChannel)
|
||||
ctx.View.Channels.SetChannels(ctx.Service.ChannelsToString())
|
||||
|
||||
termui.Render(ctx.View.Channels)
|
||||
termui.Render(ctx.View.Chat)
|
||||
}
|
||||
|
||||
func actionNewMessage(ctx *context.AppContext, channelID string) {
|
||||
ctx.View.Channels.MarkAsUnread(ctx.Service.Channels, channelID)
|
||||
ctx.Service.MarkAsUnread(ctx.View.Channels.SelectedChannel)
|
||||
ctx.View.Channels.SetChannels(ctx.Service.ChannelsToString())
|
||||
termui.Render(ctx.View.Channels)
|
||||
fmt.Print("\a")
|
||||
}
|
||||
|
||||
func actionSetPresence(ctx *context.AppContext, channelID string, presence string) {
|
||||
ctx.View.Channels.SetPresenceChannelEvent(ctx.Service.Channels, channelID, presence)
|
||||
ctx.Service.SetPresenceChannelEvent(channelID, presence)
|
||||
ctx.View.Channels.SetChannels(ctx.Service.ChannelsToString())
|
||||
termui.Render(ctx.View.Channels)
|
||||
}
|
||||
|
||||
|
71
service/channel.go
Normal file
71
service/channel.go
Normal file
@ -0,0 +1,71 @@
|
||||
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() string {
|
||||
var prefix string
|
||||
if c.Notification {
|
||||
prefix = components.IconNotification
|
||||
} else {
|
||||
prefix = " "
|
||||
}
|
||||
|
||||
var label string
|
||||
switch c.Type {
|
||||
case ChannelTypeChannel:
|
||||
label = fmt.Sprintf("%s %s %s", prefix, components.IconChannel, c.Name)
|
||||
case ChannelTypeGroup:
|
||||
label = fmt.Sprintf("%s %s %s", prefix, components.IconGroup, c.Name)
|
||||
case ChannelTypeIM:
|
||||
var icon string
|
||||
switch c.Presence {
|
||||
case PresenceActive:
|
||||
icon = components.IconOnline
|
||||
case PresenceAway:
|
||||
icon = components.IconOffline
|
||||
default:
|
||||
icon = components.IconIM
|
||||
}
|
||||
label = fmt.Sprintf("%s %s %s", prefix, icon, c.Name)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
107
service/slack.go
107
service/slack.go
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/nlopes/slack"
|
||||
|
||||
"github.com/erroneousboat/slack-term/components"
|
||||
"github.com/erroneousboat/slack-term/config"
|
||||
)
|
||||
|
||||
@ -30,15 +31,6 @@ type SlackService struct {
|
||||
CurrentUsername string
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
ID string
|
||||
Name string
|
||||
Topic string
|
||||
Type string
|
||||
UserID string
|
||||
Presence string
|
||||
}
|
||||
|
||||
// NewSlackService is the constructor for the SlackService and will initialize
|
||||
// the RTM and a Client
|
||||
func NewSlackService(token string) (*SlackService, error) {
|
||||
@ -84,7 +76,7 @@ func NewSlackService(token string) (*SlackService, error) {
|
||||
// Because the channels are of different types, we will append them to
|
||||
// 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() []Channel {
|
||||
func (s *SlackService) GetChannels() []string {
|
||||
var chans []Channel
|
||||
|
||||
// Channel
|
||||
@ -159,7 +151,33 @@ func (s *SlackService) GetChannels() []Channel {
|
||||
|
||||
s.Channels = chans
|
||||
|
||||
return chans
|
||||
var channels []string
|
||||
for _, chn := range s.Channels {
|
||||
channels = append(channels, chn.ToString())
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
// ChannelsToString will relay the string representation for a channel
|
||||
func (s *SlackService) ChannelsToString() []string {
|
||||
var channels []string
|
||||
for _, chn := range s.Channels {
|
||||
channels = append(channels, chn.ToString())
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
// SetPresenceChannelEvent will set the presence of a IM channel
|
||||
func (s *SlackService) SetPresenceChannelEvent(userID string, presence string) {
|
||||
// Get the correct Channel from svc.Channels
|
||||
var index int
|
||||
for i, channel := range s.Channels {
|
||||
if userID == channel.UserID {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
s.Channels[index].Presence = presence
|
||||
}
|
||||
|
||||
// GetSlackChannel returns the representation of a slack channel
|
||||
@ -199,8 +217,45 @@ func (s *SlackService) SetChannelReadMark(channel interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// MarkAsRead will set the channel as read
|
||||
func (s *SlackService) MarkAsRead(channelID int) {
|
||||
channel := s.Channels[channelID]
|
||||
|
||||
if channel.Notification {
|
||||
channel.Notification = false
|
||||
|
||||
switch channel.Type {
|
||||
case ChannelTypeChannel:
|
||||
s.Client.SetChannelReadMark(
|
||||
channel.ID, fmt.Sprintf("%f",
|
||||
float64(time.Now().Unix())),
|
||||
)
|
||||
case ChannelTypeGroup:
|
||||
s.Client.SetGroupReadMark(
|
||||
channel.ID, fmt.Sprintf("%f",
|
||||
float64(time.Now().Unix())),
|
||||
)
|
||||
case ChannelTypeIM:
|
||||
s.Client.MarkIMChannel(
|
||||
channel.ID, fmt.Sprintf("%f",
|
||||
float64(time.Now().Unix())),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarkAsUnread will set the channel as unread
|
||||
func (s *SlackService) MarkAsUnread(channelID int) {
|
||||
channel := s.Channels[channelID]
|
||||
|
||||
if !channel.Notification {
|
||||
channel.Notification = true
|
||||
}
|
||||
}
|
||||
|
||||
// SendMessage will send a message to a particular channel
|
||||
func (s *SlackService) SendMessage(channel string, message string) {
|
||||
func (s *SlackService) SendMessage(channelID int, message string) {
|
||||
|
||||
// https://godoc.org/github.com/nlopes/slack#PostMessageParameters
|
||||
postParams := slack.PostMessageParameters{
|
||||
AsUser: true,
|
||||
@ -208,7 +263,7 @@ func (s *SlackService) SendMessage(channel string, message string) {
|
||||
}
|
||||
|
||||
// https://godoc.org/github.com/nlopes/slack#Client.PostMessage
|
||||
s.Client.PostMessage(channel, message, postParams)
|
||||
s.Client.PostMessage(s.Channels[channelID].ID, message, postParams)
|
||||
}
|
||||
|
||||
// GetMessages will get messages for a channel, group or im channel delimited
|
||||
@ -313,14 +368,13 @@ func (s *SlackService) CreateMessage(message slack.Message) []string {
|
||||
intTime := int64(floatTime)
|
||||
|
||||
// Format message
|
||||
msg := fmt.Sprintf(
|
||||
"[%s] <%s> %s",
|
||||
time.Unix(intTime, 0).Format("15:04"),
|
||||
name,
|
||||
parseMessage(s, message.Text),
|
||||
)
|
||||
msg := components.Message{
|
||||
Time: time.Unix(intTime, 0),
|
||||
Name: name,
|
||||
Content: parseMessage(s, message.Text),
|
||||
}
|
||||
|
||||
msgs = append(msgs, msg)
|
||||
msgs = append(msgs, msg.ToString())
|
||||
|
||||
return msgs
|
||||
}
|
||||
@ -379,14 +433,13 @@ func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent
|
||||
intTime := int64(floatTime)
|
||||
|
||||
// Format message
|
||||
msg := fmt.Sprintf(
|
||||
"[%s] <%s> %s",
|
||||
time.Unix(intTime, 0).Format("15:04"),
|
||||
name,
|
||||
parseMessage(s, message.Text),
|
||||
)
|
||||
msg := components.Message{
|
||||
Time: time.Unix(intTime, 0),
|
||||
Name: name,
|
||||
Content: parseMessage(s, message.Text),
|
||||
}
|
||||
|
||||
msgs = append(msgs, msg)
|
||||
msgs = append(msgs, msg.ToString())
|
||||
|
||||
return msgs
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func CreateView(svc *service.SlackService) *View {
|
||||
// Channels: fill the component
|
||||
slackChans := svc.GetChannels()
|
||||
channels.SetChannels(slackChans)
|
||||
channels.SetPresenceChannels(slackChans)
|
||||
// channels.SetPresenceChannels(slackChans)
|
||||
|
||||
// Chat: create the component
|
||||
chat := components.CreateChatComponent(input.Par.Height)
|
||||
@ -36,7 +36,7 @@ func CreateView(svc *service.SlackService) *View {
|
||||
chat.GetMaxItems(),
|
||||
)
|
||||
chat.SetMessages(slackMsgs)
|
||||
chat.SetBorderLabel(svc.Channels[channels.SelectedChannel])
|
||||
chat.SetBorderLabel(svc.Channels[channels.SelectedChannel].GetChannelName())
|
||||
|
||||
// Debug: create the component
|
||||
debug := components.CreateDebugComponent(input.Par.Height)
|
||||
|
Loading…
Reference in New Issue
Block a user