diff --git a/matrix.go b/matrix.go index f578ac3..4dca289 100644 --- a/matrix.go +++ b/matrix.go @@ -121,18 +121,19 @@ func (c *MatrixContainer) UpdateRoomList() { func (c *MatrixContainer) Start() { defer c.gmx.Recover() - c.debug.Print("Starting sync...") - c.running = true - c.ui.SetView(ViewMain) c.client.Store = c.config.Session - c.UpdateRoomList() - - syncer := c.client.Syncer.(*gomatrix.DefaultSyncer) + syncer := gomatrix.NewDefaultSyncer(c.config.Session.MXID, c.config.Session) syncer.OnEventType("m.room.message", c.HandleMessage) syncer.OnEventType("m.room.member", c.HandleMembership) 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 { select { case <-c.stop: @@ -152,17 +153,23 @@ func (c *MatrixContainer) Start() { func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) { message, _ := evt.Content["body"].(string) - timestamp := time.Now() - if evt.Timestamp != 0 { - timestamp = time.Unix(evt.Timestamp/1000, evt.Timestamp%1000*1000) + room := c.ui.MainView().GetRoom(evt.RoomID) + if room != nil { + 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) { + membership, _ := evt.Content["membership"].(string) if evt.StateKey != nil && *evt.StateKey == c.config.Session.MXID { - membership, _ := evt.Content["membership"].(string) prevMembership := "leave" if evt.Unsigned.PrevContent != nil { prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string) @@ -175,6 +182,34 @@ func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) { } else if membership == "leave" { 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 { - room := c.client.Store.LoadRoom(roomID) - if len(room.State) == 0 { + room := c.config.Session.LoadRoom(roomID) + if room != nil && len(room.State) == 0 { events := c.getState(room.ID) if events != nil { for _, event := range events { diff --git a/message-view.go b/message-view.go index 633c551..6029f6a 100644 --- a/message-view.go +++ b/message-view.go @@ -93,11 +93,9 @@ type MessageView struct { totalHeight int messages []*Message - - debug DebugPrinter } -func NewMessageView(debug DebugPrinter) *MessageView { +func NewMessageView() *MessageView { return &MessageView{ Box: tview.NewBox(), MaxSenderWidth: 20, @@ -113,8 +111,6 @@ func NewMessageView(debug DebugPrinter) *MessageView { firstDisplayMessage: -1, lastDisplayMessage: -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 ( TimestampSenderGap = 1 SenderSeparatorGap = 1 @@ -246,7 +260,9 @@ func (view *MessageView) Draw(screen tcell.Screen) { } view.writeLine(screen, message.Timestamp, x, senderAtLine, tcell.ColorDefault) 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 { diff --git a/room-view.go b/room-view.go index 6764e85..4b6feac 100644 --- a/room-view.go +++ b/room-view.go @@ -21,6 +21,7 @@ import ( "hash/fnv" "sort" "strings" + "time" "github.com/gdamore/tcell" "maunium.net/go/gomatrix" @@ -36,7 +37,7 @@ type RoomView struct { userList *tview.TextView room *gomatrix.Room - debug DebugPrinter + parent *MainView } var colorNames []string @@ -51,15 +52,15 @@ func init() { sort.Sort(sort.StringSlice(colorNames)) } -func NewRoomView(debug DebugPrinter, room *gomatrix.Room) *RoomView { +func NewRoomView(parent *MainView, room *gomatrix.Room) *RoomView { view := &RoomView{ Box: tview.NewBox(), topic: tview.NewTextView(), - content: NewMessageView(debug), + content: NewMessageView(), status: tview.NewTextView(), userList: tview.NewTextView(), room: room, - debug: debug, + parent: parent, } view.topic. 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 { return view.content } func getColorName(s string) string { - h := fnv.New32a() - h.Write([]byte(s)) - return colorNames[int(h.Sum32())%len(colorNames)] + switch s { + case "-->": + return "green" + case "<--": + return "red" + case "---": + return "yellow" + default: + h := fnv.New32a() + h.Write([]byte(s)) + return colorNames[int(h.Sum32())%len(colorNames)] + } } func getColor(s string) tcell.Color { - h := fnv.New32a() - h.Write([]byte(s)) - return tcell.ColorNames[colorNames[int(h.Sum32())%len(colorNames)]] + return tcell.ColorNames[getColorName(s)] } func color(s string) string { @@ -129,12 +148,29 @@ func color(s string) string { } func (view *RoomView) UpdateUserList() { - var buf strings.Builder + var joined strings.Builder + var invited strings.Builder for _, user := range view.room.GetMembers() { if user.Membership == "join" { - buf.WriteString(color(user.DisplayName)) - buf.WriteRune('\n') + joined.WriteString(color(user.DisplayName)) + 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() } diff --git a/session.go b/session.go index 44f0787..b679076 100644 --- a/session.go +++ b/session.go @@ -86,8 +86,8 @@ func (s *Session) LoadNextBatch(_ string) string { } func (s *Session) LoadRoom(mxid string) *gomatrix.Room { - room, ok := s.Rooms[mxid] - if !ok || room == nil { + room, _ := s.Rooms[mxid] + if room == nil { room = gomatrix.NewRoom(mxid) s.SaveRoom(room) } diff --git a/view-main.go b/view-main.go index 219e603..4696afb 100644 --- a/view-main.go +++ b/view-main.go @@ -111,15 +111,8 @@ func findWordToTabComplete(text string) string { return output } -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 *MainView) GetRoom(id string) *RoomView { + return view.rooms[id] } func (view *MainView) InputTabComplete(text string, cursorOffset int) string { @@ -231,7 +224,7 @@ func (view *MainView) addRoom(index int, room string) { view.SwitchRoom(index) }) if !view.roomView.HasPage(room) { - roomView := NewRoomView(view.debug, roomStore) + roomView := NewRoomView(view, roomStore) view.rooms[room] = roomView view.roomView.AddPage(room, roomView, true, false) roomView.UpdateUserList() @@ -285,17 +278,13 @@ func (view *MainView) SetTyping(room string, users []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] if ok { - member := roomView.room.GetMember(sender) - if member != nil { - sender = member.DisplayName - } - roomView.content.AddMessage(id, sender, message, timestamp) + roomView.content.AddMessage("", "*", message, time.Now()) view.parent.Render() } } + +func (view *MainView) Render() { + view.parent.Render() +}