Add support for m.emote. Fixes #6

This commit is contained in:
Tulir Asokan 2018-03-22 21:44:46 +02:00
parent 702a75a8c0
commit 152b89ed5e
9 changed files with 102 additions and 53 deletions

View File

@ -28,7 +28,7 @@ type MatrixContainer interface {
Login(user, password string) error Login(user, password string) error
Start() Start()
Stop() Stop()
SendMessage(roomID, message string) (string, error) SendMessage(roomID, msgtype, message string) (string, error)
SendTyping(roomID string, typing bool) SendTyping(roomID string, typing bool)
JoinRoom(roomID string) error JoinRoom(roomID string) error
LeaveRoom(roomID string) error LeaveRoom(roomID string) error

View File

@ -47,7 +47,7 @@ type MainView interface {
SaveAllHistory() SaveAllHistory()
SetTyping(roomID string, users []string) SetTyping(roomID string, users []string)
AddServiceMessage(roomID string, message string) AddServiceMessage(roomID *widget.RoomView, message string)
ProcessMessageEvent(evt *gomatrix.Event) (*widget.RoomView, *types.Message) ProcessMessageEvent(evt *gomatrix.Event) (*widget.RoomView, *types.Message)
ProcessMembershipEvent(evt *gomatrix.Event, new bool) (*widget.RoomView, *types.Message) ProcessMembershipEvent(evt *gomatrix.Event, new bool) (*widget.RoomView, *types.Message)
} }

View File

