2018-04-13 20:25:45 +02:00
|
|
|
// gomuks - A terminal Matrix client written in Go.
|
2019-01-17 13:13:25 +01:00
|
|
|
// Copyright (C) 2019 Tulir Asokan
|
2018-04-13 20:25:45 +02:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
2019-01-17 13:13:25 +01:00
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
2018-04-13 20:25:45 +02:00
|
|
|
// 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
|
2019-01-17 13:13:25 +01:00
|
|
|
// GNU Affero General Public License for more details.
|
2018-04-13 20:25:45 +02:00
|
|
|
//
|
2019-01-17 13:13:25 +01:00
|
|
|
// 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/>.
|
2018-04-13 20:25:45 +02:00
|
|
|
|
|
|
|
package messages
|
|
|
|
|
|
|
|
import (
|
2019-04-09 17:45:41 +02:00
|
|
|
"encoding/json"
|
2018-04-13 20:25:45 +02:00
|
|
|
"time"
|
|
|
|
|
2019-04-10 01:19:38 +02:00
|
|
|
"maunium.net/go/gomuks/config"
|
2019-01-17 13:13:25 +01:00
|
|
|
"maunium.net/go/mautrix"
|
2019-04-07 17:21:38 +02:00
|
|
|
"maunium.net/go/mauview"
|
2019-01-17 13:13:25 +01:00
|
|
|
"maunium.net/go/tcell"
|
|
|
|
|
2018-04-13 20:25:45 +02:00
|
|
|
"maunium.net/go/gomuks/interface"
|
|
|
|
"maunium.net/go/gomuks/ui/messages/tstring"
|
|
|
|
"maunium.net/go/gomuks/ui/widget"
|
|
|
|
)
|
|
|
|
|
|
|
|
type BaseMessage struct {
|
2019-04-07 17:21:38 +02:00
|
|
|
MsgID string
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgTxnID string
|
2019-04-07 17:21:38 +02:00
|
|
|
MsgType mautrix.MessageType
|
|
|
|
MsgSenderID string
|
|
|
|
MsgSender string
|
|
|
|
MsgSenderColor tcell.Color
|
|
|
|
MsgTimestamp time.Time
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgState mautrix.OutgoingEventState
|
2019-04-07 17:21:38 +02:00
|
|
|
MsgIsHighlight bool
|
|
|
|
MsgIsService bool
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgSource json.RawMessage
|
2019-04-10 01:19:38 +02:00
|
|
|
ReplyTo UIMessage
|
2019-04-07 17:21:38 +02:00
|
|
|
buffer []tstring.TString
|
2018-04-13 20:25:45 +02:00
|
|
|
}
|
|
|
|
|
2019-04-09 17:45:41 +02:00
|
|
|
func newBaseMessage(event *mautrix.Event, displayname string) BaseMessage {
|
|
|
|
msgtype := event.Content.MsgType
|
|
|
|
if len(msgtype) == 0 {
|
|
|
|
msgtype = mautrix.MessageType(event.Type.String())
|
|
|
|
}
|
|
|
|
|
2018-04-13 20:25:45 +02:00
|
|
|
return BaseMessage{
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgSenderID: event.Sender,
|
2019-04-07 17:21:38 +02:00
|
|
|
MsgSender: displayname,
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgTimestamp: unixToTime(event.Timestamp),
|
|
|
|
MsgSenderColor: widget.GetHashColor(event.Sender),
|
2019-04-07 17:21:38 +02:00
|
|
|
MsgType: msgtype,
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgID: event.ID,
|
|
|
|
MsgTxnID: event.Unsigned.TransactionID,
|
|
|
|
MsgState: event.Unsigned.OutgoingState,
|
2019-04-07 17:21:38 +02:00
|
|
|
MsgIsHighlight: false,
|
|
|
|
MsgIsService: false,
|
2019-04-09 17:45:41 +02:00
|
|
|
MsgSource: event.Content.VeryRaw,
|
2018-04-13 20:25:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-09 17:45:41 +02:00
|
|
|
func unixToTime(unix int64) time.Time {
|
|
|
|
timestamp := time.Now()
|
|
|
|
if unix != 0 {
|
|
|
|
timestamp = time.Unix(unix/1000, unix%1000*1000)
|
|
|
|
}
|
|
|
|
return timestamp
|
|
|
|
}
|
|
|
|
|
2018-04-18 17:35:24 +02:00
|
|
|
func (msg *BaseMessage) RegisterMatrix(matrix ifc.MatrixContainer) {}
|
2018-04-13 20:25:45 +02:00
|
|
|
|
|
|
|
// 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 {
|
2019-04-09 17:45:41 +02:00
|
|
|
case mautrix.EventStateLocalEcho:
|
2018-04-13 20:25:45 +02:00
|
|
|
return "Sending..."
|
2019-04-09 17:45:41 +02:00
|
|
|
case mautrix.EventStateSendFail:
|
2018-04-13 20:25:45 +02:00
|
|
|
return "Error"
|
|
|
|
}
|
|
|
|
switch msg.MsgType {
|
|
|
|
case "m.emote":
|
|
|
|
// Emotes don't show a separate sender, it's included in the buffer.
|
|
|
|
return ""
|
|
|
|
default:
|
|
|
|
return msg.MsgSender
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-18 12:38:33 +02:00
|
|
|
func (msg *BaseMessage) SenderID() string {
|
|
|
|
return msg.MsgSenderID
|
|
|
|
}
|
|
|
|
|
2018-04-13 20:25:45 +02:00
|
|
|
func (msg *BaseMessage) RealSender() string {
|
|
|
|
return msg.MsgSender
|
|
|
|
}
|
|
|
|
|
2019-04-09 17:45:41 +02:00
|
|
|
func (msg *BaseMessage) NotificationSenderName() string {
|
|
|
|
return msg.MsgSender
|
|
|
|
}
|
|
|
|
|
2018-04-13 20:25:45 +02:00
|
|
|
func (msg *BaseMessage) getStateSpecificColor() tcell.Color {
|
|
|
|
switch msg.MsgState {
|
2019-04-09 17:45:41 +02:00
|
|
|
case mautrix.EventStateLocalEcho:
|
2018-04-13 20:25:45 +02:00
|
|
|
return tcell.ColorGray
|
2019-04-09 17:45:41 +02:00
|
|
|
case mautrix.EventStateSendFail:
|
2018-04-13 20:25:45 +02:00
|
|
|
return tcell.ColorRed
|
2019-04-09 17:45:41 +02:00
|
|
|
case mautrix.EventStateDefault:
|
2018-04-13 20:25:45 +02:00
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
return tcell.ColorDefault
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SenderColor returns the color the name of the sender should be shown in.
|
|
|
|
//
|
|
|
|
// If the message is being sent, the color is gray.
|
|
|
|
// If sending has failed, the color is red.
|
|
|
|
//
|
|
|
|
// 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 {
|
|
|
|
stateColor := msg.getStateSpecificColor()
|
|
|
|
switch {
|
|
|
|
case stateColor != tcell.ColorDefault:
|
|
|
|
return stateColor
|
2018-05-15 15:05:11 +02:00
|
|
|
case msg.MsgType == "m.room.member":
|
|
|
|
return widget.GetHashColor(msg.MsgSender)
|
2018-04-13 20:25:45 +02:00
|
|
|
case msg.MsgIsService:
|
|
|
|
return tcell.ColorGray
|
|
|
|
default:
|
|
|
|
return msg.MsgSenderColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TextColor returns the color the actual content of the message should be shown in.
|
|
|
|
func (msg *BaseMessage) TextColor() tcell.Color {
|
|
|
|
stateColor := msg.getStateSpecificColor()
|
|
|
|
switch {
|
|
|
|
case stateColor != tcell.ColorDefault:
|
|
|
|
return stateColor
|
2018-04-13 23:34:25 +02:00
|
|
|
case msg.MsgIsService, msg.MsgType == "m.notice":
|
2018-04-13 20:25:45 +02:00
|
|
|
return tcell.ColorGray
|
|
|
|
case msg.MsgIsHighlight:
|
|
|
|
return tcell.ColorYellow
|
|
|
|
case msg.MsgType == "m.room.member":
|
|
|
|
return tcell.ColorGreen
|
|
|
|
default:
|
|
|
|
return tcell.ColorDefault
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TimestampColor returns the color the timestamp should be shown in.
|
|
|
|
//
|
|
|
|
// As with SenderColor(), messages being sent and messages that failed to be sent are
|
|
|
|
// 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 {
|
2019-04-10 16:08:39 +02:00
|
|
|
if msg.MsgIsService {
|
|
|
|
return tcell.ColorGray
|
|
|
|
}
|
2018-04-13 20:25:45 +02:00
|
|
|
return msg.getStateSpecificColor()
|
|
|
|
}
|
|
|
|
|
2019-04-10 01:19:38 +02:00
|
|
|
func (msg *BaseMessage) ReplyHeight() int {
|
|
|
|
if msg.ReplyTo != nil {
|
|
|
|
return 2 + msg.ReplyTo.Height()
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2018-04-13 20:25:45 +02:00
|
|
|
// Height returns the number of rows in the computed buffer (see Buffer()).
|
|
|
|
func (msg *BaseMessage) Height() int {
|
2019-04-10 01:19:38 +02:00
|
|
|
return msg.ReplyHeight() + len(msg.buffer)
|
2018-04-13 20:25:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Timestamp returns the full timestamp when the message was sent.
|
|
|
|
func (msg *BaseMessage) Timestamp() time.Time {
|
|
|
|
return msg.MsgTimestamp
|
|
|
|
}
|
|
|
|
|
|
|
|
// FormatTime returns the formatted time when the message was sent.
|
|
|
|
func (msg *BaseMessage) FormatTime() string {
|
|
|
|
return msg.MsgTimestamp.Format(TimeFormat)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FormatDate returns the formatted date when the message was sent.
|
|
|
|
func (msg *BaseMessage) FormatDate() string {
|
|
|
|
return msg.MsgTimestamp.Format(DateFormat)
|
|
|
|
}
|
|
|
|
|
2019-04-10 00:42:27 +02:00
|
|
|
func (msg *BaseMessage) SameDate(message UIMessage) bool {
|
|
|
|
year1, month1, day1 := msg.Timestamp().Date()
|
|
|
|
year2, month2, day2 := message.Timestamp().Date()
|
|
|
|
return day1 == day2 && month1 == month2 && year1 == year2
|
|
|
|
}
|
|
|
|
|
2018-04-13 20:25:45 +02:00
|
|
|
func (msg *BaseMessage) ID() string {
|
2019-04-10 00:04:39 +02:00
|
|
|
if len(msg.MsgID) == 0 {
|
|
|
|
return msg.MsgTxnID
|
|
|
|
}
|
2018-04-13 20:25:45 +02:00
|
|
|
return msg.MsgID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *BaseMessage) SetID(id string) {
|
|
|
|
msg.MsgID = id
|
|
|
|
}
|
|
|
|
|
2019-04-10 00:04:39 +02:00
|
|
|
func (msg *BaseMessage) TxnID() string {
|
|
|
|
return msg.MsgTxnID
|
|
|
|
}
|
|
|
|
|
2018-11-13 23:00:35 +01:00
|
|
|
func (msg *BaseMessage) Type() mautrix.MessageType {
|
2018-04-13 20:25:45 +02:00
|
|
|
return msg.MsgType
|
|
|
|
}
|
|
|
|
|
2019-04-09 17:45:41 +02:00
|
|
|
func (msg *BaseMessage) State() mautrix.OutgoingEventState {
|
2018-04-13 20:25:45 +02:00
|
|
|
return msg.MsgState
|
|
|
|
}
|
|
|
|
|
2019-04-09 17:45:41 +02:00
|
|
|
func (msg *BaseMessage) SetState(state mautrix.OutgoingEventState) {
|
2018-04-13 20:25:45 +02:00
|
|
|
msg.MsgState = state
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *BaseMessage) IsHighlight() bool {
|
|
|
|
return msg.MsgIsHighlight
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *BaseMessage) SetIsHighlight(isHighlight bool) {
|
|
|
|
msg.MsgIsHighlight = isHighlight
|
|
|
|
}
|
|
|
|
|
2019-04-09 17:45:41 +02:00
|
|
|
func (msg *BaseMessage) Source() json.RawMessage {
|
|
|
|
return msg.MsgSource
|
|
|
|
}
|
|
|
|
|
2019-04-10 01:19:38 +02:00
|
|
|
func (msg *BaseMessage) SetReplyTo(event UIMessage) {
|
|
|
|
msg.ReplyTo = event
|
|
|
|
}
|
|
|
|
|
2019-04-07 17:21:38 +02:00
|
|
|
func (msg *BaseMessage) Draw(screen mauview.Screen) {
|
2019-04-10 01:19:38 +02:00
|
|
|
screen = msg.DrawReply(screen)
|
2019-04-07 17:21:38 +02:00
|
|
|
for y, line := range msg.buffer {
|
|
|
|
line.Draw(screen, 0, y)
|
|
|
|
}
|
|
|
|
}
|
2019-04-10 01:19:38 +02:00
|
|
|
|
2019-04-10 20:06:19 +02:00
|
|
|
func (msg *BaseMessage) clone() BaseMessage {
|
|
|
|
clone := *msg
|
|
|
|
clone.buffer = nil
|
|
|
|
return clone
|
|
|
|
}
|
|
|
|
|
2019-04-10 01:19:38 +02:00
|
|
|
func (msg *BaseMessage) 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 {
|
|
|
|
if msg.ReplyTo == nil {
|
|
|
|
return screen
|
|
|
|
}
|
|
|
|
width, height := screen.Size()
|
|
|
|
replyHeight := msg.ReplyTo.Height()
|
|
|
|
widget.WriteLineSimpleColor(screen, "In reply to", 0, 0, tcell.ColorGreen)
|
|
|
|
widget.WriteLineSimpleColor(screen, msg.ReplyTo.RealSender(), len("In reply to "), 0, msg.ReplyTo.SenderColor())
|
|
|
|
for y := 1; y < 1+replyHeight; y++ {
|
|
|
|
screen.SetCell(0, y, tcell.StyleDefault, '▋')
|
|
|
|
}
|
|
|
|
replyScreen := mauview.NewProxyScreen(screen, 1, 1, width-1, replyHeight)
|
|
|
|
msg.ReplyTo.Draw(replyScreen)
|
|
|
|
return mauview.NewProxyScreen(screen, 0, replyHeight+2, width, height-replyHeight-2)
|
|
|
|
}
|