diff --git a/plugins/chat/assets/javascripts/discourse/models/chat-message.js b/plugins/chat/assets/javascripts/discourse/models/chat-message.js index 28a86dd8680..a7e5685bdc1 100644 --- a/plugins/chat/assets/javascripts/discourse/models/chat-message.js +++ b/plugins/chat/assets/javascripts/discourse/models/chat-message.js @@ -130,7 +130,13 @@ export default class ChatMessage { if (existingReaction) { if (action === "add") { if (selfReaction && existingReaction.reacted) { - return false; + return; + } + + // we might receive a message bus event while loading a channel who would + // already have the reaction added to the message + if (existingReaction.users.find((user) => user.id === actor.id)) { + return; } existingReaction.count = existingReaction.count + 1; diff --git a/plugins/chat/spec/system/react_to_message_spec.rb b/plugins/chat/spec/system/react_to_message_spec.rb index 311ead6de1b..9cf1f576d6a 100644 --- a/plugins/chat/spec/system/react_to_message_spec.rb +++ b/plugins/chat/spec/system/react_to_message_spec.rb @@ -140,5 +140,26 @@ RSpec.describe "React to message", type: :system, js: true do context "when mobile", mobile: true do include_examples "inline reactions" end + + context "when receiving a duplicate reaction event" do + fab!(:user_1) { Fabricate(:user) } + + fab!(:reaction_2) do + Chat::ChatMessageReactor.new(user_1, category_channel_1).react!( + message_id: message_1.id, + react_action: :add, + emoji: "heart", + ) + end + + it "doesn’t create duplicate reactions" do + chat.visit_channel(category_channel_1) + + ChatPublisher.publish_reaction!(category_channel_1, message_1, "add", user_1, "heart") + channel.send_message("test") # cheap trick to ensure reaction has been processed + + expect(channel).to have_reaction(message_1, reaction_2, "1") + end + end end end