Skip to content

Commit

Permalink
Add ltl mode support
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Sep 11, 2023
1 parent 7656687 commit d1ae53e
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 27 deletions.
2 changes: 1 addition & 1 deletion app/controllers/api/v1/antennas_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ def set_antenna
end

def antenna_params
params.permit(:title, :list_id, :insert_feeds, :stl, :with_media_only, :ignore_reblog)
params.permit(:title, :list_id, :insert_feeds, :stl, :ltl, :with_media_only, :ignore_reblog)
end
end
4 changes: 2 additions & 2 deletions app/javascript/mastodon/actions/antennas.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,10 @@ export const createAntennaFail = error => ({
error,
});

export const updateAntenna = (id, title, shouldReset, list_id, stl, with_media_only, ignore_reblog, insert_feeds) => (dispatch, getState) => {
export const updateAntenna = (id, title, shouldReset, list_id, stl, ltl, with_media_only, ignore_reblog, insert_feeds) => (dispatch, getState) => {
dispatch(updateAntennaRequest(id));

api(getState).put(`/api/v1/antennas/${id}`, { title, list_id, stl, with_media_only, ignore_reblog, insert_feeds }).then(({ data }) => {
api(getState).put(`/api/v1/antennas/${id}`, { title, list_id, stl, ltl, with_media_only, ignore_reblog, insert_feeds }).then(({ data }) => {
dispatch(updateAntennaSuccess(data));

if (shouldReset) {
Expand Down
50 changes: 37 additions & 13 deletions app/javascript/mastodon/features/antenna_setting/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,31 +198,37 @@ class AntennaSetting extends PureComponent {
onStlToggle = ({ target }) => {
const { dispatch } = this.props;
const { id } = this.props.params;
dispatch(updateAntenna(id, undefined, false, undefined, target.checked, undefined, undefined, undefined));
dispatch(updateAntenna(id, undefined, false, undefined, target.checked, undefined, undefined, undefined, undefined));
};

onLtlToggle = ({ target }) => {
const { dispatch } = this.props;
const { id } = this.props.params;
dispatch(updateAntenna(id, undefined, false, undefined, undefined, target.checked, undefined, undefined, undefined));
};

onMediaOnlyToggle = ({ target }) => {
const { dispatch } = this.props;
const { id } = this.props.params;
dispatch(updateAntenna(id, undefined, false, undefined, undefined, target.checked, undefined, undefined));
dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, target.checked, undefined, undefined));
};

onIgnoreReblogToggle = ({ target }) => {
const { dispatch } = this.props;
const { id } = this.props.params;
dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, target.checked, undefined));
dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, undefined, target.checked, undefined));
};

onNoInsertFeedsToggle = ({ target }) => {
const { dispatch } = this.props;
const { id } = this.props.params;
dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, undefined, target.checked));
dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, undefined, undefined, target.checked));
};

onSelect = value => {
const { dispatch } = this.props;
const { id } = this.props.params;
dispatch(updateAntenna(id, undefined, false, value.value, undefined, undefined, undefined, undefined));
dispatch(updateAntenna(id, undefined, false, value.value, undefined, undefined, undefined, undefined, undefined));
};

