diff --git a/app/controllers/antennas_controller.rb b/app/controllers/antennas_controller.rb index 43d8ee118b..802f17b205 100644 --- a/app/controllers/antennas_controller.rb +++ b/app/controllers/antennas_controller.rb @@ -59,7 +59,7 @@ class AntennasController < ApplicationController end def resource_params - params.require(:antenna).permit(:title, :list, :available, :expires_in, :with_media_only, :ignore_reblog, :keywords_raw, :exclude_keywords_raw, :domains_raw, :exclude_domains_raw, :accounts_raw, :exclude_accounts_raw, :tags_raw, :exclude_tags_raw) + params.require(:antenna).permit(:title, :list, :available, :stl, :expires_in, :with_media_only, :ignore_reblog, :keywords_raw, :exclude_keywords_raw, :domains_raw, :exclude_domains_raw, :accounts_raw, :exclude_accounts_raw, :tags_raw, :exclude_tags_raw) end def thin_resource_params diff --git a/app/models/antenna.rb b/app/models/antenna.rb index c72c157388..c170684124 100644 --- a/app/models/antenna.rb +++ b/app/models/antenna.rb @@ -41,11 +41,10 @@ class Antenna < ApplicationRecord scope :all_accounts, -> { where(any_accounts: true) } scope :all_tags, -> { where(any_tags: true) } scope :availables, -> { where(available: true).where(Arel.sql('any_keywords = FALSE OR any_domains = FALSE OR any_accounts = FALSE OR any_tags = FALSE')) } + scope :available_stls, -> { where(available: true, stl: true) } validate :list_owner - before_save :check_stl_mode - def list_owner raise Mastodon::ValidationError, I18n.t('antennas.errors.invalid_list_owner') if !list_id.zero? && list.present? && list.account != account end @@ -213,16 +212,4 @@ class Antenna < ApplicationRecord end self[:exclude_accounts] = accounts end - - private - - def check_stl_mode - self[:stl] = stl_mode? - end - - def stl_mode? - list_id.zero? && !any_domains && any_accounts && any_keywords && any_tags && - exclude_accounts.blank? && exclude_domains.blank? && exclude_keywords.blank? && exclude_tags.blank? && - antenna_domains.count == 1 && antenna_domains.first.name == Rails.configuration.x.local_domain - end end diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 81b232558a..6f64b61989 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -49,7 +49,8 @@ class FanOutOnWriteService < BaseService when :public, :unlisted, :public_unlisted, :private deliver_to_all_followers! deliver_to_lists! - deliver_to_antennas! if [:public, :public_unlisted].include?(@status.visibility.to_sym) + deliver_to_antennas! if [:public, :public_unlisted].include?(@status.visibility.to_sym) && !@account.dissubscribable + deliver_to_stl_antennas! when :limited deliver_to_mentioned_followers! else @@ -116,11 +117,27 @@ class FanOutOnWriteService < BaseService end end - def deliver_to_antennas! - return if @account.dissubscribable && @status.reblog? + def deliver_to_stl_antennas! + return if @status.reblog? - lists = [] - homes = [] + antennas = Antenna.available_stls + antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)) + antennas = antennas.where(account: @account.followers) unless @account.domain.nil? + + collection = AntennaCollection.new(@status, @options[:update]) + + antennas.in_batches do |ans| + ans.each do |antenna| + next if antenna.expired? + + collection.push(antenna) + end + end + + collection.deliver! + end + + def deliver_to_antennas! tag_ids = @status.tags.pluck(:id) domain = @account.domain || Rails.configuration.x.local_domain @@ -128,7 +145,7 @@ class FanOutOnWriteService < BaseService antennas = antennas.left_joins(:antenna_domains).where(any_domains: true).or(Antenna.left_joins(:antenna_domains).where(antenna_domains: { name: domain })) antennas = antennas.where(with_media_only: false) unless @status.with_media? antennas = antennas.where(ignore_reblog: false) unless @status.reblog? - antennas = antennas.where(stl: true) if @account.dissubscribable + antennas = antennas.where(stl: false) antennas = Antenna.where(id: antennas.select(:id)) antennas = antennas.left_joins(:antenna_accounts).where(any_accounts: true).or(Antenna.left_joins(:antenna_accounts).where(antenna_accounts: { account: @account })) @@ -139,6 +156,8 @@ class FanOutOnWriteService < BaseService antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)) + collection = AntennaCollection.new(@status, @options[:update]) + antennas.in_batches do |ans| ans.each do |antenna| next unless antenna.enabled? @@ -148,27 +167,11 @@ class FanOutOnWriteService < BaseService next if antenna.exclude_domains&.include?(domain) next if antenna.exclude_tags&.any? { |tag_id| tag_ids.include?(tag_id) } - if antenna.list_id.zero? - homes << antenna.account_id - else - lists << antenna.list_id - end - end - end - lists = lists.uniq - homes = homes.uniq - - if lists.any? - FeedInsertWorker.push_bulk(lists) do |list| - [@status.id, list, 'list', { 'update' => update? }] + collection.push(antenna) end end - if homes.any? - FeedInsertWorker.push_bulk(homes) do |home| - [@status.id, home, 'home', { 'update' => update? }] - end - end + collection.deliver! end def deliver_to_mentioned_followers! @@ -238,4 +241,38 @@ class FanOutOnWriteService < BaseService def broadcastable_unlisted? @status.public_unlisted_visibility? && !@status.reblog? && !@account.silenced? end + + class AntennaCollection + def initialize(status, update) + @status = status + @update = update + @home_account_ids = [] + @list_ids = [] + end + + def push(antenna) + if antenna.list_id.zero? + @home_account_ids << antenna.account_id + else + @list_ids << antenna.list_id + end + end + + def deliver! + lists = @list_ids.uniq + homes = @home_account_ids.uniq + + if lists.any? + FeedInsertWorker.push_bulk(lists) do |list| + [@status.id, list, 'list', { 'update' => @update }] + end + end + + if homes.any? + FeedInsertWorker.push_bulk(homes) do |home| + [@status.id, home, 'home', { 'update' => @update }] + end + end + end + end end diff --git a/app/views/antennas/_antenna.html.haml b/app/views/antennas/_antenna.html.haml index c1b6460af4..04c5a8463d 100644 --- a/app/views/antennas/_antenna.html.haml +++ b/app/views/antennas/_antenna.html.haml @@ -2,7 +2,7 @@ = link_to edit_antenna_path(antenna), class: 'filters-list__item__title' do = antenna.title - - if !antenna.enabled_config? + - if !antenna.enabled_config? && !antenna.stl .expiration{ title: t('antennas.index.disabled') } = t('antennas.index.disabled') - elsif antenna.expires? @@ -17,58 +17,58 @@ .filters-list__item__permissions %ul.permissions-list - - unless antenna.antenna_domains.empty? - %li.permissions-list__item - .permissions-list__item__icon - = fa_icon('sitemap') - .permissions-list__item__text - .permissions-list__item__text__title - = t('antennas.index.domains', count: antenna.antenna_domains.size) - .permissions-list__item__text__type - - domains = antenna.antenna_domains.map { |domain| domain.name } - - domains = domains.take(5) + ['…'] if domains.size > 5 # TODO - = domains.join(', ') - - unless antenna.antenna_accounts.empty? - %li.permissions-list__item - .permissions-list__item__icon - = fa_icon('users') - .permissions-list__item__text - .permissions-list__item__text__title - = t('antennas.index.accounts', count: antenna.antenna_accounts.size) - .permissions-list__item__text__type - - accounts = antenna.antenna_accounts.map { |account| account.account.domain ? "@#{account.account.username}@#{account.account.domain}" : "@#{account.account.username}" } - - accounts = accounts.take(5) + ['…'] if accounts.size > 5 # TODO - = accounts.join(', ') - - unless antenna.keywords.nil? || antenna.keywords.empty? - %li.permissions-list__item - .permissions-list__item__icon - = fa_icon('paragraph') - .permissions-list__item__text - .permissions-list__item__text__title - = t('antennas.index.keywords', count: antenna.keywords.size) - .permissions-list__item__text__type - - keywords = antenna.keywords - - keywords = keywords.take(5) + ['…'] if keywords.size > 5 # TODO - = keywords.join(', ') - - unless antenna.antenna_tags.empty? - %li.permissions-list__item - .permissions-list__item__icon - = fa_icon('hashtag') - .permissions-list__item__text - .permissions-list__item__text__title - = t('antennas.index.tags', count: antenna.antenna_tags.size) - .permissions-list__item__text__type - - tags = antenna.antenna_tags.map { |tag| tag.tag.name } - - tags = keywords.take(5) + ['…'] if tags.size > 5 # TODO - = tags.join(', ') + - unless antenna.stl + - unless antenna.antenna_domains.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('sitemap') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.domains', count: antenna.antenna_domains.size) + .permissions-list__item__text__type + - domains = antenna.antenna_domains.map { |domain| domain.name } + - domains = domains.take(5) + ['…'] if domains.size > 5 # TODO + = domains.join(', ') + - unless antenna.antenna_accounts.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('users') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.accounts', count: antenna.antenna_accounts.size) + .permissions-list__item__text__type + - accounts = antenna.antenna_accounts.map { |account| account.account.domain ? "@#{account.account.username}@#{account.account.domain}" : "@#{account.account.username}" } + - accounts = accounts.take(5) + ['…'] if accounts.size > 5 # TODO + = accounts.join(', ') + - unless antenna.keywords.nil? || antenna.keywords.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('paragraph') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.keywords', count: antenna.keywords.size) + .permissions-list__item__text__type + - keywords = antenna.keywords + - keywords = keywords.take(5) + ['…'] if keywords.size > 5 # TODO + = keywords.join(', ') + - unless antenna.antenna_tags.empty? + %li.permissions-list__item + .permissions-list__item__icon + = fa_icon('hashtag') + .permissions-list__item__text + .permissions-list__item__text__title + = t('antennas.index.tags', count: antenna.antenna_tags.size) + .permissions-list__item__text__type + - tags = antenna.antenna_tags.map { |tag| tag.tag.name } + - tags = keywords.take(5) + ['…'] if tags.size > 5 # TODO + = tags.join(', ') .announcements-list__item__action-bar .announcements-list__item__meta - - if antenna.enabled_config_raws? - - if antenna.stl - = t('antennas.index.stl') - - else - = t('antennas.index.contexts', contexts: antenna.context.map { |context| I18n.t("antennas.contexts.#{context}") }.join(', ')) + - if antenna.stl + = t('antennas.index.stl') + - elsif antenna.enabled_config_raws? + = t('antennas.index.contexts', contexts: antenna.context.map { |context| I18n.t("antennas.contexts.#{context}") }.join(', ')) - else = t('antennas.errors.empty_contexts') diff --git a/app/views/antennas/_antenna_fields.html.haml b/app/views/antennas/_antenna_fields.html.haml index fe67b3753e..9a4c9de4e4 100644 --- a/app/views/antennas/_antenna_fields.html.haml +++ b/app/views/antennas/_antenna_fields.html.haml @@ -13,6 +13,9 @@ .fields-group.fields-row__column.fields-row__column-6 = f.input :available, wrapper: :with_label, label: t('antennas.edit.available'), hint: false +.fields-row + = f.input :stl, wrapper: :with_label, label: t('antennas.edit.stl'), hint: t('antennas.edit.stl_hint') + %hr.spacer/ %p.hint= t 'antennas.edit.hint' %hr.spacer/ @@ -20,7 +23,7 @@ %h4= t('antennas.contexts.domain') %p.hint= t 'antennas.edit.domains_hint' -.fields-row +.fields-row .fields-row__column.fields-row__column-6.fields-group = f.input :domains_raw, wrapper: :with_label, as: :text, input_html: { rows: 5 }, label: t('antennas.edit.domains_raw') .fields-row__column.fields-row__column-6.fields-group diff --git a/config/locales/en.yml b/config/locales/en.yml index 9b055afca4..ddce773927 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1019,6 +1019,8 @@ en: keywords_hint: キーワードは1つあたり最低2文字です。キーワードによる絞り込みを指定した場合、検索許可に対応しているサーバーからの投稿は、検索許可が「公開」以外のものは掲載されなくなります keywords_raw: Keyword list list: Destination list + stl: Enable stl (Social timeline) mode + stl_hint: All of under settings will be ignored, but rejecting subscription settings are ignored tags_raw: Hashtag list title: Edit antenna with_media_only: With media only diff --git a/config/locales/ja.yml b/config/locales/ja.yml index fb3f1dd07c..63c0d40aa5 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1016,6 +1016,8 @@ ja: keywords_hint: キーワードは1つあたり最低2文字です。キーワードによる絞り込みを指定した場合、検索許可に対応しているサーバーからの投稿は、検索許可が「公開」以外のものは掲載されなくなります keywords_raw: 絞り込むキーワード list: 投稿配置先リスト + stl: STL(ソーシャルタイムライン)モードを有効にする + stl_hint: STLモードが有効になったアンテナは、全てのフォロワーとローカルタイムラインの発言を流します。STLが有効になっていると、これより下の設定が例外なく全て何もかも無視されます。その代わり購読拒否設定は無視されます。 tags_raw: 絞り込むハッシュタグ title: アンテナを編集 with_media_only: メディアのみ