Fix bugs and add MessageView widget
This commit is contained in:
		@@ -160,7 +160,7 @@ func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) {
 | 
				
			|||||||
		timestamp = time.Unix(timestampInt64/1000, timestampInt64%1000*1000)
 | 
							timestamp = time.Unix(timestampInt64/1000, timestampInt64%1000*1000)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.ui.MainView().AddMessage(evt.RoomID, evt.Sender, message, timestamp)
 | 
						c.ui.MainView().AddRealMessage(evt.RoomID, evt.ID, evt.Sender, message, timestamp)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) {
 | 
					func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										260
									
								
								message-view.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								message-view.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,260 @@
 | 
				
			|||||||
 | 
					// gomuks - A terminal Matrix client written in Go.
 | 
				
			||||||
 | 
					// Copyright (C) 2018 Tulir Asokan
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gdamore/tcell"
 | 
				
			||||||
 | 
						"github.com/mattn/go-runewidth"
 | 
				
			||||||
 | 
						"maunium.net/go/tview"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Message struct {
 | 
				
			||||||
 | 
						ID           string
 | 
				
			||||||
 | 
						Sender       string
 | 
				
			||||||
 | 
						Text         string
 | 
				
			||||||
 | 
						Timestamp    string
 | 
				
			||||||
 | 
						RenderSender bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buffer      []string
 | 
				
			||||||
 | 
						senderColor tcell.Color
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
 | 
				
			||||||
 | 
						spacePattern    = regexp.MustCompile(`\s+`)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (message *Message) calculateBuffer(width int) {
 | 
				
			||||||
 | 
						if width < 1 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						message.buffer = []string{}
 | 
				
			||||||
 | 
						forcedLinebreaks := strings.Split(message.Text, "\n")
 | 
				
			||||||
 | 
						newlines := 0
 | 
				
			||||||
 | 
						for _, str := range forcedLinebreaks {
 | 
				
			||||||
 | 
							if len(str) == 0 && newlines < 1 {
 | 
				
			||||||
 | 
								message.buffer = append(message.buffer, "")
 | 
				
			||||||
 | 
								newlines++
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								newlines = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// From tview/textview.go#reindexBuffer()
 | 
				
			||||||
 | 
							for len(str) > 0 {
 | 
				
			||||||
 | 
								extract := runewidth.Truncate(str, width, "")
 | 
				
			||||||
 | 
								if len(extract) < len(str) {
 | 
				
			||||||
 | 
									if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 {
 | 
				
			||||||
 | 
										extract = str[:len(extract)+spaces[1]]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									matches := boundaryPattern.FindAllStringIndex(extract, -1)
 | 
				
			||||||
 | 
									if len(matches) > 0 {
 | 
				
			||||||
 | 
										extract = extract[:matches[len(matches)-1][1]]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								message.buffer = append(message.buffer, extract)
 | 
				
			||||||
 | 
								str = str[len(extract):]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MessageView struct {
 | 
				
			||||||
 | 
						*tview.Box
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ScrollOffset    int
 | 
				
			||||||
 | 
						MaxSenderWidth  int
 | 
				
			||||||
 | 
						TimestampFormat string
 | 
				
			||||||
 | 
						TimestampWidth  int
 | 
				
			||||||
 | 
						Separator       rune
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						widestSender        int
 | 
				
			||||||
 | 
						prevWidth           int
 | 
				
			||||||
 | 
						prevHeight          int
 | 
				
			||||||
 | 
						prevScrollOffset    int
 | 
				
			||||||
 | 
						firstDisplayMessage int
 | 
				
			||||||
 | 
						lastDisplayMessage  int
 | 
				
			||||||
 | 
						totalHeight         int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						messages []*Message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						debug DebugPrinter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewMessageView(debug DebugPrinter) *MessageView {
 | 
				
			||||||
 | 
						return &MessageView{
 | 
				
			||||||
 | 
							Box:             tview.NewBox(),
 | 
				
			||||||
 | 
							MaxSenderWidth:  20,
 | 
				
			||||||
 | 
							TimestampFormat: "15:04:05",
 | 
				
			||||||
 | 
							TimestampWidth:  8,
 | 
				
			||||||
 | 
							Separator:       '|',
 | 
				
			||||||
 | 
							ScrollOffset:    0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							widestSender:        5,
 | 
				
			||||||
 | 
							prevWidth:           -1,
 | 
				
			||||||
 | 
							prevHeight:          -1,
 | 
				
			||||||
 | 
							prevScrollOffset:    -1,
 | 
				
			||||||
 | 
							firstDisplayMessage: -1,
 | 
				
			||||||
 | 
							lastDisplayMessage:  -1,
 | 
				
			||||||
 | 
							totalHeight:         -1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							debug: debug,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) recalculateBuffers(width int) {
 | 
				
			||||||
 | 
						width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
 | 
				
			||||||
 | 
						for _, message := range view.messages {
 | 
				
			||||||
 | 
							message.calculateBuffer(width)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						view.prevWidth = width
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) AddMessage(id, sender, text string, timestamp time.Time) {
 | 
				
			||||||
 | 
						if len(sender) > view.widestSender {
 | 
				
			||||||
 | 
							view.widestSender = len(sender)
 | 
				
			||||||
 | 
							if view.widestSender > view.MaxSenderWidth {
 | 
				
			||||||
 | 
								view.widestSender = view.MaxSenderWidth
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						message := &Message{
 | 
				
			||||||
 | 
							ID:           id,
 | 
				
			||||||
 | 
							Sender:       sender,
 | 
				
			||||||
 | 
							RenderSender: true,
 | 
				
			||||||
 | 
							Text:         text,
 | 
				
			||||||
 | 
							Timestamp:    timestamp.Format(view.TimestampFormat),
 | 
				
			||||||
 | 
							senderColor:  getColor(sender),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, _, width, height := view.GetInnerRect()
 | 
				
			||||||
 | 
						width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
 | 
				
			||||||
 | 
						message.calculateBuffer(width)
 | 
				
			||||||
 | 
						if view.ScrollOffset > 0 {
 | 
				
			||||||
 | 
							view.ScrollOffset += len(message.buffer)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(view.messages) > 0 && view.messages[len(view.messages)-1].Sender == message.Sender {
 | 
				
			||||||
 | 
							message.RenderSender = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						view.messages = append(view.messages, message)
 | 
				
			||||||
 | 
						view.recalculateHeight(height)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) recalculateHeight(height int) {
 | 
				
			||||||
 | 
						view.firstDisplayMessage = -1
 | 
				
			||||||
 | 
						view.lastDisplayMessage = -1
 | 
				
			||||||
 | 
						view.totalHeight = 0
 | 
				
			||||||
 | 
						for i := len(view.messages) - 1; i >= 0; i-- {
 | 
				
			||||||
 | 
							prevTotalHeight := view.totalHeight
 | 
				
			||||||
 | 
							view.totalHeight += len(view.messages[i].buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if view.totalHeight < view.ScrollOffset {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							} else if view.firstDisplayMessage == -1 {
 | 
				
			||||||
 | 
								view.lastDisplayMessage = i
 | 
				
			||||||
 | 
								view.firstDisplayMessage = i
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if prevTotalHeight < height+view.ScrollOffset {
 | 
				
			||||||
 | 
								view.lastDisplayMessage = i
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						view.prevScrollOffset = view.ScrollOffset
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) PageUp() {
 | 
				
			||||||
 | 
						_, _, _, height := view.GetInnerRect()
 | 
				
			||||||
 | 
						view.ScrollOffset += height / 2
 | 
				
			||||||
 | 
						if view.ScrollOffset > view.totalHeight-height {
 | 
				
			||||||
 | 
							view.ScrollOffset = view.totalHeight - height + 5
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) PageDown() {
 | 
				
			||||||
 | 
						_, _, _, height := view.GetInnerRect()
 | 
				
			||||||
 | 
						view.ScrollOffset -= height / 2
 | 
				
			||||||
 | 
						if view.ScrollOffset < 0 {
 | 
				
			||||||
 | 
							view.ScrollOffset = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) writeLine(screen tcell.Screen, line string, x, y int, color tcell.Color) {
 | 
				
			||||||
 | 
						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
 | 
				
			||||||
 | 
						SenderMessageGap   = 3
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (view *MessageView) Draw(screen tcell.Screen) {
 | 
				
			||||||
 | 
						view.Box.Draw(screen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x, y, width, height := view.GetInnerRect()
 | 
				
			||||||
 | 
						if width != view.prevWidth {
 | 
				
			||||||
 | 
							view.recalculateBuffers(width)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if height != view.prevHeight || width != view.prevWidth || view.ScrollOffset != view.prevScrollOffset {
 | 
				
			||||||
 | 
							view.recalculateHeight(height)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						usernameOffsetX := view.TimestampWidth + TimestampSenderGap
 | 
				
			||||||
 | 
						messageOffsetX := usernameOffsetX + view.widestSender + SenderMessageGap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						separatorX := x + usernameOffsetX + view.widestSender + SenderSeparatorGap
 | 
				
			||||||
 | 
						for separatorY := y; separatorY < y+height; separatorY++ {
 | 
				
			||||||
 | 
							screen.SetContent(separatorX, separatorY, view.Separator, nil, tcell.StyleDefault)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if view.firstDisplayMessage == -1 || view.lastDisplayMessage == -1 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writeOffset := 0
 | 
				
			||||||
 | 
						for i := view.firstDisplayMessage; i >= view.lastDisplayMessage; i-- {
 | 
				
			||||||
 | 
							message := view.messages[i]
 | 
				
			||||||
 | 
							messageHeight := len(message.buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							senderAtLine := y + height - writeOffset - messageHeight
 | 
				
			||||||
 | 
							if senderAtLine < y {
 | 
				
			||||||
 | 
								senderAtLine = y
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for num, line := range message.buffer {
 | 
				
			||||||
 | 
								offsetY := height - messageHeight - writeOffset + num
 | 
				
			||||||
 | 
								if offsetY >= 0 {
 | 
				
			||||||
 | 
									view.writeLine(screen, line, x+messageOffsetX, y+offsetY, tcell.ColorDefault)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							writeOffset += messageHeight
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								room-view.go
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								room-view.go
									
									
									
									
									
								
							@@ -19,9 +19,8 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"hash/fnv"
 | 
						"hash/fnv"
 | 
				
			||||||
	"regexp"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gdamore/tcell"
 | 
						"github.com/gdamore/tcell"
 | 
				
			||||||
	"maunium.net/go/gomatrix"
 | 
						"maunium.net/go/gomatrix"
 | 
				
			||||||
@@ -32,10 +31,12 @@ type RoomView struct {
 | 
				
			|||||||
	*tview.Box
 | 
						*tview.Box
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	topic    *tview.TextView
 | 
						topic    *tview.TextView
 | 
				
			||||||
	content  *tview.TextView
 | 
						content  *MessageView
 | 
				
			||||||
	status   *tview.TextView
 | 
						status   *tview.TextView
 | 
				
			||||||
	userList *tview.TextView
 | 
						userList *tview.TextView
 | 
				
			||||||
	room     *gomatrix.Room
 | 
						room     *gomatrix.Room
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						debug DebugPrinter
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var colorNames []string
 | 
					var colorNames []string
 | 
				
			||||||
@@ -47,27 +48,30 @@ func init() {
 | 
				
			|||||||
		colorNames[i] = name
 | 
							colorNames[i] = name
 | 
				
			||||||
		i++
 | 
							i++
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						sort.Sort(sort.StringSlice(colorNames))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewRoomView(room *gomatrix.Room) *RoomView {
 | 
					func NewRoomView(debug DebugPrinter, room *gomatrix.Room) *RoomView {
 | 
				
			||||||
	view := &RoomView{
 | 
						view := &RoomView{
 | 
				
			||||||
		Box:      tview.NewBox(),
 | 
							Box:      tview.NewBox(),
 | 
				
			||||||
		topic:    tview.NewTextView(),
 | 
							topic:    tview.NewTextView(),
 | 
				
			||||||
		content:  tview.NewTextView(),
 | 
							content:  NewMessageView(debug),
 | 
				
			||||||
		status:   tview.NewTextView(),
 | 
							status:   tview.NewTextView(),
 | 
				
			||||||
		userList: tview.NewTextView(),
 | 
							userList: tview.NewTextView(),
 | 
				
			||||||
		room:     room,
 | 
							room:     room,
 | 
				
			||||||
 | 
							debug:    debug,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	view.topic.
 | 
						view.topic.
 | 
				
			||||||
		SetText(strings.Replace(room.GetTopic(), "\n", " ", -1)).
 | 
							SetText(strings.Replace(room.GetTopic(), "\n", " ", -1)).
 | 
				
			||||||
		SetBackgroundColor(tcell.ColorDarkGreen)
 | 
							SetBackgroundColor(tcell.ColorDarkGreen)
 | 
				
			||||||
	view.status.SetBackgroundColor(tcell.ColorDimGray)
 | 
						view.status.SetBackgroundColor(tcell.ColorDimGray)
 | 
				
			||||||
	view.userList.SetDynamicColors(true)
 | 
						view.userList.SetDynamicColors(true)
 | 
				
			||||||
	view.content.SetDynamicColors(true)
 | 
					 | 
				
			||||||
	return view
 | 
						return view
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (view *RoomView) Draw(screen tcell.Screen) {
 | 
					func (view *RoomView) Draw(screen tcell.Screen) {
 | 
				
			||||||
 | 
						view.Box.Draw(screen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	x, y, width, height := view.GetRect()
 | 
						x, y, width, height := view.GetRect()
 | 
				
			||||||
	view.topic.SetRect(x, y, width, 1)
 | 
						view.topic.SetRect(x, y, width, 1)
 | 
				
			||||||
	view.content.SetRect(x, y+1, width-30, height-2)
 | 
						view.content.SetRect(x, y+1, width-30, height-2)
 | 
				
			||||||
@@ -104,26 +108,24 @@ func (view *RoomView) SetTyping(users []string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var colorPattern = regexp.MustCompile(`\[([a-zA-Z]+|#[0-9a-zA-Z]{6})\]`)
 | 
					func (view *RoomView) MessageView() *MessageView {
 | 
				
			||||||
 | 
						return view.content
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func color(s string) string {
 | 
					func getColorName(s string) string {
 | 
				
			||||||
	h := fnv.New32a()
 | 
						h := fnv.New32a()
 | 
				
			||||||
	h.Write([]byte(s))
 | 
						h.Write([]byte(s))
 | 
				
			||||||
	color := colorNames[int(h.Sum32())%len(colorNames)]
 | 
						return colorNames[int(h.Sum32())%len(colorNames)]
 | 
				
			||||||
	return fmt.Sprintf("[%s]%s[white]", color, s)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func escapeColor(s string) string {
 | 
					func getColor(s string) tcell.Color {
 | 
				
			||||||
	return colorPattern.ReplaceAllString(s, "[$1[]")
 | 
						h := fnv.New32a()
 | 
				
			||||||
 | 
						h.Write([]byte(s))
 | 
				
			||||||
 | 
						return tcell.ColorNames[colorNames[int(h.Sum32())%len(colorNames)]]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (view *RoomView) AddMessage(sender, message string, timestamp time.Time) {
 | 
					func color(s string) string {
 | 
				
			||||||
	member := view.room.GetMember(sender)
 | 
						return fmt.Sprintf("[%s]%s[white]", getColorName(s), s)
 | 
				
			||||||
	if member != nil {
 | 
					 | 
				
			||||||
		sender = member.DisplayName
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fmt.Fprintf(view.content, "[%s] %s: %s\n",
 | 
					 | 
				
			||||||
		timestamp.Format("15:04:05"), color(sender), escapeColor(message))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (view *RoomView) UpdateUserList() {
 | 
					func (view *RoomView) UpdateUserList() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								view-main.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								view-main.go
									
									
									
									
									
								
							@@ -126,28 +126,34 @@ func (view *MainView) HandleCommand(room, command string, args []string) {
 | 
				
			|||||||
		view.matrix.client.LeaveRoom(room)
 | 
							view.matrix.client.LeaveRoom(room)
 | 
				
			||||||
	case "/join":
 | 
						case "/join":
 | 
				
			||||||
		if len(args) == 0 {
 | 
							if len(args) == 0 {
 | 
				
			||||||
			view.AddMessage(room, "*", "Usage: /join <room>", time.Now())
 | 
								view.AddMessage(room,  "Usage: /join <room>")
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		view.debug.Print(view.matrix.JoinRoom(args[0]))
 | 
							view.debug.Print(view.matrix.JoinRoom(args[0]))
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		view.AddMessage(room, "*", "Unknown command.", time.Now())
 | 
							view.AddMessage(room, "Unknown command.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (view *MainView) InputCapture(key *tcell.EventKey) *tcell.EventKey {
 | 
					func (view *MainView) InputCapture(key *tcell.EventKey) *tcell.EventKey {
 | 
				
			||||||
 | 
						k := key.Key()
 | 
				
			||||||
	if key.Modifiers() == tcell.ModCtrl {
 | 
						if key.Modifiers() == tcell.ModCtrl {
 | 
				
			||||||
		if key.Key() == tcell.KeyDown {
 | 
							if k == tcell.KeyDown {
 | 
				
			||||||
			view.SwitchRoom(view.currentRoomIndex + 1)
 | 
								view.SwitchRoom(view.currentRoomIndex + 1)
 | 
				
			||||||
			view.roomList.SetCurrentItem(view.currentRoomIndex)
 | 
								view.roomList.SetCurrentItem(view.currentRoomIndex)
 | 
				
			||||||
		} else if key.Key() == tcell.KeyUp {
 | 
							} else if k == tcell.KeyUp {
 | 
				
			||||||
			view.SwitchRoom(view.currentRoomIndex - 1)
 | 
								view.SwitchRoom(view.currentRoomIndex - 1)
 | 
				
			||||||
			view.roomList.SetCurrentItem(view.currentRoomIndex)
 | 
								view.roomList.SetCurrentItem(view.currentRoomIndex)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return key
 | 
								return key
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if key.Key() == tcell.KeyPgUp || key.Key() == tcell.KeyPgDn {
 | 
						} else if k == tcell.KeyPgUp || k == tcell.KeyPgDn {
 | 
				
			||||||
		view.rooms[view.CurrentRoomID()].InputHandler()(key, nil)
 | 
							msgView := view.rooms[view.CurrentRoomID()].MessageView()
 | 
				
			||||||
 | 
							if k == tcell.KeyPgUp {
 | 
				
			||||||
 | 
								msgView.PageUp()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								msgView.PageDown()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		return key
 | 
							return key
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -178,7 +184,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(roomStore)
 | 
							roomView := NewRoomView(view.debug, 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()
 | 
				
			||||||
@@ -231,10 +237,18 @@ func (view *MainView) SetTyping(room string, users []string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (view *MainView) AddMessage(room, sender, message string, timestamp time.Time) {
 | 
					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 {
 | 
				
			||||||
		roomView.AddMessage(sender, message, timestamp)
 | 
							member := roomView.room.GetMember(sender)
 | 
				
			||||||
 | 
							if member != nil {
 | 
				
			||||||
 | 
								sender = member.DisplayName
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							roomView.content.AddMessage(id, sender, message, timestamp)
 | 
				
			||||||
		view.parent.Render()
 | 
							view.parent.Render()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user