diff --git a/app/controllers/api/v1/statuses/reactions_controller.rb b/app/controllers/api/v1/statuses/reactions_controller.rb index d8ebaafd9ffbb8..70ef378944a099 100644 --- a/app/controllers/api/v1/statuses/reactions_controller.rb +++ b/app/controllers/api/v1/statuses/reactions_controller.rb @@ -22,9 +22,14 @@ def create def destroy react = current_account.status_reactions.find_by(status_id: params[:status_id], name: params[:id]) + reactions = StatusReaction.select( + [:name, :custom_emoji_id, 'COUNT(*) as count', 'FALSE AS me'] + ).where(status_id: @status.id) + if react @status = react.status count = [@status.reactions_count - 1, 0].max + reactions = reactions.where.not(id: react.id) UnreactWorker.perform_async(current_account.id, @status.id, params[:id]) else @status = Status.find(params[:status_id]) @@ -32,7 +37,9 @@ def destroy authorize @status, :show? end - relationships = StatusRelationshipsPresenter.new([@status], current_account.id, reactions_map: { @status.id => false }, attributes_map: { @status.id => { reactions_count: count } }) + reactions = reactions.group(:status_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at)').asc).to_a + + relationships = StatusRelationshipsPresenter.new([@status], current_account.id, reactions_map: { @status.id => reactions }, attributes_map: { @status.id => { reactions_count: count } }) render json: @status, serializer: REST::StatusSerializer, relationships: relationships rescue Mastodon::NotPermittedError not_found @@ -43,7 +50,7 @@ def destroy def set_reactions @reactions = ordered_reactions.select( [:id, :account_id, :name, :custom_emoji_id].tap do |values| - values << value_for_reaction_me_column(current_account) + values << Status.value_for_reaction_me_column(current_account.id) end ).to_a_paginated_by_id( limit_param(REACTIONS_LIMIT), @@ -68,27 +75,6 @@ def filtered? params[:emoji].present? end - def value_for_reaction_me_column(account) - if account.nil? - 'FALSE AS me' - else - <<~SQL.squish - EXISTS( - SELECT 1 - FROM status_reactions inner_reactions - WHERE inner_reactions.account_id = #{account.id} - AND inner_reactions.status_id = status_reactions.status_id - AND inner_reactions.name = status_reactions.name - AND ( - inner_reactions.custom_emoji_id = status_reactions.custom_emoji_id - OR inner_reactions.custom_emoji_id IS NULL - AND status_reactions.custom_emoji_id IS NULL - ) - ) AS me - SQL - end - end - def insert_pagination_headers set_pagination_headers(next_path, prev_path) end diff --git a/app/models/status.rb b/app/models/status.rb index 0d5db9d8f782aa..852517085285a6 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -489,16 +489,6 @@ def unlink_from_conversations! end end - private - - def grouped_ordered_status_reactions - status_reactions - .group(:status_id, :name, :custom_emoji_id) - .order( - Arel.sql('MIN(created_at)').asc - ) - end - def value_for_reaction_me_column(account_id) if account_id.nil? 'FALSE AS me' @@ -520,6 +510,16 @@ def value_for_reaction_me_column(account_id) end end + private + + def grouped_ordered_status_reactions + status_reactions + .group(:status_id, :name, :custom_emoji_id) + .order( + Arel.sql('MIN(created_at)').asc + ) + end + def update_status_stat!(attrs) return if marked_for_destruction? || destroyed? diff --git a/app/presenters/status_relationships_presenter.rb b/app/presenters/status_relationships_presenter.rb index 5d53040fb25407..ea5937ad3a0f4e 100644 --- a/app/presenters/status_relationships_presenter.rb +++ b/app/presenters/status_relationships_presenter.rb @@ -3,13 +3,14 @@ class StatusRelationshipsPresenter PINNABLE_VISIBILITIES = %w(public unlisted private).freeze - attr_reader :reblogs_map, :favourites_map, :mutes_map, :pins_map, - :bookmarks_map, :filters_map, :attributes_map + attr_reader :reblogs_map, :favourites_map, :reactions_map, :mutes_map, + :pins_map, :bookmarks_map, :filters_map, :attributes_map def initialize(statuses, current_account_id = nil, **options) if current_account_id.nil? @reblogs_map = {} @favourites_map = {} + @reactions_map = {} @bookmarks_map = {} @mutes_map = {} @pins_map = {} @@ -23,6 +24,7 @@ def initialize(statuses, current_account_id = nil, **options) @filters_map = build_filters_map(statuses, current_account_id).merge(options[:filters_map] || {}) @reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(options[:reblogs_map] || {}) @favourites_map = Status.favourites_map(status_ids, current_account_id).merge(options[:favourites_map] || {}) + @reactions_map = build_reactions_map(status_ids, current_account_id).merge(options[:reactions_map] || {}) @bookmarks_map = Status.bookmarks_map(status_ids, current_account_id).merge(options[:bookmarks_map] || {}) @mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(options[:mutes_map] || {}) @pins_map = Status.pins_map(pinnable_status_ids, current_account_id).merge(options[:pins_map] || {}) @@ -44,4 +46,38 @@ def build_filters_map(statuses, current_account_id) end end end + + def build_reactions_map(status_ids, current_account_id) + reactions = StatusReaction.select( + [:status_id, :name, :custom_emoji_id, 'COUNT(*) as count'].tap do |values| + values << value_for_reaction_me_column(current_account_id) + end + ).where(status_id: status_ids).where(account_id: current_account_id).group(:status_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at)').asc).to_a + + reactions.each_with_object({}) do |r, h| + h[r.status_id] = [] if h[r.status_id].nil? + h[r.status_id].push(r.attributes.except(:status_id)) + end + end + + def value_for_reaction_me_column(account_id) + if account_id.nil? + 'FALSE AS me' + else + <<~SQL.squish + EXISTS( + SELECT 1 + FROM status_reactions inner_reactions + WHERE inner_reactions.account_id = #{account_id} + AND inner_reactions.status_id = status_reactions.status_id + AND inner_reactions.name = status_reactions.name + AND ( + inner_reactions.custom_emoji_id = status_reactions.custom_emoji_id + OR inner_reactions.custom_emoji_id IS NULL + AND status_reactions.custom_emoji_id IS NULL + ) + ) AS me + SQL + end + end end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 33d897a60bb9f9..c064035f9fb9ac 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -164,7 +164,11 @@ def ordered_mentions end def reactions - object.reactions(current_user&.account&.id) + if relationships + relationships.reactions_map[object.id] || [] + else + object.reactions(current_user&.account&.id) + end end private