@ -280,10 +280,11 @@ func (c *Container) HandleTyping(evt *gomatrix.Event) {
} }
// SendMessage sends a message with the given text to the given room. // SendMessage sends a message with the given text to the given room.
func (c *Container) SendMessage(roomID, text string) (string, error) { func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) {
defer c.gmx.Recover() defer c.gmx.Recover()
c.SendTyping(roomID, false) c.SendTyping(roomID, false)
resp, err := c.client.SendText(roomID, text) resp, err := c.client.SendMessageEvent(roomID, "m.room.message",
gomatrix.TextMessage{MsgType: msgtype, Body: text})
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -225,6 +225,11 @@ func (room *Room) GetMember(userID string) *Member {
return member return member
} }
// GetSessionOwner returns the Member instance of the user whose session this room was created for.
func (room *Room) GetSessionOwner() *Member {
return room.GetMember(room.SessionUserID)
}
// NewRoom creates a new Room with the given ID // NewRoom creates a new Room with the given ID
func NewRoom(roomID, owner string) *Room { func NewRoom(roomID, owner string) *Room {
return &Room{ return &Room{

View File

@ -17,6 +17,7 @@
package types package types
import ( import (
"fmt"
"regexp" "regexp"
"strings" "strings"
@ -26,13 +27,15 @@ import (
type Message struct { type Message struct {
BasicMeta BasicMeta
Type string
ID string ID string
Text string Text string
sending bool
buffer []string buffer []string
prevBufferWidth int prevBufferWidth int
} }
func NewMessage(id, sender, text, timestamp, date string, senderColor tcell.Color) *Message { func NewMessage(id, sender, msgtype, text, timestamp, date string, senderColor tcell.Color) *Message {
return &Message{ return &Message{
BasicMeta: BasicMeta{ BasicMeta: BasicMeta{
Sender: sender, Sender: sender,
@ -42,9 +45,11 @@ func NewMessage(id, sender, text, timestamp, date string, senderColor tcell.Colo
TextColor: tcell.ColorDefault, TextColor: tcell.ColorDefault,
TimestampColor: tcell.ColorDefault, TimestampColor: tcell.ColorDefault,
}, },
Type: msgtype,
Text: text, Text: text,
ID: id, ID: id,
prevBufferWidth: 0, prevBufferWidth: 0,
sending: false,
} }
} }
@ -65,7 +70,11 @@ func (message *Message) CalculateBuffer(width int) {
return return
} }
message.buffer = []string{} message.buffer = []string{}
forcedLinebreaks := strings.Split(message.Text, "\n") text := message.Text
if message.Type == "m.emote" {
text = fmt.Sprintf("* %s %s", message.Sender, message.Text)
}
forcedLinebreaks := strings.Split(text, "\n")
newlines := 0 newlines := 0
for _, str := range forcedLinebreaks { for _, str := range forcedLinebreaks {
if len(str) == 0 && newlines < 1 { if len(str) == 0 && newlines < 1 {
@ -94,6 +103,22 @@ func (message *Message) CalculateBuffer(width int) {
message.prevBufferWidth = width message.prevBufferWidth = width
} }
func (message *Message) GetDisplaySender() string {
if message.sending {
return "Sending..."
}
switch message.Type {
case "m.emote":
return ""
default:
return message.Sender
}
}
func (message *Message) SetIsSending(sending bool) {
message.sending = sending
}
func (message *Message) RecalculateBuffer() { func (message *Message) RecalculateBuffer() {
message.CalculateBuffer(message.prevBufferWidth) message.CalculateBuffer(message.prevBufferWidth)
} }

View File

@ -22,6 +22,7 @@ import (
type MessageMeta interface { type MessageMeta interface {
GetSender() string GetSender() string
GetDisplaySender() string
GetSenderColor() tcell.Color GetSenderColor() tcell.Color
GetTextColor() tcell.Color GetTextColor() tcell.Color
GetTimestampColor() tcell.Color GetTimestampColor() tcell.Color
@ -34,6 +35,10 @@ type BasicMeta struct {
SenderColor, TextColor, TimestampColor tcell.Color SenderColor, TextColor, TimestampColor tcell.Color
} }
func (meta *BasicMeta) GetDisplaySender() string {
return meta.Sender
}
func (meta *BasicMeta) GetSender() string { func (meta *BasicMeta) GetSender() string {
return meta.Sender return meta.Sender
} }

View File

@ -19,7 +19,6 @@ package ui
import ( import (
"fmt" "fmt"
"sort" "sort"
"strconv"
"strings" "strings"
"time" "time"
"unicode" "unicode"
@ -123,42 +122,40 @@ func (view *MainView) InputSubmit(roomView *widget.RoomView, text string) {
args := strings.SplitN(text, " ", 2) args := strings.SplitN(text, " ", 2)
command := strings.ToLower(args[0]) command := strings.ToLower(args[0])
args = args[1:] args = args[1:]
go view.HandleCommand(roomView.Room.ID, command, args) go view.HandleCommand(roomView, command, args)
} else { } else {
view.SendMessage(roomView.Room.ID, text) view.SendMessage(roomView, text)
} }
roomView.SetInputText("") roomView.SetInputText("")
} }
func (view *MainView) SendMessage(room, text string) { func (view *MainView) SendMessage(roomView *widget.RoomView, text string) {
now := time.Now() tempMessage := roomView.NewTempMessage("m.text", text)
roomView := view.GetRoom(room) go view.sendTempMessage(roomView, tempMessage)
tempMessage := roomView.NewMessage(
strconv.FormatInt(now.UnixNano(), 10),
"Sending...", text, now)
tempMessage.TimestampColor = tcell.ColorGray
tempMessage.TextColor = tcell.ColorGray
tempMessage.SenderColor = tcell.ColorGray
roomView.AddMessage(tempMessage, widget.AppendMessage)
go func() {
defer view.gmx.Recover()
eventID, err := view.matrix.SendMessage(room, text)
if err != nil {
tempMessage.TextColor = tcell.ColorRed
tempMessage.TimestampColor = tcell.ColorRed
tempMessage.SenderColor = tcell.ColorRed
tempMessage.Sender = "Error"
roomView.SetStatus(fmt.Sprintf("Failed to send message: %s", err))
} else {
roomView.MessageView().UpdateMessageID(tempMessage, eventID)
}
}()
} }
func (view *MainView) HandleCommand(room, command string, args []string) { func (view *MainView) sendTempMessage(roomView *widget.RoomView, tempMessage *types.Message) {
defer view.gmx.Recover()
eventID, err := view.matrix.SendMessage(roomView.Room.ID, tempMessage.Type, tempMessage.Text)
if err != nil {
tempMessage.TextColor = tcell.ColorRed
tempMessage.TimestampColor = tcell.ColorRed
tempMessage.SenderColor = tcell.ColorRed
tempMessage.Sender = "Error"
roomView.SetStatus(fmt.Sprintf("Failed to send message: %s", err))
} else {
roomView.MessageView().UpdateMessageID(tempMessage, eventID)
}
}
func (view *MainView) HandleCommand(roomView *widget.RoomView, command string, args []string) {
defer view.gmx.Recover() defer view.gmx.Recover()
debug.Print("Handling command", command, args) debug.Print("Handling command", command, args)
switch command { switch command {
case "/me":
tempMessage := roomView.NewTempMessage("m.emote", strings.Join(args, " "))
go view.sendTempMessage(roomView, tempMessage)
view.parent.Render()
case "/quit": case "/quit":
view.gmx.Stop() view.gmx.Stop()
case "/clearcache": case "/clearcache":
@ -169,15 +166,15 @@ func (view *MainView) HandleCommand(room, command string, args []string) {
case "/part": case "/part":
fallthrough fallthrough
case "/leave": case "/leave":
debug.Print(view.matrix.LeaveRoom(room)) debug.Print("Leave room result:", view.matrix.LeaveRoom(roomView.Room.ID))
case "/join": case "/join":
if len(args) == 0 { if len(args) == 0 {
view.AddServiceMessage(room, "Usage: /join <room>") view.AddServiceMessage(roomView, "Usage: /join <room>")
break break
} }
debug.Print(view.matrix.JoinRoom(args[0])) debug.Print("Join room result:", view.matrix.JoinRoom(args[0]))
default: default:
view.AddServiceMessage(room, "Unknown command.") view.AddServiceMessage(roomView, "Unknown command.")
} }
} }
@ -331,13 +328,12 @@ func (view *MainView) SetTyping(room string, users []string) {
} }
} }
func (view *MainView) AddServiceMessage(room, message string) { func (view *MainView) AddServiceMessage(roomView *widget.RoomView, text string) {
roomView, ok := view.rooms[room] message := roomView.NewMessage("", "*", "gomuks.service", text, time.Now())
if ok { message.TextColor = tcell.ColorGray
message := roomView.NewMessage("", "*", message, time.Now()) message.SenderColor = tcell.ColorGray
roomView.AddMessage(message, widget.AppendMessage) roomView.AddMessage(message, widget.AppendMessage)
view.parent.Render() view.parent.Render()
}
} }
func (view *MainView) LoadMoreHistory(room string) { func (view *MainView) LoadMoreHistory(room string) {
@ -375,7 +371,7 @@ func (view *MainView) LoadHistory(room string, initial bool) {
debug.Print("Loading history for", room, "starting from", batch, "(initial:", initial, ")") debug.Print("Loading history for", room, "starting from", batch, "(initial:", initial, ")")
history, prevBatch, err := view.matrix.GetHistory(roomView.Room.ID, batch, 50) history, prevBatch, err := view.matrix.GetHistory(roomView.Room.ID, batch, 50)
if err != nil { if err != nil {
view.AddServiceMessage(room, "Failed to fetch history") view.AddServiceMessage(roomView, "Failed to fetch history")
debug.Print("Failed to fetch history for", roomView.Room.ID, err) debug.Print("Failed to fetch history for", roomView.Room.ID, err)
return return
} }
@ -404,7 +400,8 @@ func (view *MainView) ProcessMessageEvent(evt *gomatrix.Event) (room *widget.Roo
room = view.GetRoom(evt.RoomID) room = view.GetRoom(evt.RoomID)
if room != nil { if room != nil {
text, _ := evt.Content["body"].(string) text, _ := evt.Content["body"].(string)
message = room.NewMessage(evt.ID, evt.Sender, text, unixToTime(evt.Timestamp)) msgtype, _ := evt.Content["msgtype"].(string)
message = room.NewMessage(evt.ID, evt.Sender, msgtype, text, unixToTime(evt.Timestamp))
} }
return return
} }
@ -452,7 +449,7 @@ func (view *MainView) ProcessMembershipEvent(evt *gomatrix.Event, new bool) (roo
room = nil room = nil
return return
} }
message = room.NewMessage(evt.ID, sender, text, unixToTime(evt.Timestamp)) message = room.NewMessage(evt.ID, sender, "m.room.member", text, unixToTime(evt.Timestamp))
message.TextColor = tcell.ColorGreen message.TextColor = tcell.ColorGreen
} }
return return

View File

@ -74,8 +74,8 @@ func NewMessageView() *MessageView {
} }
} }
func (view *MessageView) NewMessage(id, sender, text string, timestamp time.Time) *types.Message { func (view *MessageView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) *types.Message {
return types.NewMessage(id, sender, text, return types.NewMessage(id, sender, msgtype, text,
timestamp.Format(view.TimestampFormat), timestamp.Format(view.TimestampFormat),
timestamp.Format(view.DateFormat), timestamp.Format(view.DateFormat),
GetHashColor(sender)) GetHashColor(sender))
@ -151,6 +151,8 @@ func (view *MessageView) AddMessage(message *types.Message, direction MessageDir
msg, messageExists := view.messageIDs[message.ID] msg, messageExists := view.messageIDs[message.ID]
if msg != nil && messageExists { if msg != nil && messageExists {
message.CopyTo(msg) message.CopyTo(msg)
message = msg
message.SetIsSending(false)
direction = IgnoreMessage direction = IgnoreMessage
} }
@ -338,9 +340,9 @@ func (view *MessageView) Draw(screen tcell.Screen) {
if len(meta.GetTimestamp()) > 0 { if len(meta.GetTimestamp()) > 0 {
view.writeLine(screen, meta.GetTimestamp(), x, y+line, meta.GetTimestampColor()) view.writeLine(screen, meta.GetTimestamp(), x, y+line, meta.GetTimestampColor())
} }
if len(meta.GetSender()) > 0 && (prevMeta == nil || meta.GetSender() != prevMeta.GetSender()) { if prevMeta == nil || meta.GetSender() != prevMeta.GetSender() {
view.writeLineRight( view.writeLineRight(
screen, meta.GetSender(), screen, meta.GetDisplaySender(),
x+usernameOffsetX, y+line, x+usernameOffsetX, y+line,
view.widestSender, meta.GetSenderColor()) view.widestSender, meta.GetSenderColor())
} }

View File

@ -19,6 +19,7 @@ package widget
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@ -239,12 +240,25 @@ func (view *RoomView) UpdateUserList() {
} }
} }
func (view *RoomView) NewMessage(id, sender, text string, timestamp time.Time) *types.Message { func (view *RoomView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) *types.Message {
member := view.Room.GetMember(sender) member := view.Room.GetMember(sender)
if member != nil { if member != nil {
sender = member.DisplayName sender = member.DisplayName
} }
return view.content.NewMessage(id, sender, text, timestamp) return view.content.NewMessage(id, sender, msgtype, text, timestamp)
}
func (view *RoomView) NewTempMessage(msgtype, text string) *types.Message {
now := time.Now()
id := strconv.FormatInt(now.UnixNano(), 10)
sender := view.Room.GetSessionOwner().DisplayName
message := view.NewMessage(id, sender, msgtype, text, now)
message.SetIsSending(true)
message.TimestampColor = tcell.ColorGray
message.TextColor = tcell.ColorGray
message.SenderColor = tcell.ColorGray
view.AddMessage(message, AppendMessage)
return message
} }
func (view *RoomView) AddMessage(message *types.Message, direction MessageDirection) { func (view *RoomView) AddMessage(message *types.Message, direction MessageDirection) {