Add quotes_count to statuses stats (#35832)

This commit is contained in:
Claire 2025-08-25 14:21:28 +02:00 committed by GitHub
parent 94ad088482
commit 2560242972
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 94 additions and 9 deletions

View File

@ -96,6 +96,7 @@ export interface ApiStatusJSON {
replies_count: number;
reblogs_count: number;
favorites_count: number;
quotes_count: number;
edited_at?: string;
favorited?: boolean;

View File

@ -160,7 +160,12 @@ export const StatusReblogButton: FC<ReblogButtonProps> = ({
)}
icon='retweet'
iconComponent={iconComponent}
counter={counters ? (status.get('reblogs_count') as number) : undefined}
counter={
counters
? (status.get('reblogs_count') as number) +
(status.get('quotes_count') as number)
: undefined
}
active={isReblogged}
/>
</Dropdown>
@ -283,7 +288,12 @@ export const LegacyReblogButton: FC<ReblogButtonProps> = ({
icon='retweet'
iconComponent={iconComponent}
onClick={!disabled ? handleClick : undefined}
counter={counters ? (status.get('reblogs_count') as number) : undefined}
counter={
counters
? (status.get('reblogs_count') as number) +
(status.get('quotes_count') as number)
: undefined
}
/>
);
};

View File

@ -233,7 +233,10 @@ export const Footer: React.FC<{
icon='retweet'
iconComponent={reblogIconComponent}
onClick={handleReblogClick}
counter={status.get('reblogs_count') as number}
counter={
(status.get('reblogs_count') as number) +
(status.get('quotes_count') as number)
}
/>
<IconButton

View File

@ -127,6 +127,7 @@ export const DetailedStatus: React.FC<{
let media;
let applicationLink;
let reblogLink;
let quotesLink;
let attachmentAspectRatio;
if (properStatus.get('media_attachments').getIn([0, 'type']) === 'video') {
@ -279,6 +280,23 @@ export const DetailedStatus: React.FC<{
);
}
if (['private', 'direct'].includes(status.get('visibility') as string)) {
quotesLink = '';
} else {
quotesLink = (
<span className='detailed-status__link'>
<span className='detailed-status__quotes'>
<AnimatedNumber value={status.get('quotes_count')} />
</span>
<FormattedMessage
id='status.quotes'
defaultMessage='{count, plural, one {quote} other {quotes}}'
values={{ count: status.get('quotes_count') }}
/>
</span>
);
}
const favouriteLink = (
<Link
to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/favourites`}
@ -420,6 +438,8 @@ export const DetailedStatus: React.FC<{
<div className='detailed-status__meta__line'>
{reblogLink}
{reblogLink && <>·</>}
{quotesLink}
{quotesLink && <>·</>}
{favouriteLink}
</div>
</div>

View File

@ -900,6 +900,7 @@
"status.quote_policy_change": "Change who can quote",
"status.quote_post_author": "Quoted a post by @{name}",
"status.quote_private": "Private posts cannot be quoted",
"status.quotes": "{count, plural, one {quote} other {quotes}}",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost with original visibility",

View File

@ -68,6 +68,7 @@ export const statusFactory: FactoryFunction<ApiStatusJSON> = ({
url: 'https://example.com/status/1',
replies_count: 0,
reblogs_count: 0,
quotes_count: 0,
favorites_count: 0,
account: accountFactory(),
media_attachments: [],

View File

@ -40,6 +40,10 @@ class Quote < ApplicationRecord
validates :approval_uri, absence: true, if: -> { quoted_account&.local? }
validate :validate_visibility
after_create_commit :increment_counter_caches!
after_destroy_commit :decrement_counter_caches!
after_update_commit :update_counter_caches!
def accept!
update!(state: :accepted)
end
@ -84,4 +88,27 @@ class Quote < ApplicationRecord
def set_activity_uri
self.activity_uri = [ActivityPub::TagManager.instance.uri_for(account), '/quote_requests/', SecureRandom.uuid].join
end
def increment_counter_caches!
return unless acceptable?
quoted_status&.increment_count!(:quotes_count)
end
def decrement_counter_caches!
return unless acceptable?
quoted_status&.decrement_count!(:quotes_count)
end
def update_counter_caches!
return if legacy? || !state_previously_changed?
if acceptable?
quoted_status&.increment_count!(:quotes_count)
else
# TODO: are there cases where this would not be correct?
quoted_status&.decrement_count!(:quotes_count)
end
end
end

View File

@ -301,6 +301,10 @@ class Status < ApplicationRecord
status_stat&.favourites_count || 0
end
def quotes_count
status_stat&.quotes_count || 0
end
# Reblogs count received from an external instance
def untrusted_reblogs_count
status_stat&.untrusted_reblogs_count unless local?

View File

@ -5,14 +5,15 @@
# Table name: status_stats
#
# id :bigint(8) not null, primary key
# status_id :bigint(8) not null
# replies_count :bigint(8) default(0), not null
# reblogs_count :bigint(8) default(0), not null
# favourites_count :bigint(8) default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
# quotes_count :bigint(8) default(0), not null
# reblogs_count :bigint(8) default(0), not null
# replies_count :bigint(8) default(0), not null
# untrusted_favourites_count :bigint(8)
# untrusted_reblogs_count :bigint(8)
# created_at :datetime not null
# updated_at :datetime not null
# status_id :bigint(8) not null
#
class StatusStat < ApplicationRecord
@ -34,6 +35,10 @@ class StatusStat < ApplicationRecord
[attributes['favourites_count'], 0].max
end
def quotes_count
[attributes['quotes_count'], 0].max
end
private
def clamp_untrusted_counts

View File

@ -99,6 +99,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
object.untrusted_favourites_count || relationships&.attributes_map&.dig(object.id, :favourites_count) || object.favourites_count
end
def quotes_count
relationships&.attributes_map&.dig(object.id, :quotes_count) || object.quotes_count
end
def favourited
if relationships
relationships.favourites_map[object.id] || false

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddQuotesCountToStatusStat < ActiveRecord::Migration[8.0]
def change
add_column :status_stats, :quotes_count, :bigint, null: false, default: 0
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_08_19_100545) do
ActiveRecord::Schema[8.0].define(version: 2025_08_20_084312) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -1103,6 +1103,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_19_100545) do
t.datetime "updated_at", precision: nil, null: false
t.bigint "untrusted_favourites_count"
t.bigint "untrusted_reblogs_count"
t.bigint "quotes_count", default: 0, null: false
t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true
end

View File

@ -63,6 +63,7 @@ module Mastodon::CLI
status_stat.replies_count = status.replies.not_direct_visibility.count
status_stat.reblogs_count = status.reblogs.count
status_stat.favourites_count = status.favourites.count
status_stat.quotes_count = status.quotes.accepted.count
status_stat.save if status_stat.changed?
end