# frozen_string_literal: true class Chat::ChatMessageReactor ADD_REACTION = :add REMOVE_REACTION = :remove MAX_REACTIONS_LIMIT = 30 def initialize(user, chat_channel) @user = user @chat_channel = chat_channel @guardian = Guardian.new(user) end def react!(message_id:, react_action:, emoji:) @guardian.ensure_can_join_chat_channel!(@chat_channel) @guardian.ensure_can_react! validate_channel_status! validate_reaction!(react_action, emoji) message = ensure_chat_message!(message_id) validate_max_reactions!(message, react_action, emoji) reaction = nil ActiveRecord::Base.transaction do enforce_channel_membership! reaction = create_reaction(message, react_action, emoji) end publish_reaction(message, react_action, emoji) reaction end private def ensure_chat_message!(message_id) message = ChatMessage.find_by(id: message_id, chat_channel: @chat_channel) raise Discourse::NotFound unless message message end def validate_reaction!(react_action, emoji) if ![ADD_REACTION, REMOVE_REACTION].include?(react_action) || !Emoji.exists?(emoji) raise Discourse::InvalidParameters end end def enforce_channel_membership! Chat::ChatChannelMembershipManager.new(@chat_channel).follow(@user) end def validate_channel_status! return if @guardian.can_create_channel_message?(@chat_channel) raise Discourse::InvalidAccess.new( nil, nil, custom_message: "chat.errors.channel_modify_message_disallowed", custom_message_params: { status: @chat_channel.status_name, }, ) end def validate_max_reactions!(message, react_action, emoji) if react_action == ADD_REACTION && message.reactions.count("DISTINCT emoji") >= MAX_REACTIONS_LIMIT && !message.reactions.exists?(emoji: emoji) raise Discourse::InvalidAccess.new( nil, nil, custom_message: "chat.errors.max_reactions_limit_reached", ) end end def create_reaction(message, react_action, emoji) if react_action == ADD_REACTION message.reactions.find_or_create_by!(user: @user, emoji: emoji) else message.reactions.where(user: @user, emoji: emoji).destroy_all end end def publish_reaction(message, react_action, emoji) ChatPublisher.publish_reaction!(@chat_channel, message, react_action, @user, emoji) end end