Unbreak things
This commit is contained in:
@ -86,26 +86,26 @@ func cmdHeapProfile(cmd *Command) {
|
||||
func cmdRainbow(cmd *Command) {
|
||||
text := strings.Join(cmd.Args, " ")
|
||||
var html strings.Builder
|
||||
fmt.Fprint(&html, "**🌈** ")
|
||||
_, _ = fmt.Fprint(&html, "**🌈** ")
|
||||
for i, char := range text {
|
||||
if unicode.IsSpace(char) {
|
||||
html.WriteRune(char)
|
||||
continue
|
||||
}
|
||||
color := rainbow.GetInterpolatedColorFor(float64(i) / float64(len(text))).Hex()
|
||||
fmt.Fprintf(&html, "<font color=\"%s\">%c</font>", color, char)
|
||||
_, _ = fmt.Fprintf(&html, "<font color=\"%s\">%c</font>", color, char)
|
||||
}
|
||||
go cmd.Room.SendMessage("m.text", html.String())
|
||||
cmd.UI.Render()
|
||||
}
|
||||
|
||||
func cmdQuit(cmd *Command) {
|
||||
cmd.Gomuks.Stop()
|
||||
cmd.Gomuks.Stop(true)
|
||||
}
|
||||
|
||||
func cmdClearCache(cmd *Command) {
|
||||
cmd.Config.Clear()
|
||||
cmd.Gomuks.Stop()
|
||||
cmd.Gomuks.Stop(false)
|
||||
}
|
||||
|
||||
func cmdUnknownCommand(cmd *Command) {
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/mattn/go-runewidth"
|
||||
sync "github.com/sasha-s/go-deadlock"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mauview"
|
||||
"maunium.net/go/tcell"
|
||||
|
||||
@ -58,11 +59,13 @@ type MessageView struct {
|
||||
prevPrefs config.UserPreferences
|
||||
|
||||
messageIDLock sync.RWMutex
|
||||
messageIDs map[string]messages.UIMessage
|
||||
messageIDs map[string]*messages.UIMessage
|
||||
messagesLock sync.RWMutex
|
||||
messages []messages.UIMessage
|
||||
messages []*messages.UIMessage
|
||||
msgBufferLock sync.RWMutex
|
||||
msgBuffer []messages.UIMessage
|
||||
msgBuffer []*messages.UIMessage
|
||||
|
||||
initialHistoryLoaded bool
|
||||
}
|
||||
|
||||
func NewMessageView(parent *RoomView) *MessageView {
|
||||
@ -74,9 +77,9 @@ func NewMessageView(parent *RoomView) *MessageView {
|
||||
TimestampWidth: len(messages.TimeFormat),
|
||||
ScrollOffset: 0,
|
||||
|
||||
messages: make([]messages.UIMessage, 0),
|
||||
messageIDs: make(map[string]messages.UIMessage),
|
||||
msgBuffer: make([]messages.UIMessage, 0),
|
||||
messages: make([]*messages.UIMessage, 0),
|
||||
messageIDs: make(map[string]*messages.UIMessage),
|
||||
msgBuffer: make([]*messages.UIMessage, 0),
|
||||
|
||||
_width: 80,
|
||||
_widestSender: 5,
|
||||
@ -108,20 +111,22 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
|
||||
if ifcMessage == nil {
|
||||
return
|
||||
}
|
||||
message, ok := ifcMessage.(messages.UIMessage)
|
||||
if !ok {
|
||||
message, ok := ifcMessage.(*messages.UIMessage)
|
||||
if !ok || message == nil {
|
||||
debug.Print("[Warning] Passed non-UIMessage ifc.Message object to AddMessage().")
|
||||
debug.PrintStack()
|
||||
return
|
||||
}
|
||||
|
||||
var oldMsg messages.UIMessage
|
||||
if oldMsg = view.getMessageByID(message.ID()); oldMsg != nil {
|
||||
var oldMsg *messages.UIMessage
|
||||
if oldMsg = view.getMessageByID(message.EventID); oldMsg != nil {
|
||||
view.replaceMessage(oldMsg, message)
|
||||
direction = IgnoreMessage
|
||||
} else if oldMsg = view.getMessageByID(message.TxnID()); oldMsg != nil {
|
||||
} else if oldMsg = view.getMessageByID(message.TxnID); oldMsg != nil {
|
||||
view.replaceMessage(oldMsg, message)
|
||||
view.deleteMessageID(message.TxnID())
|
||||
view.deleteMessageID(message.TxnID)
|
||||
direction = IgnoreMessage
|
||||
} else if oldMsg = view.getMessageByID(message.Relation.EventID); oldMsg != nil {
|
||||
direction = IgnoreMessage
|
||||
}
|
||||
|
||||
@ -134,7 +139,7 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
|
||||
}
|
||||
message.CalculateBuffer(view.config.Preferences, width)
|
||||
|
||||
makeDateChange := func() messages.UIMessage {
|
||||
makeDateChange := func() *messages.UIMessage {
|
||||
dateChange := messages.NewDateChangeMessage(
|
||||
fmt.Sprintf("Date changed to %s", message.FormatDate()))
|
||||
dateChange.CalculateBuffer(view.config.Preferences, width)
|
||||
@ -157,9 +162,9 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
|
||||
} else if direction == PrependMessage {
|
||||
view.messagesLock.Lock()
|
||||
if len(view.messages) > 0 && !view.messages[0].SameDate(message) {
|
||||
view.messages = append([]messages.UIMessage{message, makeDateChange()}, view.messages...)
|
||||
view.messages = append([]*messages.UIMessage{message, makeDateChange()}, view.messages...)
|
||||
} else {
|
||||
view.messages = append([]messages.UIMessage{message}, view.messages...)
|
||||
view.messages = append([]*messages.UIMessage{message}, view.messages...)
|
||||
}
|
||||
view.messagesLock.Unlock()
|
||||
} else if oldMsg != nil {
|
||||
@ -174,7 +179,7 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
|
||||
}
|
||||
}
|
||||
|
||||
func (view *MessageView) replaceMessage(original messages.UIMessage, new messages.UIMessage) {
|
||||
func (view *MessageView) replaceMessage(original *messages.UIMessage, new *messages.UIMessage) {
|
||||
if len(new.ID()) > 0 {
|
||||
view.setMessageID(new)
|
||||
}
|
||||
@ -187,7 +192,10 @@ func (view *MessageView) replaceMessage(original messages.UIMessage, new message
|
||||
view.messagesLock.Unlock()
|
||||
}
|
||||
|
||||
func (view *MessageView) getMessageByID(id string) messages.UIMessage {
|
||||
func (view *MessageView) getMessageByID(id string) *messages.UIMessage {
|
||||
if id == "" {
|
||||
return nil
|
||||
}
|
||||
view.messageIDLock.RLock()
|
||||
defer view.messageIDLock.RUnlock()
|
||||
msg, ok := view.messageIDs[id]
|
||||
@ -198,31 +206,37 @@ func (view *MessageView) getMessageByID(id string) messages.UIMessage {
|
||||
}
|
||||
|
||||
func (view *MessageView) deleteMessageID(id string) {
|
||||
if id == "" {
|
||||
return
|
||||
}
|
||||
view.messageIDLock.Lock()
|
||||
delete(view.messageIDs, id)
|
||||
view.messageIDLock.Unlock()
|
||||
}
|
||||
|
||||
func (view *MessageView) setMessageID(message messages.UIMessage) {
|
||||
func (view *MessageView) setMessageID(message *messages.UIMessage) {
|
||||
if message.ID() == "" {
|
||||
return
|
||||
}
|
||||
view.messageIDLock.Lock()
|
||||
view.messageIDs[message.ID()] = message
|
||||
view.messageIDLock.Unlock()
|
||||
}
|
||||
|
||||
func (view *MessageView) appendBuffer(message messages.UIMessage) {
|
||||
func (view *MessageView) appendBuffer(message *messages.UIMessage) {
|
||||
view.msgBufferLock.Lock()
|
||||
view.appendBufferUnlocked(message)
|
||||
view.msgBufferLock.Unlock()
|
||||
}
|
||||
|
||||
func (view *MessageView) appendBufferUnlocked(message messages.UIMessage) {
|
||||
func (view *MessageView) appendBufferUnlocked(message *messages.UIMessage) {
|
||||
for i := 0; i < message.Height(); i++ {
|
||||
view.msgBuffer = append(view.msgBuffer, message)
|
||||
}
|
||||
view.prevMsgCount++
|
||||
}
|
||||
|
||||
func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages.UIMessage) {
|
||||
func (view *MessageView) replaceBuffer(original *messages.UIMessage, new *messages.UIMessage) {
|
||||
start := -1
|
||||
end := -1
|
||||
view.msgBufferLock.RLock()
|
||||
@ -240,7 +254,7 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages
|
||||
|
||||
if start == -1 {
|
||||
debug.Print("Called replaceBuffer() with message that was not in the buffer:", original)
|
||||
debug.PrintStack()
|
||||
//debug.PrintStack()
|
||||
view.appendBuffer(new)
|
||||
return
|
||||
}
|
||||
@ -280,7 +294,7 @@ func (view *MessageView) recalculateBuffers() {
|
||||
if !prefs.BareMessageView {
|
||||
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender() + SenderMessageGap
|
||||
}
|
||||
view.msgBuffer = []messages.UIMessage{}
|
||||
view.msgBuffer = []*messages.UIMessage{}
|
||||
view.prevMsgCount = 0
|
||||
for i, message := range view.messages {
|
||||
if message == nil {
|
||||
@ -299,17 +313,17 @@ func (view *MessageView) recalculateBuffers() {
|
||||
view.prevPrefs = prefs
|
||||
}
|
||||
|
||||
func (view *MessageView) handleMessageClick(message messages.UIMessage) bool {
|
||||
switch message := message.(type) {
|
||||
func (view *MessageView) handleMessageClick(message *messages.UIMessage) bool {
|
||||
switch msg := message.Renderer.(type) {
|
||||
case *messages.ImageMessage:
|
||||
open.Open(message.Path())
|
||||
case messages.UIMessage:
|
||||
open.Open(msg.Path())
|
||||
default:
|
||||
debug.Print("Message clicked:", message)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (view *MessageView) handleUsernameClick(message messages.UIMessage, prevMessage messages.UIMessage) bool {
|
||||
func (view *MessageView) handleUsernameClick(message *messages.UIMessage, prevMessage *messages.UIMessage) bool {
|
||||
if prevMessage != nil && prevMessage.Sender() == message.Sender() {
|
||||
return false
|
||||
}
|
||||
@ -317,7 +331,7 @@ func (view *MessageView) handleUsernameClick(message messages.UIMessage, prevMes
|
||||
if len(message.Sender()) == 0 {
|
||||
return false
|
||||
}
|
||||
sender := fmt.Sprintf("[%s](https://matrix.to/#/%s)", message.Sender(), message.SenderID())
|
||||
sender := fmt.Sprintf("[%s](https://matrix.to/#/%s)", message.Sender(), message.SenderID)
|
||||
|
||||
cursorPos := view.parent.input.GetCursorOffset()
|
||||
text := view.parent.input.GetText()
|
||||
@ -363,7 +377,7 @@ func (view *MessageView) OnMouseEvent(event mauview.MouseEvent) bool {
|
||||
|
||||
view.msgBufferLock.RLock()
|
||||
message := view.msgBuffer[line]
|
||||
var prevMessage messages.UIMessage
|
||||
var prevMessage *messages.UIMessage
|
||||
if y != 0 && line > 0 {
|
||||
prevMessage = view.msgBuffer[line-1]
|
||||
}
|
||||
@ -496,7 +510,7 @@ func (view *MessageView) getIndexOffset(screen mauview.Screen, height, messageX
|
||||
func (view *MessageView) CapturePlaintext(height int) string {
|
||||
var buf strings.Builder
|
||||
indexOffset := view.TotalHeight() - view.ScrollOffset - height
|
||||
var prevMessage messages.UIMessage
|
||||
var prevMessage *messages.UIMessage
|
||||
view.msgBufferLock.RLock()
|
||||
for line := 0; line < height; line++ {
|
||||
index := indexOffset + line
|
||||
@ -504,14 +518,13 @@ func (view *MessageView) CapturePlaintext(height int) string {
|
||||
continue
|
||||
}
|
||||
|
||||
meta := view.msgBuffer[index]
|
||||
message, ok := meta.(messages.UIMessage)
|
||||
if ok && message != prevMessage {
|
||||
message := view.msgBuffer[index]
|
||||
if message != prevMessage {
|
||||
var sender string
|
||||
if len(message.Sender()) > 0 {
|
||||
sender = fmt.Sprintf(" <%s>", message.Sender())
|
||||
} else if message.Type() == "m.emote" {
|
||||
sender = fmt.Sprintf(" * %s", message.RealSender())
|
||||
} else if message.Type == mautrix.MsgEmote {
|
||||
sender = fmt.Sprintf(" * %s", message.SenderName)
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s%s %s\n", message.FormatTime(), sender, message.PlainText())
|
||||
prevMessage = message
|
||||
@ -561,7 +574,7 @@ func (view *MessageView) Draw(screen mauview.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
var prevMsg messages.UIMessage
|
||||
var prevMsg *messages.UIMessage
|
||||
view.msgBufferLock.RLock()
|
||||
for line := viewStart; line < height && indexOffset+line < len(view.msgBuffer); line++ {
|
||||
index := indexOffset + line
|
||||
|
@ -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
|
||||
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
|
||||
}
|
||||
|
||||
func newBaseMessage(event *mautrix.Event, displayname string) BaseMessage {
|
||||
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,76 @@ 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
|
||||
return msg.EventID
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetID(id string) {
|
||||
msg.MsgID = id
|
||||
func (msg *UIMessage) SetID(id string) {
|
||||
msg.EventID = id
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) TxnID() string {
|
||||
return msg.MsgTxnID
|
||||
func (msg *UIMessage) SetIsHighlight(isHighlight bool) {
|
||||
// TODO Textmessage cache needs to be cleared
|
||||
msg.IsHighlight = isHighlight
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) Source() json.RawMessage {
|
||||
return msg.MsgSource
|
||||
}
|
||||
|
||||
func (msg *BaseMessage) SetReplyTo(event UIMessage) {
|
||||
msg.ReplyTo = event
|
||||
}
|
||||
|
||||
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.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-1, msg)
|
||||
}
|
||||
|
||||
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 +272,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()
|
||||
}
|
||||
|
@ -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) CalculateBuffer(prefs config.UserPreferences, width int) {
|
||||
msg.CalculateReplyBuffer(prefs, width)
|
||||
msg.calculateBufferWithText(prefs, msg.MsgText, width)
|
||||
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) 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) {}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
@ -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,19 @@ 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 {
|
||||
if replyToMsg := getCachedEvent(mainView, room.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)
|
||||
replyToMsg.ReplyTo = nil
|
||||
msg.ReplyTo = replyToMsg
|
||||
} 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 +65,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 +88,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 +121,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 +219,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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) CalculateBuffer(prefs config.UserPreferences, width int) {
|
||||
msg.CalculateReplyBuffer(prefs, width)
|
||||
msg.calculateBufferWithText(prefs, msg.getCache(), width)
|
||||
func (msg *TextMessage) String() string {
|
||||
return fmt.Sprintf(`&messages.TextMessage{Text="%s"}`, msg.Text)
|
||||
}
|
||||
|
||||
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) {}
|
||||
|
@ -236,6 +236,10 @@ func (list *RoomList) AddScrollOffset(offset int) {
|
||||
func (list *RoomList) First() (string, *rooms.Room) {
|
||||
list.RLock()
|
||||
defer list.RUnlock()
|
||||
return list.first()
|
||||
}
|
||||
|
||||
func (list *RoomList) first() (string, *rooms.Room) {
|
||||
for _, tag := range list.tags {
|
||||
trl := list.items[tag]
|
||||
if trl.HasVisibleRooms() {
|
||||
@ -248,6 +252,10 @@ func (list *RoomList) First() (string, *rooms.Room) {
|
||||
func (list *RoomList) Last() (string, *rooms.Room) {
|
||||
list.RLock()
|
||||
defer list.RUnlock()
|
||||
return list.last()
|
||||
}
|
||||
|
||||
func (list *RoomList) last() (string, *rooms.Room) {
|
||||
for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- {
|
||||
tag := list.tags[tagIndex]
|
||||
trl := list.items[tag]
|
||||
@ -273,7 +281,7 @@ func (list *RoomList) Previous() (string, *rooms.Room) {
|
||||
if len(list.items) == 0 {
|
||||
return "", nil
|
||||
} else if list.selected == nil {
|
||||
return list.First()
|
||||
return list.first()
|
||||
}
|
||||
|
||||
trl := list.items[list.selectedTag]
|
||||
@ -295,11 +303,11 @@ func (list *RoomList) Previous() (string, *rooms.Room) {
|
||||
return prevTag, prevTRL.LastVisible()
|
||||
}
|
||||
}
|
||||
return list.Last()
|
||||
return list.last()
|
||||
} else if index >= 0 {
|
||||
return list.selectedTag, trl.Visible()[index+1].Room
|
||||
}
|
||||
return list.First()
|
||||
return list.first()
|
||||
}
|
||||
|
||||
func (list *RoomList) Next() (string, *rooms.Room) {
|
||||
@ -308,7 +316,7 @@ func (list *RoomList) Next() (string, *rooms.Room) {
|
||||
if len(list.items) == 0 {
|
||||
return "", nil
|
||||
} else if list.selected == nil {
|
||||
return list.First()
|
||||
return list.first()
|
||||
}
|
||||
|
||||
trl := list.items[list.selectedTag]
|
||||
@ -330,11 +338,11 @@ func (list *RoomList) Next() (string, *rooms.Room) {
|
||||
return nextTag, nextTRL.FirstVisible()
|
||||
}
|
||||
}
|
||||
return list.First()
|
||||
return list.first()
|
||||
} else if index > 0 {
|
||||
return list.selectedTag, trl.Visible()[index-1].Room
|
||||
}
|
||||
return list.Last()
|
||||
return list.last()
|
||||
}
|
||||
|
||||
// NextWithActivity Returns next room with activity.
|
||||
|
@ -57,6 +57,8 @@ type RoomView struct {
|
||||
ulBorderScreen *mauview.ProxyScreen
|
||||
ulScreen *mauview.ProxyScreen
|
||||
|
||||
userListLoaded bool
|
||||
|
||||
prevScreen mauview.Screen
|
||||
|
||||
parent *MainView
|
||||
@ -99,7 +101,6 @@ func NewRoomView(parent *MainView, room *rooms.Room) *RoomView {
|
||||
SetTabCompleteFunc(view.InputTabComplete)
|
||||
|
||||
view.topic.
|
||||
SetText(strings.Replace(room.GetTopic(), "\n", " ", -1)).
|
||||
SetTextColor(tcell.ColorWhite).
|
||||
SetBackgroundColor(tcell.ColorDarkGreen)
|
||||
|
||||
@ -385,11 +386,11 @@ func (view *RoomView) SendMessage(msgtype mautrix.MessageType, text string) {
|
||||
text = emoji.Sprint(text)
|
||||
}
|
||||
evt := view.parent.matrix.PrepareMarkdownMessage(view.Room.ID, msgtype, text)
|
||||
msg := view.ParseEvent(evt)
|
||||
msg := view.parseEvent(evt)
|
||||
view.AddMessage(msg)
|
||||
eventID, err := view.parent.matrix.SendEvent(evt)
|
||||
if err != nil {
|
||||
msg.SetState(mautrix.EventStateSendFail)
|
||||
msg.State = mautrix.EventStateSendFail
|
||||
// Show shorter version if available
|
||||
if httpErr, ok := err.(mautrix.HTTPError); ok {
|
||||
err = httpErr
|
||||
@ -401,7 +402,10 @@ func (view *RoomView) SendMessage(msgtype mautrix.MessageType, text string) {
|
||||
view.parent.parent.Render()
|
||||
} else {
|
||||
debug.Print("Event ID received:", eventID)
|
||||
//view.MessageView().UpdateMessageID(msg, eventID)
|
||||
msg.EventID = eventID
|
||||
msg.State = mautrix.EventStateDefault
|
||||
view.MessageView().setMessageID(msg)
|
||||
view.parent.parent.Render()
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,12 +417,20 @@ func (view *RoomView) MxRoom() *rooms.Room {
|
||||
return view.Room
|
||||
}
|
||||
|
||||
func (view *RoomView) Update() {
|
||||
view.topic.SetText(strings.Replace(view.Room.GetTopic(), "\n", " ", -1))
|
||||
if !view.userListLoaded {
|
||||
view.UpdateUserList()
|
||||
}
|
||||
}
|
||||
|
||||
func (view *RoomView) UpdateUserList() {
|
||||
pls := &mautrix.PowerLevels{}
|
||||
if plEvent := view.Room.GetStateEvent(mautrix.StatePowerLevels, ""); plEvent != nil {
|
||||
pls = plEvent.Content.GetPowerLevels()
|
||||
}
|
||||
view.userList.Update(view.Room.GetMembers(), pls)
|
||||
view.userListLoaded = true
|
||||
}
|
||||
|
||||
func (view *RoomView) AddServiceMessage(text string) {
|
||||
@ -429,10 +441,18 @@ func (view *RoomView) AddMessage(message ifc.Message) {
|
||||
view.content.AddMessage(message, AppendMessage)
|
||||
}
|
||||
|
||||
func (view *RoomView) ParseEvent(evt *mautrix.Event) ifc.Message {
|
||||
func (view *RoomView) parseEvent(evt *mautrix.Event) *messages.UIMessage {
|
||||
return messages.ParseEvent(view.parent.matrix, view.parent, view.Room, evt)
|
||||
}
|
||||
|
||||
func (view *RoomView) ParseEvent(evt *mautrix.Event) ifc.Message {
|
||||
msg := view.parseEvent(evt)
|
||||
if msg == nil {
|
||||
return nil
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func (view *RoomView) GetEvent(eventID string) ifc.Message {
|
||||
message, ok := view.content.messageIDs[eventID]
|
||||
if !ok {
|
||||
|
@ -74,7 +74,7 @@ func (ui *GomuksUI) NewLoginView() mauview.Component {
|
||||
view.username.SetText(ui.gmx.Config().UserID)
|
||||
view.password.SetMaskCharacter('*')
|
||||
|
||||
view.quitButton.SetOnClick(ui.gmx.Stop).SetBackgroundColor(tcell.ColorDarkCyan)
|
||||
view.quitButton.SetOnClick(func() { ui.gmx.Stop(true) }).SetBackgroundColor(tcell.ColorDarkCyan)
|
||||
view.loginButton.SetOnClick(view.Login).SetBackgroundColor(tcell.ColorDarkCyan)
|
||||
|
||||
view.SetColumns([]int{1, 10, 1, 9, 1, 9, 1, 10, 1})
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
|
||||
sync "github.com/sasha-s/go-deadlock"
|
||||
|
||||
"maunium.net/go/gomuks/ui/messages"
|
||||
"maunium.net/go/mauview"
|
||||
"maunium.net/go/tcell"
|
||||
|
||||
@ -256,6 +257,7 @@ func (view *MainView) switchRoom(tag string, room *rooms.Room, lock bool) {
|
||||
if room == nil {
|
||||
return
|
||||
}
|
||||
room.Load()
|
||||
|
||||
roomView, ok := view.getRoomView(room.ID, lock)
|
||||
if !ok {
|
||||
@ -263,12 +265,15 @@ func (view *MainView) switchRoom(tag string, room *rooms.Room, lock bool) {
|
||||
debug.Print(tag, room)
|
||||
return
|
||||
}
|
||||
roomView.Update()
|
||||
view.roomView.SetInnerComponent(roomView)
|
||||
view.currentRoom = roomView
|
||||
view.MarkRead(roomView)
|
||||
view.roomList.SetSelected(tag, room)
|
||||
view.parent.Render()
|
||||
if len(roomView.MessageView().messages) == 0 {
|
||||
|
||||
if msgView := roomView.MessageView(); len(msgView.messages) < 20 && !msgView.initialHistoryLoaded {
|
||||
msgView.initialHistoryLoaded = true
|
||||
go view.LoadHistory(room.ID)
|
||||
}
|
||||
}
|
||||
@ -278,12 +283,6 @@ func (view *MainView) addRoomPage(room *rooms.Room) *RoomView {
|
||||
roomView := NewRoomView(view, room).
|
||||
SetInputChangedFunc(view.InputChanged)
|
||||
view.rooms[room.ID] = roomView
|
||||
roomView.UpdateUserList()
|
||||
|
||||
// TODO make sure this works
|
||||
if len(roomView.MessageView().messages) == 0 {
|
||||
go view.LoadHistory(room.ID)
|
||||
}
|
||||
return roomView
|
||||
}
|
||||
return nil
|
||||
@ -292,7 +291,7 @@ func (view *MainView) addRoomPage(room *rooms.Room) *RoomView {
|
||||
func (view *MainView) GetRoom(roomID string) ifc.RoomView {
|
||||
room, ok := view.getRoomView(roomID, true)
|
||||
if !ok {
|
||||
return view.addRoom(view.matrix.GetRoom(roomID))
|
||||
return view.addRoom(view.matrix.GetOrCreateRoom(roomID))
|
||||
}
|
||||
return room
|
||||
}
|
||||
@ -348,11 +347,11 @@ func (view *MainView) addRoom(room *rooms.Room) *RoomView {
|
||||
return roomView
|
||||
}
|
||||
|
||||
func (view *MainView) SetRooms(rooms map[string]*rooms.Room) {
|
||||
func (view *MainView) SetRooms(rooms *rooms.RoomCache) {
|
||||
view.roomList.Clear()
|
||||
view.roomsLock.Lock()
|
||||
view.rooms = make(map[string]*RoomView)
|
||||
for _, room := range rooms {
|
||||
for _, room := range rooms.Map {
|
||||
if room.HasLeft {
|
||||
continue
|
||||
}
|
||||
@ -390,7 +389,8 @@ func sendNotification(room *rooms.Room, sender, text string, critical, sound boo
|
||||
|
||||
func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, should pushrules.PushActionArrayShould) {
|
||||
view.roomList.Bump(room)
|
||||
if message.SenderID() == view.config.UserID {
|
||||
uiMsg, ok := message.(*messages.UIMessage)
|
||||
if ok && uiMsg.SenderID == view.config.UserID {
|
||||
return
|
||||
}
|
||||
// Whether or not the room where the message came is the currently shown room.
|
||||
@ -420,16 +420,6 @@ func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, shoul
|
||||
message.SetIsHighlight(should.Highlight)
|
||||
}
|
||||
|
||||
func (view *MainView) InitialSyncDone() {
|
||||
view.roomList.Clear()
|
||||
view.roomsLock.RLock()
|
||||
for _, room := range view.rooms {
|
||||
view.roomList.Add(room.Room)
|
||||
room.UpdateUserList()
|
||||
}
|
||||
view.roomsLock.RUnlock()
|
||||
}
|
||||
|
||||
func (view *MainView) LoadHistory(roomID string) {
|
||||
defer debug.Recover()
|
||||
roomView, ok := view.getRoomView(roomID, true)
|
||||
|
Reference in New Issue
Block a user