Refactoring, right-align users and fix join/leave messages

This commit is contained in:
Tulir Asokan 2018-03-17 15:48:31 +02:00
parent ba379a1b4a
commit b0ebb4b9a4
5 changed files with 130 additions and 54 deletions

View File

@ -121,18 +121,19 @@ func (c *MatrixContainer) UpdateRoomList() {
func (c *MatrixContainer) Start() { func (c *MatrixContainer) Start() {
defer c.gmx.Recover() defer c.gmx.Recover()
c.debug.Print("Starting sync...")
c.running = true
c.ui.SetView(ViewMain)
c.client.Store = c.config.Session c.client.Store = c.config.Session
c.UpdateRoomList() syncer := gomatrix.NewDefaultSyncer(c.config.Session.MXID, c.config.Session)
syncer := c.client.Syncer.(*gomatrix.DefaultSyncer)
syncer.OnEventType("m.room.message", c.HandleMessage) syncer.OnEventType("m.room.message", c.HandleMessage)
syncer.OnEventType("m.room.member", c.HandleMembership) syncer.OnEventType("m.room.member", c.HandleMembership)
syncer.OnEventType("m.typing", c.HandleTyping) syncer.OnEventType("m.typing", c.HandleTyping)
c.client.Syncer = syncer
c.UpdateRoomList()
c.debug.Print("Starting sync...")
c.running = true
c.ui.SetView(ViewMain)
for { for {
select { select {
case <-c.stop: case <-c.stop:
@ -152,17 +153,23 @@ func (c *MatrixContainer) Start() {
func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) { func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) {
message, _ := evt.Content["body"].(string) message, _ := evt.Content["body"].(string)
timestamp := time.Now() room := c.ui.MainView().GetRoom(evt.RoomID)
if evt.Timestamp != 0 { if room != nil {
timestamp = time.Unix(evt.Timestamp/1000, evt.Timestamp%1000*1000) room.AddMessage(evt.ID, evt.Sender, message, unixToTime(evt.Timestamp))
}
} }
c.ui.MainView().AddRealMessage(evt.RoomID, evt.ID, evt.Sender, message, timestamp) func unixToTime(unix int64) time.Time {
timestamp := time.Now()
if unix != 0 {
timestamp = time.Unix(unix/1000, unix%1000*1000)
}
return timestamp
} }
func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) { func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) {
if evt.StateKey != nil && *evt.StateKey == c.config.Session.MXID {
membership, _ := evt.Content["membership"].(string) membership, _ := evt.Content["membership"].(string)
if evt.StateKey != nil && *evt.StateKey == c.config.Session.MXID {
prevMembership := "leave" prevMembership := "leave"
if evt.Unsigned.PrevContent != nil { if evt.Unsigned.PrevContent != nil {
prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string) prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string)
@ -175,6 +182,34 @@ func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) {
} else if membership == "leave" { } else if membership == "leave" {
c.ui.MainView().RemoveRoom(evt.RoomID) c.ui.MainView().RemoveRoom(evt.RoomID)
} }
return
}
room := c.ui.MainView().GetRoom(evt.RoomID)
// TODO this shouldn't be necessary
room.room.UpdateState(evt)
if room != nil {
var message, sender string
if membership == "invite" {
sender = "---"
message = fmt.Sprintf("%s invited %s.", evt.Sender, *evt.StateKey)
} else if membership == "join" {
sender = "-->"
message = fmt.Sprintf("%s joined the room.", *evt.StateKey)
} else if membership == "leave" {
sender = "<--"
if evt.Sender != *evt.StateKey {
reason, _ := evt.Content["reason"].(string)
message = fmt.Sprintf("%s kicked %s: %s", evt.Sender, *evt.StateKey, reason)
} else {
message = fmt.Sprintf("%s left the room.", *evt.StateKey)
}
} else {
return
}
room.UpdateUserList()
room.AddMessage(evt.ID, sender, message, unixToTime(evt.Timestamp))
} }
} }
@ -240,8 +275,8 @@ func (c *MatrixContainer) getState(roomID string) []*gomatrix.Event {
} }
func (c *MatrixContainer) GetRoom(roomID string) *gomatrix.Room { func (c *MatrixContainer) GetRoom(roomID string) *gomatrix.Room {
room := c.client.Store.LoadRoom(roomID) room := c.config.Session.LoadRoom(roomID)
if len(room.State) == 0 { if room != nil && len(room.State) == 0 {
events := c.getState(room.ID) events := c.getState(room.ID)
if events != nil { if events != nil {
for _, event := range events { for _, event := range events {

View File

@ -93,11 +93,9 @@ type MessageView struct {
totalHeight int totalHeight int
messages []*Message messages []*Message
debug DebugPrinter
} }
func NewMessageView(debug DebugPrinter) *MessageView { func NewMessageView() *MessageView {
return &MessageView{ return &MessageView{
Box: tview.NewBox(), Box: tview.NewBox(),
MaxSenderWidth: 20, MaxSenderWidth: 20,
@ -113,8 +111,6 @@ func NewMessageView(debug DebugPrinter) *MessageView {
firstDisplayMessage: -1, firstDisplayMessage: -1,
lastDisplayMessage: -1, lastDisplayMessage: -1,
totalHeight: -1, totalHeight: -1,
debug: debug,
} }
} }
@ -207,6 +203,24 @@ func (view *MessageView) writeLine(screen tcell.Screen, line string, x, y int, c
} }
} }
func (view *MessageView) writeLineRight(screen tcell.Screen, line string, x, y, maxWidth int, color tcell.Color) {
offsetX := maxWidth - runewidth.StringWidth(line)
if offsetX < 0 {
offsetX = 0
}
for _, ch := range line {
chWidth := runewidth.RuneWidth(ch)
if chWidth == 0 {
continue
}
for localOffset := 0; localOffset < chWidth; localOffset++ {
screen.SetContent(x+offsetX+localOffset, y, ch, nil, tcell.StyleDefault.Foreground(color))
}
offsetX += chWidth
}
}
const ( const (
TimestampSenderGap = 1 TimestampSenderGap = 1
SenderSeparatorGap = 1 SenderSeparatorGap = 1
@ -246,7 +260,9 @@ func (view *MessageView) Draw(screen tcell.Screen) {
} }
view.writeLine(screen, message.Timestamp, x, senderAtLine, tcell.ColorDefault) view.writeLine(screen, message.Timestamp, x, senderAtLine, tcell.ColorDefault)
if message.RenderSender || i == view.lastDisplayMessage { if message.RenderSender || i == view.lastDisplayMessage {
view.writeLine(screen, message.Sender, x+usernameOffsetX, senderAtLine, message.senderColor) view.writeLineRight(screen, message.Sender,
x+usernameOffsetX, senderAtLine,
view.widestSender, message.senderColor)
} }
for num, line := range message.buffer { for num, line := range message.buffer {

View File

@ -21,6 +21,7 @@ import (
"hash/fnv" "hash/fnv"
"sort" "sort"
"strings" "strings"
"time"
"github.com/gdamore/tcell" "github.com/gdamore/tcell"
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
@ -36,7 +37,7 @@ type RoomView struct {
userList *tview.TextView userList *tview.TextView
room *gomatrix.Room room *gomatrix.Room
debug DebugPrinter parent *MainView
} }
var colorNames []string var colorNames []string
@ -51,15 +52,15 @@ func init() {
sort.Sort(sort.StringSlice(colorNames)) sort.Sort(sort.StringSlice(colorNames))
} }
func NewRoomView(debug DebugPrinter, room *gomatrix.Room) *RoomView { func NewRoomView(parent *MainView, room *gomatrix.Room) *RoomView {
view := &RoomView{ view := &RoomView{
Box: tview.NewBox(), Box: tview.NewBox(),
topic: tview.NewTextView(), topic: tview.NewTextView(),
content: NewMessageView(debug), content: NewMessageView(),
status: tview.NewTextView(), status: tview.NewTextView(),
userList: tview.NewTextView(), userList: tview.NewTextView(),
room: room, room: room,
debug: debug, parent: parent,
} }
view.topic. view.topic.
SetText(strings.Replace(room.GetTopic(), "\n", " ", -1)). SetText(strings.Replace(room.GetTopic(), "\n", " ", -1)).
@ -108,20 +109,38 @@ func (view *RoomView) SetTyping(users []string) {
} }
} }
func (view *RoomView) AutocompleteUser(existingText string) (completions []string) {
for _, user := range view.room.GetMembers() {
if strings.HasPrefix(user.DisplayName, existingText) {
completions = append(completions, user.DisplayName)
} else if strings.HasPrefix(user.UserID, existingText) {
completions = append(completions, user.UserID)
}
}
return
}
func (view *RoomView) MessageView() *MessageView { func (view *RoomView) MessageView() *MessageView {
return view.content return view.content
} }
func getColorName(s string) string { func getColorName(s string) string {
switch s {
case "-->":
return "green"
case "<--":
return "red"
case "---":
return "yellow"
default:
h := fnv.New32a() h := fnv.New32a()
h.Write([]byte(s)) h.Write([]byte(s))
return colorNames[int(h.Sum32())%len(colorNames)] return colorNames[int(h.Sum32())%len(colorNames)]
} }
}
func getColor(s string) tcell.Color { func getColor(s string) tcell.Color {
h := fnv.New32a() return tcell.ColorNames[getColorName(s)]
h.Write([]byte(s))
return tcell.ColorNames[colorNames[int(h.Sum32())%len(colorNames)]]
} }
func color(s string) string { func color(s string) string {
@ -129,12 +148,29 @@ func color(s string) string {
} }
func (view *RoomView) UpdateUserList() { func (view *RoomView) UpdateUserList() {
var buf strings.Builder var joined strings.Builder
var invited strings.Builder
for _, user := range view.room.GetMembers() { for _, user := range view.room.GetMembers() {
if user.Membership == "join" { if user.Membership == "join" {
buf.WriteString(color(user.DisplayName)) joined.WriteString(color(user.DisplayName))
buf.WriteRune('\n') joined.WriteRune('\n')
} else if user.Membership == "invite" {
invited.WriteString(color(user.DisplayName))
invited.WriteRune('\n')
} }
} }
view.userList.SetText(buf.String()) view.userList.Clear()
fmt.Fprintf(view.userList, "%s\n", joined.String())
if invited.Len() > 0 {
fmt.Fprintf(view.userList, "\nInvited:\n%s", invited.String())
}
}
func (view *RoomView) AddMessage(id, sender, message string, timestamp time.Time) {
member := view.room.GetMember(sender)
if member != nil {
sender = member.DisplayName
}
view.content.AddMessage(id, sender, message, timestamp)
view.parent.Render()
} }

View File

@ -86,8 +86,8 @@ func (s *Session) LoadNextBatch(_ string) string {
} }
func (s *Session) LoadRoom(mxid string) *gomatrix.Room { func (s *Session) LoadRoom(mxid string) *gomatrix.Room {
room, ok := s.Rooms[mxid] room, _ := s.Rooms[mxid]
if !ok || room == nil { if room == nil {
room = gomatrix.NewRoom(mxid) room = gomatrix.NewRoom(mxid)
s.SaveRoom(room) s.SaveRoom(room)
} }

View File

@ -111,15 +111,8 @@ func findWordToTabComplete(text string) string {
return output return output
} }
func (view *RoomView) AutocompleteUser(existingText string) (completions []string) { func (view *MainView) GetRoom(id string) *RoomView {
for _, user := range view.room.GetMembers() { return view.rooms[id]
if strings.HasPrefix(user.DisplayName, existingText) {
completions = append(completions, user.DisplayName)
} else if strings.HasPrefix(user.UserID, existingText) {
completions = append(completions, user.UserID)
}
}
return
} }
func (view *MainView) InputTabComplete(text string, cursorOffset int) string { func (view *MainView) InputTabComplete(text string, cursorOffset int) string {
@ -231,7 +224,7 @@ func (view *MainView) addRoom(index int, room string) {
view.SwitchRoom(index) view.SwitchRoom(index)
}) })
if !view.roomView.HasPage(room) { if !view.roomView.HasPage(room) {
roomView := NewRoomView(view.debug, roomStore) roomView := NewRoomView(view, roomStore)
view.rooms[room] = roomView view.rooms[room] = roomView
view.roomView.AddPage(room, roomView, true, false) view.roomView.AddPage(room, roomView, true, false)
roomView.UpdateUserList() roomView.UpdateUserList()
@ -285,17 +278,13 @@ func (view *MainView) SetTyping(room string, users []string) {
} }
func (view *MainView) AddMessage(room, message string) { func (view *MainView) AddMessage(room, message string) {
view.AddRealMessage(room, "", "*", message, time.Now())
}
func (view *MainView) AddRealMessage(room, id, sender, message string, timestamp time.Time) {
roomView, ok := view.rooms[room] roomView, ok := view.rooms[room]
if ok { if ok {
member := roomView.room.GetMember(sender) roomView.content.AddMessage("", "*", message, time.Now())
if member != nil {
sender = member.DisplayName
}
roomView.content.AddMessage(id, sender, message, timestamp)
view.parent.Render() view.parent.Render()
} }
} }
func (view *MainView) Render() {
view.parent.Render()
}