diff options
Diffstat (limited to 'message-view.go')
-rw-r--r-- | message-view.go | 186 |
1 files changed, 130 insertions, 56 deletions
diff --git a/message-view.go b/message-view.go index 6029f6a..d3d2df9 100644 --- a/message-view.go +++ b/message-view.go @@ -17,6 +17,7 @@ package main import ( + "fmt" "regexp" "strings" "time" @@ -27,16 +28,27 @@ import ( ) type Message struct { - ID string - Sender string - Text string - Timestamp string - RenderSender bool + ID string + Sender string + Text string + Timestamp string + Date string buffer []string senderColor tcell.Color } +func NewMessage(id, sender, text, timestamp, date string, senderColor tcell.Color) *Message { + return &Message{ + ID: id, + Sender: sender, + Text: text, + Timestamp: timestamp, + Date: date, + senderColor: senderColor, + } +} + var ( boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)") spacePattern = regexp.MustCompile(`\s+`) @@ -80,6 +92,7 @@ type MessageView struct { ScrollOffset int MaxSenderWidth int + DateFormat string TimestampFormat string TimestampWidth int Separator rune @@ -92,18 +105,23 @@ type MessageView struct { lastDisplayMessage int totalHeight int - messages []*Message + messageIDs map[string]bool + messages []*Message } func NewMessageView() *MessageView { return &MessageView{ Box: tview.NewBox(), MaxSenderWidth: 20, + DateFormat: "January _2, 2006", TimestampFormat: "15:04:05", TimestampWidth: 8, Separator: '|', ScrollOffset: 0, + messages: make([]*Message, 0), + messageIDs: make(map[string]bool), + widestSender: 5, prevWidth: -1, prevHeight: -1, @@ -114,62 +132,94 @@ func NewMessageView() *MessageView { } } -func (view *MessageView) recalculateBuffers(width int) { +func (view *MessageView) NewMessage(id, sender, text string, timestamp time.Time) *Message { + return NewMessage(id, sender, text, + timestamp.Format(view.TimestampFormat), + timestamp.Format(view.DateFormat), + getColor(sender)) +} + +func (view *MessageView) recalculateBuffers() { + _, _, width, _ := view.GetInnerRect() width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap - for _, message := range view.messages { - message.calculateBuffer(width) + if width != view.prevWidth { + for _, message := range view.messages { + message.calculateBuffer(width) + } + view.prevWidth = width } - view.prevWidth = width } -func (view *MessageView) AddMessage(id, sender, text string, timestamp time.Time) { +func (view *MessageView) updateWidestSender(sender string) { 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), +} + +const ( + AppendMessage int = iota + PrependMessage +) + +func (view *MessageView) AddMessage(message *Message, direction int) { + _, messageExists := view.messageIDs[message.ID] + if messageExists { + return } - _, _, width, height := view.GetInnerRect() + + view.updateWidestSender(message.Sender) + + _, _, width, _ := 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 + + if direction == AppendMessage { + if view.ScrollOffset > 0 { + view.ScrollOffset += len(message.buffer) + } + view.messages = append(view.messages, message) + } else if direction == PrependMessage { + view.messages = append([]*Message{message}, view.messages...) } - view.messages = append(view.messages, message) - view.recalculateHeight(height) + + view.messageIDs[message.ID] = true + view.recalculateHeight() } -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) +func (view *MessageView) recalculateHeight() { + _, _, width, height := view.GetInnerRect() + if height != view.prevHeight || width != view.prevWidth || view.ScrollOffset != view.prevScrollOffset { + view.firstDisplayMessage = -1 + view.lastDisplayMessage = -1 + view.totalHeight = 0 + prevDate := "" + for i := len(view.messages) - 1; i >= 0; i-- { + prevTotalHeight := view.totalHeight + message := view.messages[i] + view.totalHeight += len(message.buffer) + if message.Date != prevDate { + if len(prevDate) != 0 { + view.totalHeight++ + } + prevDate = message.Date + } - if view.totalHeight < view.ScrollOffset { - continue - } else if view.firstDisplayMessage == -1 { - view.lastDisplayMessage = i - view.firstDisplayMessage = i - } + 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 + if prevTotalHeight < height+view.ScrollOffset { + view.lastDisplayMessage = i + } } + view.prevScrollOffset = view.ScrollOffset } - view.prevScrollOffset = view.ScrollOffset } func (view *MessageView) PageUp() { @@ -230,43 +280,67 @@ const ( 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) + x, y, _, height := view.GetInnerRect() + view.recalculateBuffers() + view.recalculateHeight() + + if view.firstDisplayMessage == -1 || view.lastDisplayMessage == -1 { + view.writeLine(screen, "It's quite empty in here.", x, y+height, tcell.ColorDefault) + return } + 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 + prevDate := "" + prevSender := "" + prevSenderLine := -1 for i := view.firstDisplayMessage; i >= view.lastDisplayMessage; i-- { message := view.messages[i] messageHeight := len(message.buffer) + // Show message when the date changes. + if message.Date != prevDate { + if len(prevDate) > 0 { + writeOffset++ + view.writeLine( + screen, fmt.Sprintf("Date changed to %s", prevDate), + x+messageOffsetX, y+height-writeOffset, tcell.ColorGreen) + } + prevDate = message.Date + } + senderAtLine := y + height - writeOffset - messageHeight + // The message may be only partially on screen, so we need to make sure the sender + // is on screen even when the message is not shown completely. if senderAtLine < y { senderAtLine = y } + view.writeLine(screen, message.Timestamp, x, senderAtLine, tcell.ColorDefault) - if message.RenderSender || i == view.lastDisplayMessage { - view.writeLineRight(screen, message.Sender, - x+usernameOffsetX, senderAtLine, + view.writeLineRight(screen, message.Sender, + x+usernameOffsetX, senderAtLine, + view.widestSender, message.senderColor) + + if message.Sender == prevSender { + // Sender is same as previous. We're looping from bottom to top, and we want the + // sender name only on the topmost message, so clear out the duplicate sender name + // below. + view.writeLineRight(screen, strings.Repeat(" ", view.widestSender), + x+usernameOffsetX, prevSenderLine, view.widestSender, message.senderColor) } + prevSender = message.Sender + prevSenderLine = senderAtLine for num, line := range message.buffer { offsetY := height - messageHeight - writeOffset + num + // Only render message if it's within the message view. if offsetY >= 0 { view.writeLine(screen, line, x+messageOffsetX, y+offsetY, tcell.ColorDefault) } |