aboutsummaryrefslogtreecommitdiff
path: root/message-view.go
diff options
context:
space:
mode:
Diffstat (limited to 'message-view.go')
-rw-r--r--message-view.go186
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)
}