onHomeSelect = () => this.onSelect({ value: '0' });
Expand Down Expand Up @@ -293,6 +299,7 @@ class AntennaSetting extends PureComponent {
const pinned = !!columnId;
const title = antenna ? antenna.get('title') : id;
const isStl = antenna ? antenna.get('stl') : undefined;
const isLtl = antenna ? antenna.get('ltl') : undefined;
const isMediaOnly = antenna ? antenna.get('with_media_only') : undefined;
const isIgnoreReblog = antenna ? antenna.get('ignore_reblog') : undefined;
const isInsertFeeds = antenna ? antenna.get('insert_feeds') : undefined;
Expand All @@ -312,7 +319,7 @@ class AntennaSetting extends PureComponent {
}

let columnSettings;
if (!isStl) {
if (!isStl && !isLtl) {
columnSettings = (
<>
<div className='setting-toggle'>
Expand All @@ -339,6 +346,12 @@ class AntennaSetting extends PureComponent {
<p><FormattedMessage id='antennas.in_stl_mode' defaultMessage='This antenna is in STL mode.' /></p>
</div>
);
} else if (isLtl) {
stlAlert = (
<div className='antenna-setting'>
<p><FormattedMessage id='antennas.in_ltl_mode' defaultMessage='This antenna is in LTL mode.' /></p>
</div>
);
}

const rangeRadioValues = ImmutableList([
Expand Down Expand Up @@ -384,12 +397,23 @@ class AntennaSetting extends PureComponent {
</button>
</div>

<div className='setting-toggle'>
<Toggle id={`antenna-${id}-stl`} defaultChecked={isStl} onChange={this.onStlToggle} />
<label htmlFor={`antenna-${id}-stl`} className='setting-toggle__label'>
<FormattedMessage id='antennas.stl' defaultMessage='STL mode' />
</label>
</div>
{!isLtl && (
<div className='setting-toggle'>
<Toggle id={`antenna-${id}-stl`} defaultChecked={isStl} onChange={this.onStlToggle} />
<label htmlFor={`antenna-${id}-stl`} className='setting-toggle__label'>
<FormattedMessage id='antennas.stl' defaultMessage='STL mode' />
</label>
</div>
)}

{!isStl && (
<div className='setting-toggle'>
<Toggle id={`antenna-${id}-ltl`} defaultChecked={isLtl} onChange={this.onLtlToggle} />
<label htmlFor={`antenna-${id}-ltl`} className='setting-toggle__label'>
<FormattedMessage id='antennas.ltl' defaultMessage='LTL mode' />
</label>
</div>
)}

<div className='setting-toggle'>
<Toggle id={`antenna-${id}-noinsertfeeds`} defaultChecked={isInsertFeeds} onChange={this.onNoInsertFeedsToggle} />
Expand Down Expand Up @@ -429,7 +453,7 @@ class AntennaSetting extends PureComponent {
</>
)}

{!isStl && (
{!isStl && !isLtl && (
<>
<h2><FormattedMessage id='antennas.filter' defaultMessage='Filter' /></h2>
<RadioPanel values={rangeRadioValues} value={rangeRadioValue} onChange={this.onRangeRadioChanged} />
Expand Down
22 changes: 21 additions & 1 deletion app/models/antenna.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
# stl :boolean default(FALSE), not null
# ignore_reblog :boolean default(FALSE), not null
# insert_feeds :boolean default(FALSE), not null
# ltl :boolean default(FALSE), not null
#
class Antenna < ApplicationRecord
include Expireable
Expand All @@ -45,16 +46,19 @@ class Antenna < ApplicationRecord
belongs_to :list, optional: true

scope :stls, -> { where(stl: true) }
scope :ltls, -> { where(ltl: true) }
scope :all_keywords, -> { where(any_keywords: true) }
scope :all_domains, -> { where(any_domains: true) }
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) }
scope :available_ltls, -> { where(available: true, stl: false, ltl: true) }

validate :list_owner
validate :validate_limit
validate :validate_stl_limit
validate :validate_ltl_limit

def list_owner
raise Mastodon::ValidationError, I18n.t('antennas.errors.invalid_list_owner') if !list_id.zero? && list.present? && list.account != account
Expand Down Expand Up @@ -235,6 +239,22 @@ def validate_stl_limit

stls = account.antennas.where(stl: true).where.not(id: id)

errors.add(:base, I18n.t('antennas.errors.over_stl_limit', limit: 1)) if list_id.zero? ? stls.any? { |tl| tl.list_id.zero? } : stls.any? { |tl| tl.list_id != 0 }
errors.add(:base, I18n.t('antennas.errors.over_stl_limit', limit: 1)) if if insert_feeds
list_id.zero? ? stls.any? { |tl| tl.list_id.zero? } : stls.any? { |tl| tl.list_id != 0 }
else
stls.any? { |tl| !tl.insert_feeds }
end
end

def validate_ltl_limit
return unless ltl

ltls = account.antennas.where(ltl: true).where.not(id: id)

errors.add(:base, I18n.t('antennas.errors.over_ltl_limit', limit: 1)) if if insert_feeds
list_id.zero? ? ltls.any? { |tl| tl.list_id.zero? } : ltls.any? { |tl| tl.list_id != 0 }
else
ltls.any? { |tl| !tl.insert_feeds }
end
end
end
2 changes: 1 addition & 1 deletion app/serializers/rest/antenna_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class REST::AntennaSerializer < ActiveModel::Serializer
attributes :id, :title, :stl, :insert_feeds, :with_media_only, :ignore_reblog, :accounts_count, :domains_count, :tags_count, :keywords_count
attributes :id, :title, :stl, :ltl, :insert_feeds, :with_media_only, :ignore_reblog, :accounts_count, :domains_count, :tags_count, :keywords_count

class ListSerializer < ActiveModel::Serializer
attributes :id, :title
Expand Down
36 changes: 30 additions & 6 deletions app/services/delivery_antenna_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
class DeliveryAntennaService
include FormattingHelper

def call(status, update, stl_home)
def call(status, update, **options)
@status = status
@account = @status.account
@update = update

if stl_home
delivery_stl!
else
mode = options[:mode] || :home
case mode
when :home
delivery!
when :stl
delivery_stl!
when :ltl
delivery_ltl!
end
end

Expand Down Expand Up @@ -44,7 +48,7 @@ def delivery!
antennas = antennas.where(account: @status.mentioned_accounts) if @status.visibility.to_sym == :limited
antennas = antennas.where(with_media_only: false) unless @status.with_media?
antennas = antennas.where(ignore_reblog: false) if @status.reblog?
antennas = antennas.where(stl: false)
antennas = antennas.where(stl: false, ltl: false)

collection = AntennaCollection.new(@status, @update, false)
content = extract_status_plain_text_with_spoiler_text(@status)
Expand Down Expand Up @@ -72,7 +76,7 @@ def delivery_stl!
antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago))

home_post = !@account.domain.nil? || @status.reblog? || [:public, :public_unlisted, :login].exclude?(@status.visibility.to_sym)
antennas = antennas.where(account: @account.followers).or(antennas.where(account: @account)).where.not(list_id: 0) if home_post
antennas = antennas.where(account: @account.followers).or(antennas.where(account: @account)).where('insert_feeds IS FALSE OR list_id > 0') if home_post

collection = AntennaCollection.new(@status, @update, home_post)

Expand All @@ -87,6 +91,26 @@ def delivery_stl!
collection.deliver!
end

def delivery_ltl!
return if %i(public public_unlisted login).exclude?(@status.visibility.to_sym)
return unless @account.local?

antennas = Antenna.available_ltls
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, @update, false)

antennas.in_batches do |ans|
ans.each do |antenna|
next if antenna.expired?

collection.push(antenna)
end
end

collection.deliver!
end

class AntennaCollection
def initialize(status, update, stl_home = false) # rubocop:disable Style/OptionalBooleanParameter
@status = status
Expand Down
9 changes: 7 additions & 2 deletions app/services/fan_out_on_write_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def fan_out_to_local_recipients!
deliver_to_lists!
deliver_to_antennas! if !@account.dissubscribable || (@status.dtl? && @account.user&.setting_dtl_force_subscribable && @status.tags.exists?(name: 'kmyblue'))
deliver_to_stl_antennas!
deliver_to_ltl_antennas!
when :limited
deliver_to_lists_mentioned_accounts_only!
deliver_to_antennas! unless @account.dissubscribable
Expand Down Expand Up @@ -135,11 +136,15 @@ def deliver_to_lists_mentioned_accounts_only!
end

def deliver_to_stl_antennas!
DeliveryAntennaService.new.call(@status, @options[:update], true)
DeliveryAntennaService.new.call(@status, @options[:update], mode: :stl)
end

def deliver_to_ltl_antennas!
DeliveryAntennaService.new.call(@status, @options[:update], mode: :ltl)
end

def deliver_to_antennas!
DeliveryAntennaService.new.call(@status, @options[:update], false)
DeliveryAntennaService.new.call(@status, @options[:update], mode: :home)
end

def deliver_to_mentioned_followers!
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,7 @@ en:
invalid_context: None or invalid context supplied
invalid_list_owner: This list is not yours
over_limit: You have exceeded the limit of %{limit} antennas
over_ltl_limit: You have exceeded the limit of %{limit} ltl antennas
over_stl_limit: You have exceeded the limit of %{limit} stl antennas
index:
contexts: Antennas in %{contexts}
Expand Down
1 change: 1 addition & 0 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,7 @@ ja:
keywords: 登録できるキーワード数の上限に達しています
tags: 登録できるタグ数の上限に達しています
over_limit: 所持できるアンテナ数 %{limit}を超えています
over_ltl_limit: 所持できるLTLモード付きアンテナ数 (ホーム/リストそれぞれにつき%{limit}) を超えています
over_stl_limit: 所持できるSTLモード付きアンテナ数 (ホーム/リストそれぞれにつき%{limit}) を超えています
too_short_keyword: キーワードが短すぎます
edit:
Expand Down
15 changes: 15 additions & 0 deletions db/migrate/20230911022527_add_ltl_to_antennas.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

require Rails.root.join('lib', 'mastodon', 'migration_helpers')

class AddLtlToAntennas < ActiveRecord::Migration[7.0]
include Mastodon::MigrationHelpers

disable_ddl_transaction!

def change
safety_assured do
add_column_with_default :antennas, :ltl, :boolean, default: false, allow_null: false
end
end
end
13 changes: 12 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2023_09_07_150100) do
ActiveRecord::Schema[7.0].define(version: 2023_09_11_022527) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -50,6 +50,15 @@
t.index ["account_id", "domain"], name: "index_account_domain_blocks_on_account_id_and_domain", unique: true
end

create_table "account_groups", force: :cascade do |t|
t.bigint "account_id", null: false
t.bigint "group_account_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_account_groups_on_account_id"
t.index ["group_account_id"], name: "index_account_groups_on_group_account_id"
end

create_table "account_migrations", force: :cascade do |t|
t.bigint "account_id"
t.string "acct", default: "", null: false
Expand Down Expand Up @@ -313,6 +322,7 @@
t.boolean "stl", default: false, null: false
t.boolean "ignore_reblog", default: false, null: false
t.boolean "insert_feeds", default: false, null: false
t.boolean "ltl", default: false, null: false
t.index ["account_id"], name: "index_antennas_on_account_id"
t.index ["any_accounts"], name: "index_antennas_on_any_accounts"
t.index ["any_domains"], name: "index_antennas_on_any_domains"
Expand Down Expand Up @@ -1357,6 +1367,7 @@
add_foreign_key "account_conversations", "conversations", on_delete: :cascade
add_foreign_key "account_deletion_requests", "accounts", on_delete: :cascade
add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade
add_foreign_key "account_groups", "accounts", on_delete: :cascade
add_foreign_key "account_migrations", "accounts", column: "target_account_id", on_delete: :nullify
add_foreign_key "account_migrations", "accounts", on_delete: :cascade
add_foreign_key "account_moderation_notes", "accounts"
Expand Down
Loading

0 comments on commit d1ae53e

Please sign in to comment.