diff --git a/app/models/status.rb b/app/models/status.rb index e7ea191a80..dd9d2ccc6c 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -62,6 +62,7 @@ class Status < ApplicationRecord belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true has_many :favourites, inverse_of: :status, dependent: :destroy + has_many :emoji_reactions, inverse_of: :status, dependent: :destroy has_many :bookmarks, inverse_of: :status, dependent: :destroy has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy has_many :reblogged_by_accounts, through: :reblogs, class_name: 'Account', source: :account @@ -288,6 +289,29 @@ class Status < ApplicationRecord update_status_stat!(key => [public_send(key) - 1, 0].max) end + def emoji_reactions_grouped_by_name(account = nil) + (Oj.load(status_stat&.emoji_reactions || '', mode: :strict) || []).tap do |emoji_reactions| + if account.present? + emoji_reactions.each do |emoji_reaction| + emoji_reaction['me'] = emoji_reaction['account_ids'].include?(account.id.to_s) + emoji_reaction['count'] = emoji_reaction['account_ids'].size + emoji_reaction['account_ids'] -= account.excluded_from_timeline_account_ids.map(&:to_s) + end + end + end + end + + def generate_emoji_reactions_grouped_by_name + records = emoji_reactions.group(:name).order(Arel.sql('MIN(created_at) ASC')).select('name, min(custom_emoji_id) as custom_emoji_id, count(*) as count, array_agg(account_id::text order by created_at) as account_ids') + Oj.dump(ActiveModelSerializers::SerializableResource.new(records, each_serializer: REST::EmojiReactionsGroupedByNameSerializer, scope: nil, scope_name: :current_user)) + end + + def refresh_emoji_reactions_grouped_by_name! + generate_emoji_reactions_grouped_by_name.tap do |emoji_reactions| + update_status_stat!(emoji_reactions: emoji_reactions) + end + end + def trendable? if attributes['trendable'].nil? account.trendable? diff --git a/app/models/status_stat.rb b/app/models/status_stat.rb index d101cc1789..022c99e2f4 100644 --- a/app/models/status_stat.rb +++ b/app/models/status_stat.rb @@ -9,6 +9,7 @@ # replies_count :bigint(8) default(0), not null # reblogs_count :bigint(8) default(0), not null # favourites_count :bigint(8) default(0), not null +# emoji_reactions :string # created_at :datetime not null # updated_at :datetime not null # @@ -30,6 +31,10 @@ class StatusStat < ApplicationRecord [attributes['favourites_count'], 0].max end + def emoji_reactions + attributes['emoji_reactions'] || '' + end + private def reset_parent_cache diff --git a/app/serializers/rest/emoji_reactions_grouped_by_name_serializer.rb b/app/serializers/rest/emoji_reactions_grouped_by_name_serializer.rb new file mode 100644 index 0000000000..ac8054179a --- /dev/null +++ b/app/serializers/rest/emoji_reactions_grouped_by_name_serializer.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# name: string, +#count: number, +#account_ids: Array, +#me: boolean, +#url: string, +#domain: string + +class REST::EmojiReactionsGroupedByNameSerializer < ActiveModel::Serializer + attributes :name, :count + + attribute :me, if: :current_user? + attribute :url, if: :custom_emoji? + attribute :static_url, if: :custom_emoji? + attribute :domain, if: :custom_emoji? + attribute :account_ids, if: :has_account_ids? + + def current_user? + !current_user.nil? + end + + def custom_emoji? + object.respond_to?(:custom_emoji) + end + + def has_account_ids? + object.respond_to?(:account_ids) + end +end + \ No newline at end of file diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index e0b8f32a68..12e76fa285 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -6,7 +6,7 @@ class REST::StatusSerializer < ActiveModel::Serializer attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id, :sensitive, :spoiler_text, :visibility, :language, :uri, :url, :replies_count, :reblogs_count, - :favourites_count, :edited_at + :favourites_count, :emoji_reactions, :edited_at attribute :favourited, if: :current_user? attribute :reblogged, if: :current_user? @@ -89,6 +89,10 @@ class REST::StatusSerializer < ActiveModel::Serializer end end + def emoji_reactions + object.emoji_reactions_grouped_by_name(current_user&.account) + end + def reblogged if instance_options && instance_options[:relationships] instance_options[:relationships].reblogs_map[object.id] || false diff --git a/db/migrate/20230223102416_add_emoji_reactions_to_status_stats.rb b/db/migrate/20230223102416_add_emoji_reactions_to_status_stats.rb new file mode 100644 index 0000000000..7f2ec68cc6 --- /dev/null +++ b/db/migrate/20230223102416_add_emoji_reactions_to_status_stats.rb @@ -0,0 +1,5 @@ +class AddEmojiReactionsToStatusStats < ActiveRecord::Migration[6.1] + def change + add_column :status_stats, :emoji_reactions, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 64fa61c855..6804c3af2e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_02_22_235218) do +ActiveRecord::Schema.define(version: 2023_02_23_102416) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -914,6 +914,7 @@ ActiveRecord::Schema.define(version: 2023_02_22_235218) do t.bigint "favourites_count", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "emoji_reactions" t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true end diff --git a/lib/mastodon/cache_cli.rb b/lib/mastodon/cache_cli.rb index 803404c34f..71da961799 100644 --- a/lib/mastodon/cache_cli.rb +++ b/lib/mastodon/cache_cli.rb @@ -45,6 +45,7 @@ module Mastodon status_stat.replies_count = status.replies.where.not(visibility: :direct).count status_stat.reblogs_count = status.reblogs.count status_stat.favourites_count = status.favourites.count + status_stat.emoji_reactions = status.generate_emoji_reactions_grouped_by_name status_stat.save if status_stat.changed? end