Add support for displaying reactions
This commit is contained in:
		@@ -284,11 +284,16 @@ func (view *MessageView) replaceBuffer(original *messages.UIMessage, new *messag
 | 
			
		||||
 | 
			
		||||
	view.msgBufferLock.Lock()
 | 
			
		||||
	if new.Height() != end-start {
 | 
			
		||||
		metaBuffer := view.msgBuffer[0:start]
 | 
			
		||||
		for i := 0; i < new.Height(); i++ {
 | 
			
		||||
			metaBuffer = append(metaBuffer, new)
 | 
			
		||||
		height := new.Height()
 | 
			
		||||
 | 
			
		||||
		newBuffer := make([]*messages.UIMessage, height + len(view.msgBuffer) - end)
 | 
			
		||||
		for i := 0; i < height; i++ {
 | 
			
		||||
			newBuffer[i] = new
 | 
			
		||||
		}
 | 
			
		||||
		view.msgBuffer = append(metaBuffer, view.msgBuffer[end:]...)
 | 
			
		||||
		for i := height; i < len(newBuffer); i++ {
 | 
			
		||||
			newBuffer[i] = view.msgBuffer[end + (i - height)]
 | 
			
		||||
		}
 | 
			
		||||
		view.msgBuffer = append(view.msgBuffer[0:start], newBuffer...)
 | 
			
		||||
	} else {
 | 
			
		||||
		for i := start; i < end; i++ {
 | 
			
		||||
			view.msgBuffer[i] = new
 | 
			
		||||
@@ -594,33 +599,38 @@ func (view *MessageView) Draw(screen mauview.Screen) {
 | 
			
		||||
 | 
			
		||||
	var prevMsg *messages.UIMessage
 | 
			
		||||
	view.msgBufferLock.RLock()
 | 
			
		||||
	for line := viewStart; line < height && indexOffset+line < len(view.msgBuffer); line++ {
 | 
			
		||||
	for line := viewStart; line < height && indexOffset+line < len(view.msgBuffer); {
 | 
			
		||||
		index := indexOffset + line
 | 
			
		||||
 | 
			
		||||
		msg := view.msgBuffer[index]
 | 
			
		||||
		if msg != prevMsg {
 | 
			
		||||
			if len(msg.FormatTime()) > 0 {
 | 
			
		||||
				widget.WriteLineSimpleColor(screen, msg.FormatTime(), 0, line, msg.TimestampColor())
 | 
			
		||||
			}
 | 
			
		||||
			// TODO hiding senders might not be that nice after all, maybe an option? (disabled for now)
 | 
			
		||||
			//if !bareMode && (prevMsg == nil || meta.Sender() != prevMsg.Sender()) {
 | 
			
		||||
			widget.WriteLineColor(
 | 
			
		||||
				screen, mauview.AlignRight, msg.Sender(),
 | 
			
		||||
				usernameX, line, view.widestSender(),
 | 
			
		||||
				msg.SenderColor())
 | 
			
		||||
			//}
 | 
			
		||||
			if msg.Edited {
 | 
			
		||||
				// TODO add better indicator for edits
 | 
			
		||||
				screen.SetCell(usernameX + view.widestSender(), line, tcell.StyleDefault.Foreground(tcell.ColorDarkRed), '*')
 | 
			
		||||
			}
 | 
			
		||||
			prevMsg = msg
 | 
			
		||||
		if msg == prevMsg {
 | 
			
		||||
			debug.Print("Unexpected re-encounter of", msg, msg.Height(), "at", line, index)
 | 
			
		||||
			line++
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(msg.FormatTime()) > 0 {
 | 
			
		||||
			widget.WriteLineSimpleColor(screen, msg.FormatTime(), 0, line, msg.TimestampColor())
 | 
			
		||||
		}
 | 
			
		||||
		// TODO hiding senders might not be that nice after all, maybe an option? (disabled for now)
 | 
			
		||||
		//if !bareMode && (prevMsg == nil || meta.Sender() != prevMsg.Sender()) {
 | 
			
		||||
		widget.WriteLineColor(
 | 
			
		||||
			screen, mauview.AlignRight, msg.Sender(),
 | 
			
		||||
			usernameX, line, view.widestSender(),
 | 
			
		||||
			msg.SenderColor())
 | 
			
		||||
		//}
 | 
			
		||||
		if msg.Edited {
 | 
			
		||||
			// TODO add better indicator for edits
 | 
			
		||||
			screen.SetCell(usernameX+view.widestSender(), line, tcell.StyleDefault.Foreground(tcell.ColorDarkRed), '*')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for i := index - 1; i >= 0 && view.msgBuffer[i] == msg; i-- {
 | 
			
		||||
			line--
 | 
			
		||||
		}
 | 
			
		||||
		msg.Draw(mauview.NewProxyScreen(screen, messageX, line, view.width()-messageX, msg.Height()))
 | 
			
		||||
		line += msg.Height() - 1
 | 
			
		||||
		line += msg.Height()
 | 
			
		||||
 | 
			
		||||
		prevMsg = msg
 | 
			
		||||
	}
 | 
			
		||||
	view.msgBufferLock.RUnlock()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ package messages
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/gomuks/config"
 | 
			
		||||
@@ -41,6 +43,29 @@ type MessageRenderer interface {
 | 
			
		||||
	String() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReactionItem struct {
 | 
			
		||||
	Key   string
 | 
			
		||||
	Count int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ri ReactionItem) String() string {
 | 
			
		||||
	return fmt.Sprintf("%d %s", ri.Count, ri.Key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReactionSlice []ReactionItem
 | 
			
		||||
 | 
			
		||||
func (rs ReactionSlice) Len() int {
 | 
			
		||||
	return len(rs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rs ReactionSlice) Less(i, j int) bool {
 | 
			
		||||
	return rs[i].Key < rs[j].Key
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rs ReactionSlice) Swap(i, j int) {
 | 
			
		||||
	rs[i], rs[j] = rs[j], rs[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIMessage struct {
 | 
			
		||||
	EventID            string
 | 
			
		||||
	TxnID              string
 | 
			
		||||
@@ -56,7 +81,10 @@ type UIMessage struct {
 | 
			
		||||
	Edited             bool
 | 
			
		||||
	Event              *event.Event
 | 
			
		||||
	ReplyTo            *UIMessage
 | 
			
		||||
	Reactions          ReactionSlice
 | 
			
		||||
	Renderer           MessageRenderer
 | 
			
		||||
 | 
			
		||||
	reactionBuffer string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DateFormat = "January _2, 2006"
 | 
			
		||||
@@ -68,6 +96,15 @@ func newUIMessage(evt *event.Event, displayname string, renderer MessageRenderer
 | 
			
		||||
		msgtype = mautrix.MessageType(evt.Type.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reactions := make(ReactionSlice, 0, len(evt.Unsigned.Relations.Annotations.Map))
 | 
			
		||||
	for key, count := range evt.Unsigned.Relations.Annotations.Map {
 | 
			
		||||
		reactions = append(reactions, ReactionItem{
 | 
			
		||||
			Key:   key,
 | 
			
		||||
			Count: count,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(reactions)
 | 
			
		||||
 | 
			
		||||
	return &UIMessage{
 | 
			
		||||
		SenderID:           evt.Sender,
 | 
			
		||||
		SenderName:         displayname,
 | 
			
		||||
@@ -81,11 +118,31 @@ func newUIMessage(evt *event.Event, displayname string, renderer MessageRenderer
 | 
			
		||||
		IsHighlight:        false,
 | 
			
		||||
		IsService:          false,
 | 
			
		||||
		Edited:             len(evt.Gomuks.Edits) > 0,
 | 
			
		||||
		Reactions:          reactions,
 | 
			
		||||
		Event:              evt,
 | 
			
		||||
		Renderer:           renderer,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) AddReaction(key string) {
 | 
			
		||||
	found := false
 | 
			
		||||
	for _, rs := range msg.Reactions {
 | 
			
		||||
		if rs.Key == key {
 | 
			
		||||
			rs.Count++
 | 
			
		||||
			found = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !found {
 | 
			
		||||
		msg.Reactions = append(msg.Reactions, ReactionItem{
 | 
			
		||||
			Key:   key,
 | 
			
		||||
			Count: 1,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(msg.Reactions)
 | 
			
		||||
	msg.CalculateReactionBuffer()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unixToTime(unix int64) time.Time {
 | 
			
		||||
	timestamp := time.Now()
 | 
			
		||||
	if unix != 0 {
 | 
			
		||||
@@ -195,9 +252,16 @@ func (msg *UIMessage) ReplyHeight() int {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) ReactionHeight() int {
 | 
			
		||||
	if len(msg.Reactions) > 0 {
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Height returns the number of rows in the computed buffer (see Buffer()).
 | 
			
		||||
func (msg *UIMessage) Height() int {
 | 
			
		||||
	return msg.ReplyHeight() + msg.Renderer.Height()
 | 
			
		||||
	return msg.ReplyHeight() + msg.Renderer.Height() + msg.ReactionHeight()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) Time() time.Time {
 | 
			
		||||
@@ -235,14 +299,25 @@ func (msg *UIMessage) SetIsHighlight(isHighlight bool) {
 | 
			
		||||
	msg.IsHighlight = isHighlight
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) DrawReactions(screen mauview.Screen) {
 | 
			
		||||
	if len(msg.Reactions) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	width, height := screen.Size()
 | 
			
		||||
	screen = mauview.NewProxyScreen(screen, 0, height-1, width, 1)
 | 
			
		||||
	mauview.Print(screen, msg.reactionBuffer, 0, 0, width, mauview.AlignLeft, mauview.Styles.PrimaryTextColor)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) Draw(screen mauview.Screen) {
 | 
			
		||||
	screen = msg.DrawReply(screen)
 | 
			
		||||
	msg.Renderer.Draw(screen)
 | 
			
		||||
	msg.DrawReactions(screen)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) Clone() *UIMessage {
 | 
			
		||||
	clone := *msg
 | 
			
		||||
	clone.ReplyTo = nil
 | 
			
		||||
	clone.Reactions = nil
 | 
			
		||||
	clone.Renderer = clone.Renderer.Clone()
 | 
			
		||||
	return &clone
 | 
			
		||||
}
 | 
			
		||||
@@ -254,9 +329,19 @@ func (msg *UIMessage) CalculateReplyBuffer(preferences config.UserPreferences, w
 | 
			
		||||
	msg.ReplyTo.CalculateBuffer(preferences, width-1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) CalculateReactionBuffer() {
 | 
			
		||||
	var text strings.Builder
 | 
			
		||||
	for _, reaction := range msg.Reactions {
 | 
			
		||||
		text.WriteString(reaction.String())
 | 
			
		||||
		text.WriteRune(' ')
 | 
			
		||||
	}
 | 
			
		||||
	msg.reactionBuffer = text.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) CalculateBuffer(preferences config.UserPreferences, width int) {
 | 
			
		||||
	msg.Renderer.CalculateBuffer(preferences, width, msg)
 | 
			
		||||
	msg.CalculateReplyBuffer(preferences, width)
 | 
			
		||||
	msg.CalculateReactionBuffer()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) DrawReply(screen mauview.Screen) mauview.Screen {
 | 
			
		||||
@@ -286,8 +371,7 @@ func (msg *UIMessage) String() string {
 | 
			
		||||
		msg.EventID, msg.TxnID,
 | 
			
		||||
		msg.Type, msg.Timestamp.String(),
 | 
			
		||||
		msg.SenderID, msg.SenderName, msg.DefaultSenderColor.Hex(),
 | 
			
		||||
		msg.IsService, msg.IsHighlight, msg.Renderer.String(),
 | 
			
		||||
	)
 | 
			
		||||
		msg.IsService, msg.IsHighlight, msg.Renderer.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (msg *UIMessage) PlainText() string {
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ func ParseEvent(matrix ifc.MatrixContainer, mainView ifc.MainView, room *rooms.R
 | 
			
		||||
		} else if replyToEvt, _ := matrix.GetEvent(room, evt.Content.GetReplyTo()); replyToEvt != nil {
 | 
			
		||||
			if replyToMsg := directParseEvent(matrix, room, replyToEvt); replyToMsg != nil {
 | 
			
		||||
				msg.ReplyTo = replyToMsg
 | 
			
		||||
				msg.ReplyTo.Reactions = nil
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO add unrenderable reply header
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -558,6 +558,23 @@ func (view *RoomView) AddEdit(evt *event.Event) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *RoomView) AddReaction(evt *event.Event, key string) {
 | 
			
		||||
	msgView := view.MessageView()
 | 
			
		||||
	msg := msgView.getMessageByID(evt.ID)
 | 
			
		||||
	if msg == nil {
 | 
			
		||||
		// Message not in view, nothing to do
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	recalculate := len(msg.Reactions) == 0
 | 
			
		||||
	msg.AddReaction(key)
 | 
			
		||||
	if recalculate {
 | 
			
		||||
		debug.Print(msg.ReactionHeight(), msg.Height())
 | 
			
		||||
		// Recalculate height for message
 | 
			
		||||
		msg.CalculateBuffer(msgView.prevPrefs, msgView.prevWidth())
 | 
			
		||||
		msgView.replaceBuffer(msg, msg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (view *RoomView) GetEvent(eventID string) ifc.Message {
 | 
			
		||||
	message, ok := view.content.messageIDs[eventID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user