feat(chat): render message reactions (#819)

This commit is contained in:
ayn2op 2026-07-30 02:22:21 -04:00 • committed by GitHub
parent 1d21a4b0c0
commit 226936310d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 52 additions and 0 deletions

View file

@ -239,6 +239,8 @@ mention_style = { foreground = "blue", attributes = "bold" }
emoji_style = { foreground = "green" }
url_style = { foreground = "blue" }
attachment_style = { foreground = "yellow" }
reaction_style = { attributes = "dim" }
own_reaction_style = { attributes = "bold" }
message_style = {}
selected_message_style = { attributes = "reverse" }

View file

@ -223,6 +223,8 @@ type (
EmojiStyle StyleWrapper `toml:"emoji_style"`
URLStyle StyleWrapper `toml:"url_style"`
AttachmentStyle StyleWrapper `toml:"attachment_style"`
ReactionStyle StyleWrapper `toml:"reaction_style"`
OwnReactionStyle StyleWrapper `toml:"own_reaction_style"`
MessageStyle StyleWrapper `toml:"message_style"`
SelectedMessageStyle StyleWrapper `toml:"selected_message_style"`

View file

@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
"unicode/utf8"
@ -357,6 +358,30 @@ func (ml *messagesList) writeMessage(builder *tview.LineBuilder, message discord
ml.drawTimestamps(builder, message.Timestamp, baseStyle)
ml.drawAuthor(builder, message, baseStyle)
}
ml.drawReactions(builder, message.Reactions, baseStyle)
}
func (ml *messagesList) drawReactions(builder *tview.LineBuilder, reactions []discord.Reaction, baseStyle tcell.Style) {
if len(reactions) == 0 {
return
}
builder.NewLine()
for i, reaction := range reactions {
if i > 0 {
builder.Write(" ", baseStyle)
}
name := reaction.Emoji.Name
if reaction.Emoji.IsCustom() {
name = ":" + name + ":"
}
style := ml.cfg.Theme.MessagesList.ReactionStyle.Style
if reaction.Me {
style = ml.cfg.Theme.MessagesList.OwnReactionStyle.Style
}
builder.Write(name+" "+strconv.Itoa(reaction.Count), tview.MergeStyle(baseStyle, style))
}
}
func (ml *messagesList) formatTimestamp(ts discord.Timestamp) string {

View file

@ -268,6 +268,14 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd {
m.onPresenceUpdate(eventMsg)
case *gateway.MessageDeleteEvent:
m.onMessageDelete(eventMsg)
case *gateway.MessageReactionAddEvent:
m.onMessageReaction(eventMsg.ChannelID, eventMsg.MessageID)
case *gateway.MessageReactionRemoveEvent:
m.onMessageReaction(eventMsg.ChannelID, eventMsg.MessageID)
case *gateway.MessageReactionRemoveAllEvent:
m.onMessageReaction(eventMsg.ChannelID, eventMsg.MessageID)
case *gateway.MessageReactionRemoveEmojiEvent:
m.onMessageReaction(eventMsg.ChannelID, eventMsg.MessageID)
case *gateway.GuildMembersChunkEvent:
return tview.Batch(m.onGuildMembersChunk(eventMsg), m.listen())

View file

@ -157,6 +157,21 @@ func (m *Model) onMessageDelete(message *gateway.MessageDeleteEvent) {
}
}
func (m *Model) onMessageReaction(channelID discord.ChannelID, messageID discord.MessageID) {
selectedChannel, ok := m.SelectedChannel()
if !ok || selectedChannel.ID != channelID {
return
}
index := slices.IndexFunc(m.messagesList.messages, func(message discord.Message) bool {
return message.ID == messageID
})
message, err := m.state.Cabinet.Message(channelID, messageID)
if index >= 0 && err == nil {
m.messagesList.setMessage(index, *message)
}
}
func cursorAfterDelete(prevCursor, deletedIndex, remaining int) int {
switch {
case prevCursor > deletedIndex: