Reaction rendering

Reactions can now be rendered inline. This can be toggled via the
`ShowReactionsInline` configuration field.
This commit is contained in:
Marcel Schramm 2020-11-22 15:30:07 +01:00
parent 041a8423a7
commit 42a4104c45
No known key found for this signature in database
GPG Key ID: 05971054C70EEDC7
4 changed files with 146 additions and 2 deletions

View File

@ -145,6 +145,9 @@ type Config struct {
// application, as there are some childish goons that deem it funny
// to impersonate people or change their name every 5 minutes.
ShowNicknames bool
// ShowReactionsInline decides whether reactions are displayed below a
// message.
ShowReactionsInline bool
// FileHandlers allow registering specific file-handers for certain
FileOpenHandlers map[string]string
@ -208,6 +211,7 @@ func createDefaultConfig() *Config {
IndicateChannelAccessRestriction: false,
ShowBottomBar: true,
ShowNicknames: true,
ShowReactionsInline: true,
FileOpenHandlers: make(map[string]string),
FileOpenSaveFilesPermanently: false,
FileDownloadSaveLocation: "~/Downloads",

View File

@ -245,3 +245,51 @@ func ReplaceMentions(message *discordgo.Message) string {
}
return strings.NewReplacer(replaceInstructions...).Replace(message.Content)
}
// HandleReactionAdd adds a new reaction to a message or updates the count if
// that message already has a reaction with that same emoji.
func HandleReactionAdd(state *discordgo.State,
message *discordgo.Message,
newReaction *discordgo.MessageReactionAdd) {
for _, reaction := range message.Reactions {
//Only custom emojis have IDs and non custom unes have unique names.
if reaction.Emoji.ID == newReaction.Emoji.ID && reaction.Emoji.Name == newReaction.Emoji.Name {
//Match found, so we can add one to the count.
reaction.Count++
return
}
}
//FIXME Better look up emoji in cache if possible?
message.Reactions = append(message.Reactions, &discordgo.MessageReactions{
Count: 1,
Emoji: &newReaction.Emoji,
Me: newReaction.UserID == state.User.ID,
})
}
// HandleReactionRemove removes an existing reaction to a message or updates
// the count if the same message still has reactions with the same emoji left.
func HandleReactionRemove(state *discordgo.State,
message *discordgo.Message,
newReaction *discordgo.MessageReactionRemove) {
for index, reaction := range message.Reactions {
//Only custom emojis have IDs and non custom unes have unique names.
if reaction.Emoji.ID == newReaction.Emoji.ID && reaction.Emoji.Name == newReaction.Emoji.Name {
if reaction.Count <= 1 {
message.Reactions = append(message.Reactions[:index], message.Reactions[index+1:]...)
//No more reactions of that emoji would be left, therefore we remove the array entry.
} else {
//Only a single user removed his reaction, so we keep the array entry.
reaction.Count--
}
return
}
}
}
// HandleReactionRemoveAll removes all reactions from all users in a message.
func HandleReactionRemoveAll(state *discordgo.State,
message *discordgo.Message) {
message.Reactions = message.Reactions[0:0]
}

View File

@ -714,8 +714,32 @@ func (chatView *ChatView) formatDefaultMessageText(message *discordgo.Message) s
}
}
var reactionText string
if len(message.Reactions) > 0 {
var reactionBuilder strings.Builder
reactionBuilder.Grow(10 + len(message.Reactions)*8)
reactionBuilder.WriteString("\nReactions: ")
for rIndex, reaction := range message.Reactions {
if reaction.Emoji.Name != "" {
reactionBuilder.WriteString(tviewutil.Escape(reaction.Emoji.Name))
if reaction.Me {
reactionBuilder.WriteString("[::r]")
}
reactionBuilder.WriteRune('-')
reactionBuilder.WriteString(strconv.FormatInt(int64(reaction.Count), 10))
if reaction.Me {
reactionBuilder.WriteString("[::-]")
}
if rIndex != len(message.Reactions)-1 {
reactionBuilder.WriteRune(' ')
}
}
}
reactionText = reactionBuilder.String()
}
if !hasRichEmbed {
return messageText
return messageText + reactionText
}
var messageBuffer strings.Builder
@ -808,7 +832,7 @@ func (chatView *ChatView) formatDefaultMessageText(message *discordgo.Message) s
embedBuffer.WriteRune('\n')
}
return messageBuffer.String()
return messageBuffer.String() + reactionText
}
func parseCustomEmojis(text string) string {

View File

@ -742,6 +742,7 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
window.registerMessageEventHandler(messageInputChan, messageEditChan, messageDeleteChan, messageBulkDeleteChan)
window.startMessageHandlerRoutines(messageInputChan, messageEditChan, messageDeleteChan, messageBulkDeleteChan)
window.registerReactionEventHandlers()
window.userList = NewUserTree(window.session.State)
@ -1653,6 +1654,73 @@ func (window *Window) registerMessageEventHandler(input, edit, delete chan *disc
})
}
// registerReactionEventHandlers are responsible for updating the cache if
// reactions are added or removed and updating the chatview if needed.
func (window *Window) registerReactionEventHandlers() {
window.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
message, stateError := s.State.Message(m.ChannelID, m.MessageID)
if message != nil && stateError == nil {
s.State.Lock()
defer func() {
selectedChannel := window.selectedChannel
if selectedChannel != nil && selectedChannel.ID == m.ChannelID {
window.app.QueueUpdateDraw(func() {
window.chatView.Lock()
defer window.chatView.Unlock()
window.chatView.UpdateMessage(message)
})
}
}()
defer s.State.Unlock()
discordutil.HandleReactionAdd(s.State, message, m)
}
})
window.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageReactionRemove) {
message, stateError := s.State.Message(m.ChannelID, m.MessageID)
if message != nil && stateError == nil {
s.State.Lock()
defer func() {
selectedChannel := window.selectedChannel
if selectedChannel != nil && selectedChannel.ID == m.ChannelID {
window.app.QueueUpdateDraw(func() {
window.chatView.Lock()
defer window.chatView.Unlock()
window.chatView.UpdateMessage(message)
})
}
}()
defer s.State.Unlock()
discordutil.HandleReactionRemove(s.State, message, m)
}
})
window.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageReactionRemoveAll) {
message, stateError := s.State.Message(m.ChannelID, m.MessageID)
if message != nil && stateError == nil {
s.State.Lock()
defer func() {
selectedChannel := window.selectedChannel
if selectedChannel != nil && selectedChannel.ID == m.ChannelID {
window.app.QueueUpdateDraw(func() {
window.chatView.Lock()
defer window.chatView.Unlock()
window.chatView.UpdateMessage(message)
})
}
}()
defer s.State.Unlock()
discordutil.HandleReactionRemoveAll(s.State, message)
}
})
}
// QueueUpdateDrawSynchronized is meant to be used by goroutines that aren't
// the main goroutine in order to wait for the UI-Thread to execute the given
// If this method is ever called from the main thread, the application will