This commit is contained in:
Matt Jankowski 2026-03-27 09:22:02 +00:00 committed by GitHub
commit 121a1e9490
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 93 additions and 70 deletions

View File

@ -19,13 +19,14 @@
#
class Announcement < ApplicationRecord
include Reactions
scope :unpublished, -> { where(published: false) }
scope :published, -> { where(published: true) }
scope :chronological, -> { order(coalesced_chronology_timestamps.asc) }
scope :reverse_chronological, -> { order(coalesced_chronology_timestamps.desc) }
has_many :announcement_mutes, dependent: :destroy
has_many :announcement_reactions, dependent: :destroy
validates :text, presence: true
validates :starts_at, presence: true, if: :ends_at?
@ -81,46 +82,12 @@ class Announcement < ApplicationRecord
@emojis ||= CustomEmoji.from_text(text)
end
def reactions(account = nil)
grouped_ordered_announcement_reactions.select(
[:name, :custom_emoji_id, 'COUNT(*) as count'].tap do |values|
values << value_for_reaction_me_column(account)
end
).to_a.tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call
end
end
def scope_for_notification
User.confirmed.joins(:account).merge(Account.without_suspended)
end
private
def grouped_ordered_announcement_reactions
announcement_reactions
.group(:announcement_id, :name, :custom_emoji_id)
.order(
Arel.sql('MIN(created_at)').asc
)
end
def value_for_reaction_me_column(account)
if account.nil?
'FALSE AS me'
else
<<~SQL.squish
EXISTS(
SELECT 1
FROM announcement_reactions inner_reactions
WHERE inner_reactions.account_id = #{account.id}
AND inner_reactions.announcement_id = announcement_reactions.announcement_id
AND inner_reactions.name = announcement_reactions.name
) AS me
SQL
end
end
def set_published
return unless scheduled_at.blank? || scheduled_at.past?

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
module Announcement::Reactions
extend ActiveSupport::Concern
included do
has_many :announcement_reactions, dependent: :destroy
end
def reactions(account = nil)
grouped_ordered_announcement_reactions
.select(reaction_columns_for_account(account))
.to_a
.tap { |records| ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call }
end
private
def grouped_ordered_announcement_reactions
announcement_reactions
.group(:announcement_id, :name, :custom_emoji_id)
.order(AnnouncementReaction.arel_table[:created_at].minimum.asc)
end
def reaction_columns_for_account(account)
[:name, :custom_emoji_id, Arel.star.count.as('count')]
.tap { |values| values << reaction_me_column_value(account).as('me') }
end
def reaction_me_column_value(account)
if account.nil?
Arel.sql 'FALSE'
else
Arel.sql(<<~SQL.squish)
EXISTS(
SELECT 1
FROM announcement_reactions inner_reactions
WHERE inner_reactions.account_id = #{account.id}
AND inner_reactions.announcement_id = announcement_reactions.announcement_id
AND inner_reactions.name = announcement_reactions.name
)
SQL
end
end
end

View File

@ -0,0 +1,46 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Announcement::Reactions do
subject { Fabricate.build :announcement }
describe 'Associations' do
it { is_expected.to have_many(:announcement_reactions).dependent(:destroy) }
end
describe '#reactions' do
context 'with announcement_reactions present' do
let(:account_reaction_emoji) { Fabricate :custom_emoji }
let(:other_reaction_emoji) { Fabricate :custom_emoji }
let!(:account) { Fabricate(:account) }
let!(:announcement) { Fabricate(:announcement) }
before do
Fabricate(:announcement_reaction, announcement: announcement, created_at: 10.days.ago, name: other_reaction_emoji.shortcode)
Fabricate(:announcement_reaction, announcement: announcement, created_at: 5.days.ago, account: account, name: account_reaction_emoji.shortcode)
Fabricate(:announcement_reaction) # For some other announcement
end
it 'returns the announcement reactions for the announcement' do
results = announcement.reactions
expect(results).to have_attributes(
size: eq(2),
first: have_attributes(name: other_reaction_emoji.shortcode, me: false),
last: have_attributes(name: account_reaction_emoji.shortcode, me: false)
)
end
it 'returns the announcement reactions for the announcement with `me` set correctly' do
results = announcement.reactions(account)
expect(results).to have_attributes(
size: eq(2),
first: have_attributes(name: other_reaction_emoji.shortcode, me: false),
last: have_attributes(name: account_reaction_emoji.shortcode, me: true)
)
end
end
end
end

View File

@ -118,41 +118,6 @@ RSpec.describe Announcement do
end
end
describe '#reactions' do
context 'with announcement_reactions present' do
let(:account_reaction_emoji) { Fabricate :custom_emoji }
let(:other_reaction_emoji) { Fabricate :custom_emoji }
let!(:account) { Fabricate(:account) }
let!(:announcement) { Fabricate(:announcement) }
before do
Fabricate(:announcement_reaction, announcement: announcement, created_at: 10.days.ago, name: other_reaction_emoji.shortcode)
Fabricate(:announcement_reaction, announcement: announcement, created_at: 5.days.ago, account: account, name: account_reaction_emoji.shortcode)
Fabricate(:announcement_reaction) # For some other announcement
end
it 'returns the announcement reactions for the announcement' do
results = announcement.reactions
expect(results).to have_attributes(
size: eq(2),
first: have_attributes(name: other_reaction_emoji.shortcode, me: false),
last: have_attributes(name: account_reaction_emoji.shortcode, me: false)
)
end
it 'returns the announcement reactions for the announcement with `me` set correctly' do
results = announcement.reactions(account)
expect(results).to have_attributes(
size: eq(2),
first: have_attributes(name: other_reaction_emoji.shortcode, me: false),
last: have_attributes(name: account_reaction_emoji.shortcode, me: true)
)
end
end
end
describe '#statuses' do
let(:announcement) { Fabricate(:announcement, status_ids: status_ids) }