aboutsummaryrefslogtreecommitdiff
path: root/ui/messages
diff options
context:
space:
mode:
Diffstat (limited to 'ui/messages')
-rw-r--r--ui/messages/base.go235
-rw-r--r--ui/messages/expandedtextmessage.go67
-rw-r--r--ui/messages/htmlmessage.go35
-rw-r--r--ui/messages/imagemessage.go44
-rw-r--r--ui/messages/message.go53
-rw-r--r--ui/messages/parser.go55
-rw-r--r--ui/messages/textbase.go11
-rw-r--r--ui/messages/textmessage.go78
8 files changed, 267 insertions, 311 deletions
diff --git a/ui/messages/base.go b/ui/messages/base.go
index 05df72b..ef495fb 100644
--- a/ui/messages/base.go
+++ b/ui/messages/base.go
@@ -27,44 +27,60 @@ import (
"maunium.net/go/tcell"
"maunium.net/go/gomuks/interface"
- "maunium.net/go/gomuks/ui/messages/tstring"
"maunium.net/go/gomuks/ui/widget"
)
-type BaseMessage struct {
- MsgID string
- MsgTxnID string
- MsgType mautrix.MessageType
- MsgSenderID string
- MsgSender string
- MsgSenderColor tcell.Color
- MsgTimestamp time.Time
- MsgState mautrix.OutgoingEventState
- MsgIsHighlight bool
- MsgIsService bool
- MsgSource json.RawMessage
- ReplyTo UIMessage
- buffer []tstring.TString
-}
-
-func newBaseMessage(event *mautrix.Event, displayname string) BaseMessage {
+type MessageRenderer interface {
+ Draw(screen mauview.Screen)
+ NotificationContent() string
+ PlainText() string
+ CalculateBuffer(prefs config.UserPreferences, width int, msg *UIMessage)
+ RegisterMatrix(matrix ifc.MatrixContainer)
+ Height() int
+ Clone() MessageRenderer
+ String() string
+}
+
+type UIMessage struct {
+ EventID string
+ TxnID string
+ Relation mautrix.RelatesTo
+ Type mautrix.MessageType
+ SenderID string
+ SenderName string
+ DefaultSenderColor tcell.Color
+ Timestamp time.Time
+ State mautrix.OutgoingEventState
+ IsHighlight bool
+ IsService bool
+ Source json.RawMessage
+ ReplyTo *UIMessage
+ Renderer MessageRenderer
+}
+
+const DateFormat = "January _2, 2006"
+const TimeFormat = "15:04:05"
+
+func newUIMessage(event *mautrix.Event, displayname string, renderer MessageRenderer) *UIMessage {
msgtype := event.Content.MsgType
if len(msgtype) == 0 {
msgtype = mautrix.MessageType(event.Type.String())
}
- return BaseMessage{
- MsgSenderID: event.Sender,
- MsgSender: displayname,
- MsgTimestamp: unixToTime(event.Timestamp),
- MsgSenderColor: widget.GetHashColor(event.Sender),
- MsgType: msgtype,
- MsgID: event.ID,
- MsgTxnID: event.Unsigned.TransactionID,
- MsgState: event.Unsigned.OutgoingState,
- MsgIsHighlight: false,
- MsgIsService: false,
- MsgSource: event.Content.VeryRaw,
+ return &UIMessage{
+ SenderID: event.Sender,
+ SenderName: displayname,
+ Timestamp: unixToTime(event.Timestamp),
+ DefaultSenderColor: widget.GetHashColor(event.Sender),
+ Type: msgtype,
+ EventID: event.ID,
+ TxnID: event.Unsigned.TransactionID,
+ Relation: *event.Content.GetRelatesTo(),
+ State: event.Unsigned.OutgoingState,
+ IsHighlight: false,
+ IsService: false,
+ Source: event.Content.VeryRaw,
+ Renderer: renderer,
}
}
@@ -76,44 +92,38 @@ func unixToTime(unix int64) time.Time {
return timestamp
}
-func (msg *BaseMessage) RegisterMatrix(matrix ifc.MatrixContainer) {}
-
// Sender gets the string that should be displayed as the sender of this message.
//
// If the message is being sent, the sender is "Sending...".
// If sending has failed, the sender is "Error".
// If the message is an emote, the sender is blank.
// In any other case, the sender is the display name of the user who sent the message.
-func (msg *BaseMessage) Sender() string {
- switch msg.MsgState {
+func (msg *UIMessage) Sender() string {
+ switch msg.State {
case mautrix.EventStateLocalEcho:
return "Sending..."
case mautrix.EventStateSendFail:
return "Error"
}
- switch msg.MsgType {
+ switch msg.Type {
case "m.emote":
// Emotes don't show a separate sender, it's included in the buffer.
return ""
default:
- return msg.MsgSender
+ return msg.SenderName
}
}
-func (msg *BaseMessage) SenderID() string {
- return msg.MsgSenderID
+func (msg *UIMessage) NotificationSenderName() string {
+ return msg.SenderName
}
-func (msg *BaseMessage) RealSender() string {
- return msg.MsgSender
+func (msg *UIMessage) NotificationContent() string {
+ return msg.Renderer.NotificationContent()
}
-func (msg *BaseMessage) NotificationSenderName() string {
- return msg.MsgSender
-}
-
-func (msg *BaseMessage) getStateSpecificColor() tcell.Color {
- switch msg.MsgState {
+func (msg *UIMessage) getStateSpecificColor() tcell.Color {
+ switch msg.State {
case mautrix.EventStateLocalEcho:
return tcell.ColorGray
case mautrix.EventStateSendFail:
@@ -132,31 +142,31 @@ func (msg *BaseMessage) getStateSpecificColor() tcell.Color {
//
// In any other case, the color is whatever is specified in the Message struct.
// Usually that means it is the hash-based color of the sender (see ui/widget/color.go)
-func (msg *BaseMessage) SenderColor() tcell.Color {
+func (msg *UIMessage) SenderColor() tcell.Color {
stateColor := msg.getStateSpecificColor()
switch {
case stateColor != tcell.ColorDefault:
return stateColor
- case msg.MsgType == "m.room.member":
- return widget.GetHashColor(msg.MsgSender)
- case msg.MsgIsService:
+ case msg.Type == "m.room.member":
+ return widget.GetHashColor(msg.SenderName)
+ case msg.IsService:
return tcell.ColorGray
default:
- return msg.MsgSenderColor
+ return msg.DefaultSenderColor
}
}
// TextColor returns the color the actual content of the message should be shown in.
-func (msg *BaseMessage) TextColor() tcell.Color {
+func (msg *UIMessage) TextColor() tcell.Color {
stateColor := msg.getStateSpecificColor()
switch {
case stateColor != tcell.ColorDefault:
return stateColor
- case msg.MsgIsService, msg.MsgType == "m.notice":
+ case msg.IsService, msg.Type == "m.notice":
return tcell.ColorGray
- case msg.MsgIsHighlight:
+ case msg.IsHighlight:
return tcell.ColorYellow
- case msg.MsgType == "m.room.member":
+ case msg.Type == "m.room.member":
return tcell.ColorGreen
default:
return tcell.ColorDefault
@@ -169,14 +179,14 @@ func (msg *BaseMessage) TextColor() tcell.Color {
// gray and red respectively.
//
// However, other messages are the default color instead of a color stored in the struct.
-func (msg *BaseMessage) TimestampColor() tcell.Color {
- if msg.MsgIsService {
+func (msg *UIMessage) TimestampColor() tcell.Color {
+ if msg.IsService {
return tcell.ColorGray
}
return msg.getStateSpecificColor()
}
-func (msg *BaseMessage) ReplyHeight() int {
+func (msg *UIMessage) ReplyHeight() int {
if msg.ReplyTo != nil {
return 1 + msg.ReplyTo.Height()
}
@@ -184,102 +194,78 @@ func (msg *BaseMessage) ReplyHeight() int {
}
// Height returns the number of rows in the computed buffer (see Buffer()).
-func (msg *BaseMessage) Height() int {
- return msg.ReplyHeight() + len(msg.buffer)
+func (msg *UIMessage) Height() int {
+ return msg.ReplyHeight() + msg.Renderer.Height()
}
-// Timestamp returns the full timestamp when the message was sent.
-func (msg *BaseMessage) Timestamp() time.Time {
- return msg.MsgTimestamp
+func (msg *UIMessage) Time() time.Time {
+ return msg.Timestamp
}
// FormatTime returns the formatted time when the message was sent.
-func (msg *BaseMessage) FormatTime() string {
- return msg.MsgTimestamp.Format(TimeFormat)
+func (msg *UIMessage) FormatTime() string {
+ return msg.Timestamp.Format(TimeFormat)
}
// FormatDate returns the formatted date when the message was sent.
-func (msg *BaseMessage) FormatDate() string {
- return msg.MsgTimestamp.Format(DateFormat)
+func (msg *UIMessage) FormatDate() string {
+ return msg.Timestamp.Format(DateFormat)
}
-func (msg *BaseMessage) SameDate(message UIMessage) bool {
- year1, month1, day1 := msg.Timestamp().Date()
- year2, month2, day2 := message.Timestamp().Date()
+func (msg *UIMessage) SameDate(message *UIMessage) bool {
+ year1, month1, day1 := msg.Timestamp.Date()
+ year2, month2, day2 := message.Timestamp.Date()
return day1 == day2 && month1 == month2 && year1 == year2
}
-func (msg *BaseMessage) ID() string {
- if len(msg.MsgID) == 0 {
- return msg.MsgTxnID
+func (msg *UIMessage) ID() string {
+ if len(msg.EventID) == 0 {
+ return msg.TxnID
}
- return msg.MsgID
-}
-
-func (msg *BaseMessage) SetID(id string) {
- msg.MsgID = id
-}
-
-func (msg *BaseMessage) TxnID() string {
- return msg.MsgTxnID
-}
-
-func (msg *BaseMessage) Type() mautrix.MessageType {
- return msg.MsgType
-}
-
-func (msg *BaseMessage) State() mautrix.OutgoingEventState {
- return msg.MsgState
-}
-
-func (msg *BaseMessage) SetState(state mautrix.OutgoingEventState) {
- msg.MsgState = state
-}
-
-func (msg *BaseMessage) IsHighlight() bool {
- return msg.MsgIsHighlight
-}
-
-func (msg *BaseMessage) SetIsHighlight(isHighlight bool) {
- msg.MsgIsHighlight = isHighlight
+ return msg.EventID
}
-func (msg *BaseMessage) Source() json.RawMessage {
- return msg.MsgSource
+func (msg *UIMessage) SetID(id string) {
+ msg.EventID = id
}
-func (msg *BaseMessage) SetReplyTo(event UIMessage) {
- msg.ReplyTo = event
+func (msg *UIMessage) SetIsHighlight(isHighlight bool) {
+ // TODO Textmessage cache needs to be cleared
+ msg.IsHighlight = isHighlight
}
-func (msg *BaseMessage) Draw(screen mauview.Screen) {
+func (msg *UIMessage) Draw(screen mauview.Screen) {
screen = msg.DrawReply(screen)
- for y, line := range msg.buffer {
- line.Draw(screen, 0, y)
- }
+ msg.Renderer.Draw(screen)
}
-func (msg *BaseMessage) clone() BaseMessage {
+func (msg *UIMessage) Clone() *UIMessage {
clone := *msg
- clone.buffer = nil
- return clone
+ clone.ReplyTo = nil
+ clone.Renderer = clone.Renderer.Clone()
+ return &clone
}
-func (msg *BaseMessage) CalculateReplyBuffer(preferences config.UserPreferences, width int) {
+func (msg *UIMessage) CalculateReplyBuffer(preferences config.UserPreferences, width int) {
if msg.ReplyTo == nil {
return
}
msg.ReplyTo.CalculateBuffer(preferences, width-1)
}
-func (msg *BaseMessage) DrawReply(screen mauview.Screen) mauview.Screen {
+func (msg *UIMessage) CalculateBuffer(preferences config.UserPreferences, width int) {
+ msg.Renderer.CalculateBuffer(preferences, width, msg)
+ msg.CalculateReplyBuffer(preferences, width)
+}
+
+func (msg *UIMessage) DrawReply(screen mauview.Screen) mauview.Screen {
if msg.ReplyTo == nil {
return screen
}
width, height := screen.Size()
replyHeight := msg.ReplyTo.Height()
widget.WriteLineSimpleColor(screen, "In reply to", 1, 0, tcell.ColorGreen)
- widget.WriteLineSimpleColor(screen, msg.ReplyTo.RealSender(), 13, 0, msg.ReplyTo.SenderColor())
+ widget.WriteLineSimpleColor(screen, msg.ReplyTo.SenderName, 13, 0, msg.ReplyTo.SenderColor())
for y := 0; y < 1+replyHeight; y++ {
screen.SetCell(0, y, tcell.StyleDefault, '▊')
}
@@ -288,16 +274,21 @@ func (msg *BaseMessage) DrawReply(screen mauview.Screen) mauview.Screen {
return mauview.NewProxyScreen(screen, 0, replyHeight+1, width, height-replyHeight-1)
}
-func (msg *BaseMessage) String() string {
- return fmt.Sprintf(`&messages.BaseMessage{
+func (msg *UIMessage) String() string {
+ return fmt.Sprintf(`&messages.UIMessage{
ID="%s", TxnID="%s",
Type="%s", Timestamp=%s,
Sender={ID="%s", Name="%s", Color=#%X},
IsService=%t, IsHighlight=%t,
+ Renderer=%s,
}`,
- msg.MsgID, msg.MsgTxnID,
- msg.MsgType, msg.MsgTimestamp.String(),
- msg.MsgSenderID, msg.MsgSender, msg.MsgSenderColor.Hex(),
- msg.MsgIsService, msg.MsgIsHighlight,
+ msg.EventID, msg.TxnID,
+ msg.Type, msg.Timestamp.String(),
+ msg.SenderID, msg.SenderName, msg.DefaultSenderColor.Hex(),
+ msg.IsService, msg.IsHighlight, msg.Renderer.String(),
)
}
+
+func (msg *UIMessage) PlainText() string {
+ return msg.Renderer.PlainText()
+}
diff --git a/ui/messages/expandedtextmessage.go b/ui/messages/expandedtextmessage.go
index cf71ba1..c9cbf0c 100644
--- a/ui/messages/expandedtextmessage.go
+++ b/ui/messages/expandedtextmessage.go
@@ -17,9 +17,12 @@
package messages
import (
+ "fmt"
"time"
+ ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/mautrix"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
"maunium.net/go/gomuks/config"
@@ -27,55 +30,63 @@ import (
)
type ExpandedTextMessage struct {
- BaseMessage
- MsgText tstring.TString
+ Text tstring.TString
+ buffer []tstring.TString
}
// NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state.
-func NewExpandedTextMessage(event *mautrix.Event, displayname string, text tstring.TString) UIMessage {
- return &ExpandedTextMessage{
- BaseMessage: newBaseMessage(event, displayname),
- MsgText: text,
- }
+func NewExpandedTextMessage(event *mautrix.Event, displayname string, text tstring.TString) *UIMessage {
+ return newUIMessage(event, displayname, &ExpandedTextMessage{
+ Text: text,
+ })
}
-func NewDateChangeMessage(text string) UIMessage {
+func NewDateChangeMessage(text string) *UIMessage {
midnight := time.Now()
midnight = time.Date(midnight.Year(), midnight.Month(), midnight.Day(),
0, 0, 0, 0,
midnight.Location())
- return &ExpandedTextMessage{
- BaseMessage: BaseMessage{
- MsgSenderID: "*",
- MsgSender: "*",
- MsgTimestamp: midnight,
- MsgIsService: true,
+ return &UIMessage{
+ SenderID: "*",
+ SenderName: "*",
+ Timestamp: midnight,
+ IsService: true,
+ Renderer: &ExpandedTextMessage{
+ Text: tstring.NewColorTString(text, tcell.ColorGreen),
},
- MsgText: tstring.NewColorTString(text, tcell.ColorGreen),
}
}
-
-func (msg *ExpandedTextMessage) Clone() UIMessage {
+func (msg *ExpandedTextMessage) Clone() MessageRenderer {
return &ExpandedTextMessage{
- BaseMessage: msg.BaseMessage.clone(),
- MsgText: msg.MsgText.Clone(),
+ Text: msg.Text.Clone(),
}
}
-func (msg *ExpandedTextMessage) GenerateText() tstring.TString {
- return msg.MsgText
-}
-
func (msg *ExpandedTextMessage) NotificationContent() string {
- return msg.MsgText.String()
+ return msg.Text.String()
}
func (msg *ExpandedTextMessage) PlainText() string {
- return msg.MsgText.String()
+ return msg.Text.String()
+}
+
+func (msg *ExpandedTextMessage) String() string {
+ return fmt.Sprintf(`&messages.ExpandedTextMessage{Text="%s"}`, msg.Text.String())
+}
+
+func (msg *ExpandedTextMessage) CalculateBuffer(prefs config.UserPreferences, width int, uiMsg *UIMessage) {
+ msg.buffer = calculateBufferWithText(prefs, msg.Text, width, uiMsg)
}
-func (msg *ExpandedTextMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
- msg.CalculateReplyBuffer(prefs, width)
- msg.calculateBufferWithText(prefs, msg.MsgText, width)
+func (msg *ExpandedTextMessage) Height() int {
+ return len(msg.buffer)
}
+
+func (msg *ExpandedTextMessage) Draw(screen mauview.Screen) {
+ for y, line := range msg.buffer {
+ line.Draw(screen, 0, y)
+ }
+}
+
+func (msg *ExpandedTextMessage) RegisterMatrix(matrix ifc.MatrixContainer) {}
diff --git a/ui/messages/htmlmessage.go b/ui/messages/htmlmessage.go
index 30b1588..5b95a82 100644
--- a/ui/messages/htmlmessage.go
+++ b/ui/messages/htmlmessage.go
@@ -17,9 +17,7 @@
package messages
import (
- "fmt"
- "strings"
-
+ ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/mautrix"
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -29,30 +27,27 @@ import (
)
type HTMLMessage struct {
- BaseMessage
-
Root html.Entity
FocusedBg tcell.Color
focused bool
}
-func NewHTMLMessage(event *mautrix.Event, displayname string, root html.Entity) UIMessage {
- return &HTMLMessage{
- BaseMessage: newBaseMessage(event, displayname),
- Root: root,
- }
+func NewHTMLMessage(event *mautrix.Event, displayname string, root html.Entity) *UIMessage {
+ return newUIMessage(event, displayname, &HTMLMessage{
+ Root: root,
+ })
}
-func (hw *HTMLMessage) Clone() UIMessage {
+func (hw *HTMLMessage) RegisterMatrix(matrix ifc.MatrixContainer) {}
+
+func (hw *HTMLMessage) Clone() MessageRenderer {
return &HTMLMessage{
- BaseMessage: hw.BaseMessage.clone(),
- Root: hw.Root.Clone(),
- FocusedBg: hw.FocusedBg,
+ Root: hw.Root.Clone(),
+ FocusedBg: hw.FocusedBg,
}
}
func (hw *HTMLMessage) Draw(screen mauview.Screen) {
- screen = hw.DrawReply(screen)
if hw.focused {
screen.SetStyle(tcell.StyleDefault.Background(hw.FocusedBg))
}
@@ -80,18 +75,17 @@ func (hw *HTMLMessage) OnPasteEvent(event mauview.PasteEvent) bool {
return false
}
-func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width int) {
+func (hw *HTMLMessage) CalculateBuffer(preferences config.UserPreferences, width int, msg *UIMessage) {
if width < 2 {
return
}
- hw.CalculateReplyBuffer(preferences, width)
// TODO account for bare messages in initial startX
startX := 0
hw.Root.CalculateBuffer(width, startX, preferences.BareMessageView)
}
func (hw *HTMLMessage) Height() int {
- return hw.ReplyHeight() + hw.Root.Height()
+ return hw.Root.Height()
}
func (hw *HTMLMessage) PlainText() string {
@@ -103,8 +97,5 @@ func (hw *HTMLMessage) NotificationContent() string {
}
func (hw *HTMLMessage) String() string {
- return fmt.Sprintf("&messages.HTMLMessage{\n" +
- " Base=%s,\n" +
- " Root=||\n%s\n" +
- "}", strings.Replace(hw.BaseMessage.String(), "\n", "\n ", -1), hw.Root.String())
+ return hw.Root.String()
}
diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go
index 01a6500..6c31327 100644
--- a/ui/messages/imagemessage.go
+++ b/ui/messages/imagemessage.go
@@ -22,6 +22,7 @@ import (
"image/color"
"maunium.net/go/mautrix"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
"maunium.net/go/gomuks/config"
@@ -32,32 +33,30 @@ import (
)
type ImageMessage struct {
- BaseMessage
Body string
Homeserver string
FileID string
data []byte
+ buffer []tstring.TString
matrix ifc.MatrixContainer
}
// NewImageMessage creates a new ImageMessage object with the provided values and the default state.
-func NewImageMessage(matrix ifc.MatrixContainer, event *mautrix.Event, displayname string, body, homeserver, fileID string, data []byte) UIMessage {
- return &ImageMessage{
- newBaseMessage(event, displayname),
- body,
- homeserver,
- fileID,
- data,
- matrix,
- }
+func NewImageMessage(matrix ifc.MatrixContainer, event *mautrix.Event, displayname string, body, homeserver, fileID string, data []byte) *UIMessage {
+ return newUIMessage(event, displayname, &ImageMessage{
+ Body: body,
+ Homeserver: homeserver,
+ FileID: fileID,
+ data: data,
+ matrix: matrix,
+ })
}
-func (msg *ImageMessage) Clone() UIMessage {
+func (msg *ImageMessage) Clone() MessageRenderer {
data := make([]byte, len(msg.data))
copy(data, msg.data)
return &ImageMessage{
- BaseMessage: msg.BaseMessage.clone(),
Body: msg.Body,
Homeserver: msg.Homeserver,
FileID: msg.FileID,
@@ -70,7 +69,7 @@ func (msg *ImageMessage) RegisterMatrix(matrix ifc.MatrixContainer) {
msg.matrix = matrix
if len(msg.data) == 0 {
- go msg.updateData()
+ //FIXME go msg.updateData()
}
}
@@ -82,6 +81,10 @@ func (msg *ImageMessage) PlainText() string {
return fmt.Sprintf("%s: %s", msg.Body, msg.matrix.GetDownloadURL(msg.Homeserver, msg.FileID))
}
+func (msg *ImageMessage) String() string {
+ return fmt.Sprintf(`&messages.ImageMessage{Body="%s", Homeserver="%s", FileID="%s"}`, msg.Body, msg.Homeserver, msg.FileID)
+}
+
func (msg *ImageMessage) updateData() {
defer debug.Recover()
debug.Print("Loading image:", msg.Homeserver, msg.FileID)
@@ -101,14 +104,13 @@ func (msg *ImageMessage) Path() string {
// CalculateBuffer generates the internal buffer for this message that consists
// of the text of this message split into lines at most as wide as the width
// parameter.
-func (msg *ImageMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
+func (msg *ImageMessage) CalculateBuffer(prefs config.UserPreferences, width int, uiMsg *UIMessage) {
if width < 2 {
return
}
- msg.CalculateReplyBuffer(prefs, width)
if prefs.BareMessageView || prefs.DisableImages {
- msg.calculateBufferWithText(prefs, tstring.NewTString(msg.PlainText()), width)
+ msg.buffer = calculateBufferWithText(prefs, tstring.NewTString(msg.PlainText()), width, uiMsg)
return
}
@@ -121,3 +123,13 @@ func (msg *ImageMessage) CalculateBuffer(prefs config.UserPreferences, width int
msg.buffer = image.Render()
}
+
+func (msg *ImageMessage) Height() int {
+ return len(msg.buffer)
+}
+
+func (msg *ImageMessage) Draw(screen mauview.Screen) {
+ for y, line := range msg.buffer {
+ line.Draw(screen, 0, y)
+ }
+}
diff --git a/ui/messages/message.go b/ui/messages/message.go
deleted file mode 100644
index c990368..0000000
--- a/ui/messages/message.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// gomuks - A terminal Matrix client written in Go.
-// Copyright (C) 2019 Tulir Asokan
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero 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 Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-package messages
-
-import (
- "maunium.net/go/gomuks/config"
- "maunium.net/go/gomuks/interface"
- "maunium.net/go/mautrix"
- "maunium.net/go/mauview"
- "maunium.net/go/tcell"
-)
-
-// UIMessage is a wrapper for the content and metadata of a Matrix message intended to be displayed.
-type UIMessage interface {
- ifc.Message
-
- Type() mautrix.MessageType
- Sender() string
- SenderColor() tcell.Color
- TextColor() tcell.Color
- TimestampColor() tcell.Color
- FormatTime() string
- FormatDate() string
- SameDate(message UIMessage) bool
-
- SetReplyTo(message UIMessage)
- CalculateBuffer(preferences config.UserPreferences, width int)
- Draw(screen mauview.Screen)
- Height() int
- PlainText() string
-
- Clone() UIMessage
-
- RealSender() string
- RegisterMatrix(matrix ifc.MatrixContainer)
-}
-
-const DateFormat = "January _2, 2006"
-const TimeFormat = "15:04:05"
diff --git a/ui/messages/parser.go b/ui/messages/parser.go
index 0723257..29f078c 100644
--- a/ui/messages/parser.go
+++ b/ui/messages/parser.go
@@ -31,10 +31,10 @@ import (
"maunium.net/go/gomuks/ui/widget"
)
-func getCachedEvent(mainView ifc.MainView, roomID, eventID string) UIMessage {
+func getCachedEvent(mainView ifc.MainView, roomID, eventID string) *UIMessage {
if roomView := mainView.GetRoom(roomID); roomView != nil {
if replyToIfcMsg := roomView.GetEvent(eventID); replyToIfcMsg != nil {
- if replyToMsg, ok := replyToIfcMsg.(UIMessage); ok && replyToMsg != nil {
+ if replyToMsg, ok := replyToIfcMsg.(*UIMessage); ok && replyToMsg != nil {
return replyToMsg
}
}
@@ -42,24 +42,17 @@ func getCachedEvent(mainView ifc.MainView, roomID, eventID string) UIMessage {
return nil
}
-func ParseEvent(matrix ifc.MatrixContainer, mainView ifc.MainView, room *rooms.Room, evt *mautrix.Event) UIMessage {
+func ParseEvent(matrix ifc.MatrixContainer, mainView ifc.MainView, room *rooms.Room, evt *mautrix.Event) *UIMessage {
msg := directParseEvent(matrix, room, evt)
if msg == nil {
return nil
}
if len(evt.Content.GetReplyTo()) > 0 {
- replyToRoom := room
- if len(evt.Content.RelatesTo.InReplyTo.RoomID) > 0 {
- replyToRoom = matrix.GetRoom(evt.Content.RelatesTo.InReplyTo.RoomID)
- }
-
- if replyToMsg := getCachedEvent(mainView, replyToRoom.ID, evt.Content.GetReplyTo()); replyToMsg != nil {
- replyToMsg = replyToMsg.Clone()
- replyToMsg.SetReplyTo(nil)
- msg.SetReplyTo(replyToMsg)
- } else if replyToEvt, _ := matrix.GetEvent(replyToRoom, evt.Content.GetReplyTo()); replyToEvt != nil {
- if replyToMsg := directParseEvent(matrix, replyToRoom, replyToEvt); replyToMsg != nil {
- msg.SetReplyTo(replyToMsg)
+ if replyToMsg := getCachedEvent(mainView, room.ID, evt.Content.GetReplyTo()); replyToMsg != nil {
+ msg.ReplyTo = replyToMsg.Clone()
+ } else if replyToEvt, _ := matrix.GetEvent(room, evt.Content.GetReplyTo()); replyToEvt != nil {
+ if replyToMsg := directParseEvent(matrix, room, replyToEvt); replyToMsg != nil {
+ msg.ReplyTo = replyToMsg
} else {
// TODO add unrenderable reply header
}
@@ -70,15 +63,22 @@ func ParseEvent(matrix ifc.MatrixContainer, mainView ifc.MainView, room *rooms.R
return msg
}
-func directParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event) UIMessage {
+func directParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event) *UIMessage {
+ displayname := evt.Sender
+ member := room.GetMember(evt.Sender)
+ if member != nil {
+ displayname = member.Displayname
+ }
switch evt.Type {
case mautrix.EventSticker:
evt.Content.MsgType = mautrix.MsgImage
fallthrough
case mautrix.EventMessage:
- return ParseMessage(matrix, room, evt)
+ return ParseMessage(matrix, room, evt, displayname)
+ case mautrix.EventEncrypted:
+ return NewExpandedTextMessage(evt, displayname, tstring.NewStyleTString("Encrypted messages are not yet supported", tcell.StyleDefault.Italic(true)))
case mautrix.StateTopic, mautrix.StateRoomName, mautrix.StateAliases, mautrix.StateCanonicalAlias:
- return ParseStateEvent(matrix, room, evt)
+ return ParseStateEvent(evt, displayname)
case mautrix.StateMember:
return ParseMembershipEvent(room, evt)
}
@@ -86,12 +86,7 @@ func directParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix
return nil
}
-func ParseStateEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event) UIMessage {
- displayname := evt.Sender
- member := room.GetMember(evt.Sender)
- if member != nil {
- displayname = member.Displayname
- }
+func ParseStateEvent(evt *mautrix.Event, displayname string) *UIMessage {
text := tstring.NewColorTString(displayname, widget.GetHashColor(evt.Sender))
switch evt.Type {
case mautrix.StateTopic:
@@ -124,15 +119,13 @@ func ParseStateEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.
return NewExpandedTextMessage(evt, displayname, text)
}
-func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event) UIMessage {
- displayname := evt.Sender
- member := room.GetMember(evt.Sender)
- if member != nil {
- displayname = member.Displayname
- }
+func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Event, displayname string) *UIMessage {
if len(evt.Content.GetReplyTo()) > 0 {
evt.Content.RemoveReplyFallback()
}
+ if evt.Content.GetRelatesTo().Type == mautrix.RelReplace && evt.Content.NewContent != nil {
+ evt.Content = *evt.Content.NewContent
+ }
switch evt.Content.MsgType {
case "m.text", "m.notice", "m.emote":
if evt.Content.Format == mautrix.FormatHTML {
@@ -224,7 +217,7 @@ func getMembershipEventContent(room *rooms.Room, evt *mautrix.Event) (sender str
return
}
-func ParseMembershipEvent(room *rooms.Room, evt *mautrix.Event) UIMessage {
+func ParseMembershipEvent(room *rooms.Room, evt *mautrix.Event) *UIMessage {
displayname, text := getMembershipEventContent(room, evt)
if len(text) == 0 {
return nil
diff --git a/ui/messages/textbase.go b/ui/messages/textbase.go
index 321d998..0d1cc3b 100644
--- a/ui/messages/textbase.go
+++ b/ui/messages/textbase.go
@@ -52,12 +52,12 @@ func matchBoundaryPattern(bare bool, extract tstring.TString) tstring.TString {
// CalculateBuffer generates the internal buffer for this message that consists
// of the text of this message split into lines at most as wide as the width
// parameter.
-func (msg *BaseMessage) calculateBufferWithText(prefs config.UserPreferences, text tstring.TString, width int) {
+func calculateBufferWithText(prefs config.UserPreferences, text tstring.TString, width int, msg *UIMessage) []tstring.TString {
if width < 2 {
- return
+ return nil
}
- msg.buffer = []tstring.TString{}
+ var buffer []tstring.TString
if prefs.BareMessageView {
newText := tstring.NewTString(msg.FormatTime())
@@ -74,7 +74,7 @@ func (msg *BaseMessage) calculateBufferWithText(prefs config.UserPreferences, te
newlines := 0
for _, str := range forcedLinebreaks {
if len(str) == 0 && newlines < 1 {
- msg.buffer = append(msg.buffer, tstring.TString{})
+ buffer = append(buffer, tstring.TString{})
newlines++
} else {
newlines = 0
@@ -88,8 +88,9 @@ func (msg *BaseMessage) calculateBufferWithText(prefs config.UserPreferences, te
}
extract = matchBoundaryPattern(prefs.BareMessageView, extract)
}
- msg.buffer = append(msg.buffer, extract)
+ buffer = append(buffer, extract)
str = str[len(extract):]
}
}
+ return buffer
}
diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go
index f8c4573..9ace201 100644
--- a/ui/messages/textmessage.go
+++ b/ui/messages/textmessage.go
@@ -20,72 +20,82 @@ import (
"fmt"
"time"
+ ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/mautrix"
+ "maunium.net/go/mauview"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/ui/messages/tstring"
)
type TextMessage struct {
- BaseMessage
- cache tstring.TString
- MsgText string
+ cache tstring.TString
+ buffer []tstring.TString
+ Text string
}
// NewTextMessage creates a new UITextMessage object with the provided values and the default state.
-func NewTextMessage(event *mautrix.Event, displayname string, text string) UIMessage {
- return &TextMessage{
- BaseMessage: newBaseMessage(event, displayname),
- MsgText: text,
- }
+func NewTextMessage(event *mautrix.Event, displayname string, text string) *UIMessage {
+ return newUIMessage(event, displayname, &TextMessage{
+ Text: text,
+ })
}
-func NewServiceMessage(text string) UIMessage {
- return &TextMessage{
- BaseMessage: BaseMessage{
- MsgSenderID: "*",
- MsgSender: "*",
- MsgTimestamp: time.Now(),
- MsgIsService: true,
+func NewServiceMessage(text string) *UIMessage {
+ return &UIMessage{
+ SenderID: "*",
+ SenderName: "*",
+ Timestamp: time.Now(),
+ IsService: true,
+ Renderer: &TextMessage{
+ Text: text,
},
- MsgText: text,
}
}
-func (msg *TextMessage) Clone() UIMessage {
+func (msg *TextMessage) Clone() MessageRenderer {
return &TextMessage{
- BaseMessage: msg.BaseMessage.clone(),
- MsgText: msg.MsgText,
+ Text: msg.Text,
}
}
-func (msg *TextMessage) getCache() tstring.TString {
+func (msg *TextMessage) getCache(uiMsg *UIMessage) tstring.TString {
if msg.cache == nil {
- switch msg.MsgType {
+ switch uiMsg.Type {
case "m.emote":
- msg.cache = tstring.NewColorTString(fmt.Sprintf("* %s %s", msg.MsgSender, msg.MsgText), msg.TextColor())
- msg.cache.Colorize(0, len(msg.MsgSender)+2, msg.SenderColor())
+ msg.cache = tstring.NewColorTString(fmt.Sprintf("* %s %s", uiMsg.SenderName, msg.Text), uiMsg.TextColor())
+ msg.cache.Colorize(0, len(uiMsg.SenderName)+2, uiMsg.SenderColor())
default:
- msg.cache = tstring.NewColorTString(msg.MsgText, msg.TextColor())
+ msg.cache = tstring.NewColorTString(msg.Text, uiMsg.TextColor())
}
}
return msg.cache
}
-func (msg *TextMessage) SetIsHighlight(isHighlight bool) {
- msg.BaseMessage.SetIsHighlight(isHighlight)
- msg.cache = nil
-}
-
func (msg *TextMessage) NotificationContent() string {
- return msg.MsgText
+ return msg.Text
}
func (msg *TextMessage) PlainText() string {
- return msg.MsgText
+ return msg.Text
+}
+
+func (msg *TextMessage) String() string {
+ return fmt.Sprintf(`&messages.TextMessage{Text="%s"}`, msg.Text)
}
-func (msg *TextMessage) CalculateBuffer(prefs config.UserPreferences, width int) {
- msg.CalculateReplyBuffer(prefs, width)
- msg.calculateBufferWithText(prefs, msg.getCache(), width)
+func (msg *TextMessage) CalculateBuffer(prefs config.UserPreferences, width int, uiMsg *UIMessage) {
+ msg.buffer = calculateBufferWithText(prefs, msg.getCache(uiMsg), width, uiMsg)
}
+
+func (msg *TextMessage) Height() int {
+ return len(msg.buffer)
+}
+
+func (msg *TextMessage) Draw(screen mauview.Screen) {
+ for y, line := range msg.buffer {
+ line.Draw(screen, 0, y)
+ }
+}
+
+func (msg *TextMessage) RegisterMatrix(matrix ifc.MatrixContainer) {}