Improved timeline merge for account subscriptions and add option to subscribe to media only

This commit is contained in:
noellabo 2020-11-15 15:57:58 +09:00
parent 9720a83867
commit 0d0d754b27
45 changed files with 264 additions and 93 deletions

View file

@ -24,6 +24,7 @@ gem 'paperclip', '~> 6.0'
gem 'blurhash', '~> 0.1'
gem 'active_model_serializers', '~> 0.10'
gem 'active_record_extended', git: 'https://github.com/GeorgeKaraszi/ActiveRecordExtended.git', ref: '8c9d1a3e72aabf1a4f1fbeeb93a6e0f170fd0c3e'
gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.6.0', require: false
gem 'browser'

View file

@ -1,3 +1,13 @@
GIT
remote: https://github.com/GeorgeKaraszi/ActiveRecordExtended.git
revision: 8c9d1a3e72aabf1a4f1fbeeb93a6e0f170fd0c3e
ref: 8c9d1a3e72aabf1a4f1fbeeb93a6e0f170fd0c3e
specs:
active_record_extended (2.0.0)
activerecord (>= 5.1, < 6.2)
ar_outer_joins (~> 0.2)
pg (< 3.0)
GEM
remote: https://rubygems.org/
specs:
@ -74,6 +84,8 @@ GEM
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
rake (>= 10.4, < 14.0)
ar_outer_joins (0.2.0)
activerecord (>= 3.2)
ast (2.4.2)
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
@ -668,6 +680,7 @@ PLATFORMS
DEPENDENCIES
active_model_serializers (~> 0.10)
active_record_extended!
active_record_query_trace (~> 1.8)
addressable (~> 2.8)
annotate (~> 3.1)

View file

@ -39,7 +39,7 @@ class Api::V1::AccountsController < Api::BaseController
end
def subscribe
AccountSubscribeService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs), list_id: params[:list_id])
AccountSubscribeService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs), media_only: truthy_param?(:media_only) || false, list_id: params[:list_id])
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end
@ -59,7 +59,7 @@ class Api::V1::AccountsController < Api::BaseController
end
def unsubscribe
UnsubscribeAccountService.new.call(current_user.account, @account, params[:list_id])
UnsubscribeAccountService.new.call(current_user.account, @account, list_id: params[:list_id])
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end

View file

@ -29,6 +29,6 @@ class Api::V1::DomainSubscribesController < Api::BaseController
end
def domain_subscribe_params
params.permit(:domain, :list_id, :exclude_reblog)
params.permit(:domain, :list_id, :exclude_reblog, :media_only)
end
end

View file

@ -38,6 +38,6 @@ class Api::V1::FollowTagsController < Api::BaseController
end
def follow_tag_params
params.permit(:name)
params.permit(:name, :media_only)
end
end

View file

@ -44,6 +44,6 @@ class Api::V1::KeywordSubscribesController < Api::BaseController
end
def resource_params
params.permit(:name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :disabled, :list_id)
params.permit(:name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :media_only, :disabled, :list_id)
end
end

View file

@ -16,7 +16,7 @@ class Settings::AccountSubscribesController < Settings::BaseController
def create
@form_account_subscribing = Form::AccountSubscribe.new(account_subscribe_params)
target_account = AccountSubscribeService.new.call(current_account, @form_account_subscribing.acct, { show_reblogs: @form_account_subscribing.show_reblogs, list_id: @form_account_subscribing.list_id })
target_account = AccountSubscribeService.new.call(current_account, @form_account_subscribing.acct, { show_reblogs: @form_account_subscribing.show_reblogs, list_id: @form_account_subscribing.list_id, media_only: @form_account_subscribing.media_only })
if target_account
redirect_to settings_account_subscribes_path
@ -38,7 +38,7 @@ class Settings::AccountSubscribesController < Settings::BaseController
end
def destroy
UnsubscribeAccountService.new.call(current_account, @account_subscribing.target_account, @account_subscribing.list_id)
UnsubscribeAccountService.new.call(current_account, @account_subscribing.target_account, list_id: @account_subscribing.list_id)
redirect_to settings_account_subscribes_path
end
@ -46,7 +46,7 @@ class Settings::AccountSubscribesController < Settings::BaseController
def set_account_subscribing
@account_subscribing = current_account.active_subscribes.find(params[:id])
@form_account_subscribing = Form::AccountSubscribe.new(id: @account_subscribing.id, acct: @account_subscribing.target_account.acct, show_reblogs: @account_subscribing.show_reblogs, list_id: @account_subscribing.list_id)
@form_account_subscribing = Form::AccountSubscribe.new(id: @account_subscribing.id, acct: @account_subscribing.target_account.acct, show_reblogs: @account_subscribing.show_reblogs, list_id: @account_subscribing.list_id, media_only: @account_subscribing.media_only)
end
def set_account_subscribings
@ -69,6 +69,6 @@ class Settings::AccountSubscribesController < Settings::BaseController
end
def resource_params
params.require(:form_account_subscribe).permit(:acct, :show_reblogs, :list_id)
params.require(:form_account_subscribe).permit(:acct, :show_reblogs, :list_id, :media_only)
end
end

View file

@ -68,6 +68,6 @@ class Settings::DomainSubscribesController < Settings::BaseController
end
def resource_params
params.require(:domain_subscribe).permit(:domain, :list_id, :exclude_reblog)
params.require(:domain_subscribe).permit(:domain, :list_id, :exclude_reblog, :media_only)
end
end

View file

@ -69,6 +69,6 @@ class Settings::FollowTagsController < Settings::BaseController
end
def resource_params
params.require(:follow_tag).permit(:name, :list_id)
params.require(:follow_tag).permit(:name, :list_id, :media_only)
end
end

View file

@ -69,7 +69,7 @@ class Settings::KeywordSubscribesController < ApplicationController
end
def resource_params
params.require(:keyword_subscribe).permit(:name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :disabled, :list_id)
params.require(:keyword_subscribe).permit(:name, :keyword, :exclude_keyword, :ignorecase, :regexp, :ignore_block, :disabled, :list_id, :media_only)
end
def set_body_classes

View file

@ -14,7 +14,7 @@ class ActivityPub::Activity::Block < ActivityPub::Activity
UnfollowService.new.call(@account, target_account) if @account.following?(target_account)
UnfollowService.new.call(target_account, @account) if target_account.following?(@account)
RejectFollowService.new.call(target_account, @account) if target_account.requested?(@account)
UnsubscribeAccountService.new.call(target_account, @account, :all)
UnsubscribeAccountService.new.call(target_account, @account, list_id: :all)
unless delete_arrived_first?(@json['id'])
BlockWorker.perform_async(@account.id, target_account.id)

View file

@ -100,14 +100,41 @@ class FeedManager
# @param [Account] from_account
# @param [Account] into_account
# @return [void]
def merge_into_home(from_account, into_account, public_only = false)
timeline_key = key(:home, into_account.id)
def merge_into_home(from_account, into_account, options = {})
options = { show_reblogs: true }.merge(options)
if options[:list_id].nil?
list = nil
type = :home
id = into_account.id
else
list = List.find(options[:list_id])
type = :list
id = options[:list_id]
end
timeline_key = key(type, id)
aggregate = into_account.user&.aggregates_reblogs?
query = from_account.statuses.where(visibility: public_only ? :public : [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
query = from_account.statuses
query = query.where(visibility: options[:public_only] ? :public : [:public, :unlisted, :private])
if options[:show_reblogs] && options[:media_only]
query = begin
Status
.union(query.where(reblog_of_id: nil).joins(:media_attachments))
.union(query.where.not(reblog_of_id: nil).joins({:reblog => :media_attachments}))
end
else
query = query.joins(:media_attachments) if options[:media_only]
query = query.where(reblog_of_id: nil) if options[:show_reblogs] == false
end
query = query.includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
query = query.where('id > ?', oldest_home_score)
oldest_home_score = redis.zrange(timeline_key, 0, 0).first.to_i
query = query.where('id >= ?', oldest_home_score)
end
statuses = query.to_a
@ -115,37 +142,20 @@ class FeedManager
statuses.each do |status|
next if filter_from_home?(status, into_account.id, crutches)
next if !list.nil? && filter_from_list?(status, list)
add_to_feed(:home, into_account.id, status, aggregate)
add_to_feed(type, id, status, aggregate)
end
trim(:home, into_account.id)
trim(type, id)
end
# Fill a list feed with an account's statuses
# @param [Account] from_account
# @param [List] list
# @return [void]
def merge_into_list(from_account, list)
timeline_key = key(:list, list.id)
aggregate = list.account.user&.aggregates_reblogs?
query = from_account.statuses.where(visibility: [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
query = query.where('id > ?', oldest_home_score)
end
statuses = query.to_a
crutches = build_crutches(list.account_id, statuses)
statuses.each do |status|
next if filter_from_home?(status, list.account_id, crutches) || filter_from_list?(status, list)
add_to_feed(:list, list.id, status, aggregate)
end
trim(:list, list.id)
def merge_into_list(from_account, list, options = {})
merge_into_home(from_account, list.account, options.merge(list_id: list.id))
end
# Remove an account's statuses from a home feed
@ -154,9 +164,9 @@ class FeedManager
# @return [void]
def unmerge_from_home(from_account, into_account)
timeline_key = key(:home, into_account.id)
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
oldest_home_score = redis.zrange(timeline_key, 0, 0)&.first&.to_i || 0
from_account.statuses.select('id, reblog_of_id').where('id > ?', oldest_home_score).reorder(nil).find_each do |status|
from_account.statuses.select('id, reblog_of_id').where('id >= ?', oldest_home_score).reorder(nil).find_each do |status|
remove_from_feed(:home, into_account.id, status, into_account.user&.aggregates_reblogs?)
end
end
@ -167,9 +177,9 @@ class FeedManager
# @return [void]
def unmerge_from_list(from_account, list)
timeline_key = key(:list, list.id)
oldest_list_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
oldest_list_score = redis.zrange(timeline_key, 0, 0)&.first&.to_i || 0
from_account.statuses.select('id, reblog_of_id').where('id > ?', oldest_list_score).reorder(nil).find_each do |status|
from_account.statuses.select('id, reblog_of_id').where('id >= ?', oldest_list_score).reorder(nil).find_each do |status|
remove_from_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?)
end
end

View file

@ -5,10 +5,11 @@
# id :bigint(8) not null, primary key
# account_id :bigint(8)
# target_account_id :bigint(8)
# show_reblogs :boolean default(TRUE), not null
# created_at :datetime not null
# updated_at :datetime not null
# list_id :bigint(8)
# show_reblogs :boolean default(TRUE), not null
# media_only :boolean default(FALSE), not null
#
class AccountSubscribe < ApplicationRecord
@ -26,6 +27,7 @@ class AccountSubscribe < ApplicationRecord
scope :home, -> { where(list_id: nil) }
scope :list, -> { where.not(list_id: nil) }
scope :with_reblog, ->(reblog) { where(show_reblogs: true) if reblog }
scope :with_media, ->(status) { where(media_only: false) unless status.with_media? }
after_create :increment_cache_counters
after_destroy :decrement_cache_counters

View file

@ -211,11 +211,13 @@ module AccountInteractions
block&.destroy
end
def subscribe!(other_account, reblogs = true, list_id = nil)
rel = active_subscribes.create_with(show_reblogs: reblogs)
.find_or_create_by!(target_account: other_account, list_id: list_id)
def subscribe!(other_account, options = {})
options = { show_reblogs: true, list_id: nil, media_only: false }.merge(options)
rel.update!(show_reblogs: reblogs)
rel = active_subscribes.create_with(show_reblogs: options[:show_reblogs], media_only: options[:media_only] || false)
.find_or_create_by!(target_account: other_account, list_id: options[:list_id])
rel.update!(show_reblogs: options[:show_reblogs], media_only: options[:media_only] || false)
remove_potential_friendship(other_account)
rel

View file

@ -9,6 +9,7 @@
# created_at :datetime not null
# updated_at :datetime not null
# exclude_reblog :boolean default(TRUE)
# media_only :boolean default(FALSE), not null
#
class DomainSubscribe < ApplicationRecord
@ -21,4 +22,5 @@ class DomainSubscribe < ApplicationRecord
scope :domain_to_home, ->(domain) { where(domain: domain).where(list_id: nil) }
scope :domain_to_list, ->(domain) { where(domain: domain).where.not(list_id: nil) }
scope :with_reblog, ->(reblog) { where(exclude_reblog: false) if reblog }
scope :with_media, ->(status) { where(media_only: false) unless status.with_media? }
end

View file

@ -10,9 +10,8 @@
# target_account_id :bigint(8) not null
# show_reblogs :boolean default(TRUE), not null
# uri :string
# notify :boolean default(FALSE), not null
# private :boolean default(TRUE), not null
# delivery :boolean default(TRUE), not null
# notify :boolean default(FALSE), not null
#
class Follow < ApplicationRecord

View file

@ -31,7 +31,6 @@ class FollowRequest < ApplicationRecord
def authorize!
account.follow!(target_account, reblogs: show_reblogs, notify: notify, delivery: delivery, uri: uri, bypass_limit: true)
UnsubscribeAccountService.new.call(account, target_account) if account.subscribing?(target_account)
MergeWorker.perform_async(target_account.id, account.id) if account.local? && delivery?
destroy!
end

View file

@ -8,6 +8,7 @@
# created_at :datetime not null
# updated_at :datetime not null
# list_id :bigint(8)
# media_only :boolean default(FALSE), not null
#
class FollowTag < ApplicationRecord
@ -23,6 +24,7 @@ class FollowTag < ApplicationRecord
scope :home, -> { where(list_id: nil) }
scope :list, -> { where.not(list_id: nil) }
scope :with_media, ->(status) { where(media_only: false) unless status.with_media? }
def name=(str)
self.tag = Tag.find_or_create_by_names(str.strip)&.first

View file

@ -6,6 +6,7 @@ class Form::AccountSubscribe
attribute :acct, :string
attribute :show_reblogs, :boolean, default: true
attribute :list_id, :integer
attribute :media_only, :boolean, default: false
def acct=(val)
super(val.to_s.strip.gsub(/\A@/, ''))

View file

@ -14,6 +14,7 @@
# disabled :boolean default(FALSE)
# exclude_keyword :string default(""), not null
# list_id :bigint(8)
# media_only :boolean default(FALSE), not null
#
class KeywordSubscribe < ApplicationRecord
@ -30,8 +31,9 @@ class KeywordSubscribe < ApplicationRecord
scope :ignore_block, -> { where(ignore_block: true) }
scope :home, -> { where(list_id: nil) }
scope :list, -> { where.not(list_id: nil) }
scope :without_local_followed_home, ->(account) { home.where.not(account: account.delivery_followers.local).where.not(account: account.subscribers.local) }
scope :without_local_followed_list, ->(account) { list.where.not(list_id: ListAccount.followed_lists(account)).where.not(list_id: AccountSubscribe.subscribed_lists(account)) }
scope :without_local_followed_home, ->(account) { home.where.not(account: account.delivery_followers.local) }
scope :without_local_followed_list, ->(account) { list.where.not(list_id: ListAccount.followed_lists(account)) }
scope :with_media, ->(status) { where(media_only: false) unless status.with_media? }
def keyword=(val)
super(regexp ? val : keyword_normalization(val))

View file

@ -5,30 +5,38 @@ class AccountSubscribeService < BaseService
# @param [Account] source_account From which to subscribe
# @param [String, Account] uri User URI to subscribe in the form of username@domain (or account record)
def call(source_account, target_acct, options = {})
@options = { show_reblogs: true, list_id: nil }.merge(options)
@source_account = source_account
@options = { show_reblogs: true, list_id: nil, media_only: false }.merge(options)
if target_acct.class.name == 'Account'
target_account = target_acct
@target_account = target_acct
else
begin
target_account = ResolveAccountService.new.call(target_acct, skip_webfinger: true)
target_account ||= ResolveAccountService.new.call(target_acct, skip_webfinger: false)
@target_account = ResolveAccountService.new.call(target_acct, skip_webfinger: true)
@target_account ||= ResolveAccountService.new.call(target_acct, skip_webfinger: false)
rescue
target_account = nil
@target_account = nil
end
end
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || (!target_account.local? && target_account.ostatus?) || source_account.domain_blocking?(target_account.domain)
raise ActiveRecord::RecordNotFound if @target_account.nil? || @target_account.id == @source_account.id || @target_account.suspended?
raise Mastodon::NotPermittedError if @target_account.blocking?(@source_account) || @source_account.blocking?(@target_account) || (!@target_account.local? && @target_account.ostatus?) || @source_account.domain_blocking?(@target_account.domain)
if source_account.subscribing?(target_account, @options[:list_id])
return
if @source_account.subscribing?(@target_account, @options[:list_id])
return change_subscribe_options!
end
ActivityTracker.increment('activity:interactions')
subscribe = source_account.subscribe!(target_account, @options[:show_reblogs], @options[:list_id])
MergeWorker.perform_async(target_account.id, source_account.id, true) if @options[:list_id].nil?
subscribe = @source_account.subscribe!(@target_account, @options)
MergeWorker.perform_async(@target_account.id, @source_account.id, @options.merge(public_only: true))
subscribe
end
private
def change_subscribe_options!
@source_account.subscribe!(@target_account, @options)
end
end

View file

@ -9,8 +9,8 @@ class BlockService < BaseService
UnfollowService.new.call(account, target_account) if account.following?(target_account)
UnfollowService.new.call(target_account, account) if target_account.following?(account)
RejectFollowService.new.call(target_account, account) if target_account.requested?(account)
UnsubscribeAccountService.new.call(account, target_account, :all)
UnsubscribeAccountService.new.call(target_account, account, :all)
UnsubscribeAccountService.new.call(account, target_account, list_id: :all)
UnsubscribeAccountService.new.call(target_account, account, list_id: :all)
block = account.block!(target_account)

View file

@ -68,7 +68,7 @@ class FanOutOnWriteService < BaseService
def deliver_to_subscribers(status)
Rails.logger.debug "Delivering status #{status.id} to subscribers"
status.account.subscribers_for_local_distribution.with_reblog(status.reblog?).select(:id, :account_id).reorder(nil).find_in_batches do |subscribings|
status.account.subscribers_for_local_distribution.with_reblog(status.reblog?).with_media(status.proper).select(:id, :account_id).reorder(nil).find_in_batches do |subscribings|
FeedInsertWorker.push_bulk(subscribings) do |subscribing|
[status.id, subscribing.account_id, :home]
end
@ -78,7 +78,7 @@ class FanOutOnWriteService < BaseService
def deliver_to_subscribers_lists(status)
Rails.logger.debug "Delivering status #{status.id} to subscribers lists"
status.account.list_subscribers_for_local_distribution.with_reblog(status.reblog?).select(:id, :list_id).reorder(nil).find_in_batches do |subscribings|
status.account.list_subscribers_for_local_distribution.with_reblog(status.reblog?).with_media(status.proper).select(:id, :list_id).reorder(nil).find_in_batches do |subscribings|
FeedInsertWorker.push_bulk(subscribings) do |subscribing|
[status.id, subscribing.list_id, :list]
end
@ -93,7 +93,7 @@ class FanOutOnWriteService < BaseService
end
def deliver_to_domain_subscribers_home(status)
DomainSubscribe.domain_to_home(status.account.domain).with_reblog(status.reblog?).select(:id, :account_id).find_in_batches do |subscribes|
DomainSubscribe.domain_to_home(status.account.domain).with_reblog(status.reblog?).with_media(status.proper).select(:id, :account_id).find_in_batches do |subscribes|
FeedInsertWorker.push_bulk(subscribes) do |subscribe|
[status.id, subscribe.account_id, :home]
end
@ -101,7 +101,7 @@ class FanOutOnWriteService < BaseService
end
def deliver_to_domain_subscribers_list(status)
DomainSubscribe.domain_to_list(status.account.domain).with_reblog(status.reblog?).select(:id, :list_id).find_in_batches do |subscribes|
DomainSubscribe.domain_to_list(status.account.domain).with_reblog(status.reblog?).with_media(status.proper).select(:id, :list_id).find_in_batches do |subscribes|
FeedInsertWorker.push_bulk(subscribes) do |subscribe|
[status.id, subscribe.list_id, :list]
end
@ -118,7 +118,7 @@ class FanOutOnWriteService < BaseService
def deliver_to_keyword_subscribers_home(status)
match_accounts = []
KeywordSubscribe.active.without_local_followed_home(status.account).order(:account_id).each do |keyword_subscribe|
KeywordSubscribe.active.with_media(status.proper).without_local_followed_home(status.account).order(:account_id).each do |keyword_subscribe|
next if match_accounts[-1] == keyword_subscribe.account_id
match_accounts << keyword_subscribe.account_id if keyword_subscribe.match?(status.index_text)
end
@ -131,7 +131,7 @@ class FanOutOnWriteService < BaseService
def deliver_to_keyword_subscribers_list(status)
match_lists = []
KeywordSubscribe.active.without_local_followed_list(status.account).order(:list_id).each do |keyword_subscribe|
KeywordSubscribe.active.with_media(status.proper).without_local_followed_list(status.account).order(:list_id).each do |keyword_subscribe|
next if match_lists[-1] == keyword_subscribe.list_id
match_lists << keyword_subscribe.list_id if keyword_subscribe.match?(status.index_text)
end
@ -191,13 +191,13 @@ class FanOutOnWriteService < BaseService
end
def deliver_to_hashtag_followers_home(status)
FeedInsertWorker.push_bulk(FollowTag.home.where(tag: status.tags).pluck(:account_id).uniq) do |follower|
FeedInsertWorker.push_bulk(FollowTag.home.where(tag: status.tags).with_media(status.proper).pluck(:account_id).uniq) do |follower|
[status.id, follower, :home]
end
end
def deliver_to_hashtag_followers_list(status)
FeedInsertWorker.push_bulk(FollowTag.list.where(tag: status.tags).pluck(:list_id).uniq) do |list_id|
FeedInsertWorker.push_bulk(FollowTag.list.where(tag: status.tags).with_media(status.proper).pluck(:list_id).uniq) do |list_id|
[status.id, list_id, :list]
end
end

View file

@ -4,20 +4,21 @@ class UnsubscribeAccountService < BaseService
# UnsubscribeAccount
# @param [Account] source_account Where to unsubscribe from
# @param [Account] target_account Which to unsubscribe
def call(source_account, target_account, list_id = nil)
if (list_id == :all)
AccountSubscribe.where(account: source_account, target_account: target_account).each do |subscribe|
subscribe.destroy!
UnmergeWorker.perform_async(target_account.id, source_account.id) if subscribe.list_id.nil?
end
def call(source_account, target_account, options = {})
if (options[:list_id] == :all)
subscribes = AccountSubscribe.where(account: source_account, target_account: target_account)
else
subscribes = AccountSubscribe.where(account: source_account, target_account: target_account, list_id: options[:list_id])
end
subscribe = AccountSubscribe.find_by(account: source_account, target_account: target_account, list_id: list_id)
return unless subscribe
subscribe.destroy!
UnmergeWorker.perform_async(target_account.id, source_account.id) if list_id.nil?
subscribe
subscribes.each do |subscribe|
list_id = subscribe.list_id
subscribe.destroy!
if list_id.nil? && !source_account.delivery_following?(target_account)
UnmergeWorker.perform_async(target_account.id, source_account.id)
elsif !ListAccount.where(list_id: list_id, account_id: target_account.id).exists?
UnmergeWorker.perform_async(target_account.id, source_account.id, list_id: list_id)
end
end
end
end

View file

@ -4,6 +4,9 @@
%td.nowrap.symbol
- if account_subscribe.show_reblogs
= fa_icon('check')
%td.nowrap.symbol
- if account_subscribe.media_only
= fa_icon('check')
%td.nowrap
- if account_subscribe.list_id
= fa_icon 'list-ul'

View file

@ -4,6 +4,9 @@
.fields-group
= f.input :show_reblogs, as: :boolean, wrapper: :with_label
.fields-group
= f.input :media_only, as: :boolean, wrapper: :with_label
.fields-group
= f.label :list_id
= f.collection_select :list_id, home_list_new(@lists), :first, :last

View file

@ -16,6 +16,7 @@
%tr
%th= t('simple_form.labels.form_account_subscribe.acct')
%th.nowrap.symbol= t('simple_form.labels.form_account_subscribe.reblog')
%th.nowrap.symbol= t('simple_form.labels.form_account_subscribe.media_only')
%th.nowrap= t('simple_form.labels.form_account_subscribe.timeline')
%th.nowrap
%tbody

View file

@ -4,6 +4,9 @@
%td.nowrap.symbol
- if domain_subscribe.exclude_reblog
= fa_icon('times')
%td.nowrap.symbol
- if domain_subscribe.media_only
= fa_icon('check')
%td.nowrap
- if domain_subscribe.list_id
= fa_icon 'list-ul'

View file

@ -4,6 +4,9 @@
.fields-group
= f.input :exclude_reblog, wrapper: :with_label
.fields-group
= f.input :media_only, as: :boolean, wrapper: :with_label
.fields-group
= f.label :list_id
= f.collection_select :list_id, home_list_new(@lists), :first, :last

View file

@ -16,6 +16,7 @@
%tr
%th= t('simple_form.labels.domain_subscribe.domain')
%th.nowrap.symbol= t('simple_form.labels.domain_subscribe.reblog')
%th.nowrap.symbol= t('simple_form.labels.domain_subscribe.media_only')
%th.nowrap= t('simple_form.labels.domain_subscribe.timeline')
%th.nowrap
%tbody

View file

@ -1,6 +1,9 @@
.fields-group
= f.input :name, wrapper: :with_label
.fields-group
= f.input :media_only, as: :boolean, wrapper: :with_label
.fields-group
= f.label :list_id
= f.collection_select :list_id, home_list_new(@lists), :first, :last

View file

@ -2,6 +2,9 @@
%td
= fa_icon 'hashtag'
= follow_tag.name
%td.nowrap.symbol
- if follow_tag.media_only
= fa_icon('check')
%td.nowrap
- if follow_tag.list_id
= fa_icon 'list-ul'

View file

@ -15,6 +15,7 @@
%thead
%tr
%th= t('simple_form.labels.follow_tag.name')
%th.nowrap.symbol= t('simple_form.labels.follow_tag.media_only')
%th.nowrap= t('simple_form.labels.follow_tag.timeline')
%th.nowrap
%tbody

View file

@ -16,6 +16,9 @@
.fields-group
= f.input :ignore_block, wrapper: :with_label
.fields-group
= f.input :media_only, as: :boolean, wrapper: :with_label
.fields-group
= f.label :list_id
= f.collection_select :list_id, home_list_new(@lists), :first, :last

View file

@ -18,6 +18,9 @@
%td.nowrap
- if keyword_subscribe.ignore_block
= t 'keyword_subscribes.ignore_block'
%td.nowrap.symbol
- if keyword_subscribe.media_only
= fa_icon('check')
%td.nowrap
- if keyword_subscribe.list_id
= fa_icon 'list-ul'

View file

@ -21,6 +21,7 @@
%th= t('simple_form.labels.keyword_subscribes.keyword')
%th.nowrap= t('simple_form.labels.keyword_subscribes.ignorecase')
%th.nowrap= t('simple_form.labels.keyword_subscribes.ignore_block')
%th.nowrap= t('simple_form.labels.keyword_subscribes.media_only')
%th.nowrap= t('simple_form.labels.keyword_subscribes.timeline')
%th.nowrap= t('simple_form.labels.keyword_subscribes.disabled')
%th.nowrap

View file

@ -3,8 +3,14 @@
class MergeWorker
include Sidekiq::Worker
def perform(from_account_id, into_account_id, public_only = false)
FeedManager.instance.merge_into_home(Account.find(from_account_id), Account.find(into_account_id), public_only)
def perform(from_account_id, into_account_id, options = {})
options.symbolize_keys!
if options[:list_id].nil?
FeedManager.instance.merge_into_home(Account.find(from_account_id), Account.find(into_account_id), options)
else
FeedManager.instance.merge_into_list(Account.find(from_account_id), List.find(options[:list_id]), options)
end
rescue ActiveRecord::RecordNotFound
true
ensure

View file

@ -5,8 +5,14 @@ class UnmergeWorker
sidekiq_options queue: 'pull'
def perform(from_account_id, into_account_id)
FeedManager.instance.unmerge_from_home(Account.find(from_account_id), Account.find(into_account_id))
def perform(from_account_id, into_account_id, options = {})
options.symbolize_keys!
if options[:list_id].nil?
FeedManager.instance.unmerge_from_home(Account.find(from_account_id), Account.find(into_account_id))
else
FeedManager.instance.unmerge_from_list(Account.find(from_account_id), List.find(options[:list_id]))
end
rescue ActiveRecord::RecordNotFound
true
end

View file

@ -71,15 +71,18 @@ en:
domain_subscribe:
domain: Specify the domain name of the server you want to subscribe to
exclude_reblog: Exclude boosted posts from subscription
media_only: Target only statuses with media attached
email_domain_block:
domain: This can be the domain name that shows up in the e-mail address, the MX record that domain resolves to, or IP of the server that MX record resolves to. Those will be checked upon user sign-up and the sign-up will be rejected.
with_dns_records: An attempt to resolve the given domain's DNS records will be made and the results will also be blocked
featured_tag:
name: 'You might want to use one of these:'
follow_tag:
media_only: Target only statuses with media attached
name: Specify the name of the hashtag without '#' you want to follow
form_account_subscribe:
acct: Specify the username@domain of the account you want to subscribe
media_only: Target only statuses with media attached
show_reblogs: Show boosted posts from subscription
form_challenge:
current_password: You are entering a secure area
@ -99,6 +102,7 @@ en:
exclude_keyword: List multiple excluded keywords separated by commas (or use regular expressions)
ignore_block: You can prioritize keyword subscriptions while keeping the entire domain block
keyword: List multiple keywords separated by commas (or use regular expressions)
media_only: Target only statuses with media attached
name: Optional
rule:
text: Describe a rule or requirement for users on this server. Try to keep it short and simple
@ -210,6 +214,7 @@ en:
domain: Domain
exclude_reblog: Exclude boost
list_id: Target timeline
media_only: Media only
timeline: Timeline
reblog: Boost
email_domain_block:
@ -218,11 +223,13 @@ en:
name: Hashtag
follow_tag:
list_id: Target timeline
media_only: Media only
name: Tag name
timeline: Timeline
form_account_subscribe:
acct: Account
list_id: Target timeline
media_only: Media only
reblog: Boost
show_reblogs: Show boost
timeline: Timeline
@ -248,6 +255,7 @@ en:
ignore_block: Ignore User's domain blocking
keyword: Keyword list or regular expression
list_id: Target timeline
media_only: Media only
name: Name
regexp: Use regular expressions for keywords
keyword_subscribes:
@ -255,6 +263,7 @@ en:
ignorecase: Case
ignore_block: Block
keyword: String
media_only: Media
name: Name
regexp: Type
timeline: Timeline

View file

@ -71,15 +71,18 @@ ja:
domain_subscribe:
domain: 購読したいサーバのドメイン名を指定します
exclude_reblog: ブーストされた投稿を購読から除外します
media_only: メディアが添付された投稿だけを対象にします
email_domain_block:
domain: メールアドレスのドメイン名および、名前解決したMXレコード、IPアドレスを指定できます。ユーザー登録時にこれらをチェックし、該当する場合はユーザー登録を拒否します。
with_dns_records: 指定したドメインのDNSレコードを取得し、その結果もメールドメインブロックに登録されます
featured_tag:
name: 'これらを使うといいかもしれません:'
follow_tag:
media_only: メディアが添付された投稿だけを対象にします
name: フォローしたいハッシュタグを '#' 抜きで指定します
form_account_subscribe:
acct: 購読したいアカウントを username@domain 形式で指定します
media_only: メディアが添付された投稿だけを対象にします
show_reblogs: ブーストされた投稿を購読に含めます
form_challenge:
current_password: セキュリティ上重要なエリアにアクセスしています
@ -99,6 +102,7 @@ ja:
exclude_keyword: カンマで区切って複数の除外するキーワードを並べます(または正規表現で指定します)
ignore_block: ドメイン全体を非表示にしたまま、キーワードの購読を優先することができます
keyword: カンマで区切って複数のキーワードを並べます(または正規表現で指定します)
media_only: メディアが添付された投稿だけを対象にします
name: オプションです
rule:
text: ユーザーのためのルールや要件を記述してください。短くシンプルにしてください。
@ -210,6 +214,7 @@ ja:
domain: ドメイン
exclude_reblog: ブースト除外
list_id: 対象タイムライン
media_only: メディアのみ
reblog: ブースト
timeline: タイムライン
email_domain_block:
@ -222,11 +227,13 @@ ja:
name: ハッシュタグ
follow_tag:
list_id: 対象タイムライン
media_only: メディアのみ
name: ハッシュタグ
timeline: タイムライン
form_account_subscribe:
acct: アカウント
list_id: 対象タイムライン
media_only: メディアのみ
reblog: ブースト
show_reblogs: ブーストを表示
timeline: タイムライン
@ -251,6 +258,7 @@ ja:
ignorecase: 大文字と小文字を区別しない
ignore_block: ユーザーによるドメインブロックを無視する
keyword: キーワードまたは正規表現
media_only: メディアのみ
list_id: 対象タイムライン
name: 名称
regexp: キーワードに正規表現を使う
@ -259,6 +267,7 @@ ja:
ignorecase: 大小
ignore_block: ブロック
keyword: 設定値
media_only: メディア
name: 名称
regexp: 種別
timeline: タイムライン

View file

@ -0,0 +1,17 @@
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
class AddMediaOnlyToAccountSubscribe < ActiveRecord::Migration[5.2]
include Mastodon::MigrationHelpers
disable_ddl_transaction!
def up
safety_assured do
add_column_with_default :account_subscribes, :media_only, :boolean, default: false, allow_null: false
end
end
def down
remove_column :account_subscribes, :media_only
end
end

View file

@ -0,0 +1,17 @@
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
class AddMediaOnlyToDomainSubscribe < ActiveRecord::Migration[5.2]
include Mastodon::MigrationHelpers
disable_ddl_transaction!
def up
safety_assured do
add_column_with_default :domain_subscribes, :media_only, :boolean, default: false, allow_null: false
end
end
def down
remove_column :domain_subscribes, :media_only
end
end

View file

@ -0,0 +1,17 @@
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
class AddMediaOnlyToKeywordSubscribe < ActiveRecord::Migration[5.2]
include Mastodon::MigrationHelpers
disable_ddl_transaction!
def up
safety_assured do
add_column_with_default :keyword_subscribes, :media_only, :boolean, default: false, allow_null: false
end
end
def down
remove_column :keyword_subscribes, :media_only
end
end

View file

@ -0,0 +1,17 @@
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
class AddMediaOnlyToFollowTag < ActiveRecord::Migration[5.2]
include Mastodon::MigrationHelpers
disable_ddl_transaction!
def up
safety_assured do
add_column_with_default :follow_tags, :media_only, :boolean, default: false, allow_null: false
end
end
def down
remove_column :follow_tags, :media_only
end
end

View file

@ -139,6 +139,7 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
t.datetime "updated_at", null: false
t.bigint "list_id"
t.boolean "show_reblogs", default: true, null: false
t.boolean "media_only", default: false, null: false
t.index ["account_id"], name: "index_account_subscribes_on_account_id"
t.index ["list_id"], name: "index_account_subscribes_on_list_id"
t.index ["target_account_id"], name: "index_account_subscribes_on_target_account_id"
@ -419,6 +420,7 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "exclude_reblog", default: true
t.boolean "media_only", default: false, null: false
t.index ["account_id"], name: "index_domain_subscribes_on_account_id"
t.index ["list_id"], name: "index_domain_subscribes_on_list_id"
end
@ -529,6 +531,7 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "list_id"
t.boolean "media_only", default: false, null: false
t.index ["account_id"], name: "index_follow_tags_on_account_id"
t.index ["list_id"], name: "index_follow_tags_on_list_id"
t.index ["tag_id"], name: "index_follow_tags_on_tag_id"
@ -542,7 +545,6 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
t.boolean "show_reblogs", default: true, null: false
t.string "uri"
t.boolean "notify", default: false, null: false
t.boolean "private", default: true, null: false
t.boolean "delivery", default: true, null: false
t.index ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true
t.index ["target_account_id"], name: "index_follows_on_target_account_id"
@ -605,6 +607,7 @@ ActiveRecord::Schema.define(version: 2021_08_08_071221) do
t.boolean "disabled", default: false
t.string "exclude_keyword", default: "", null: false
t.bigint "list_id"
t.boolean "media_only", default: false, null: false
t.index ["account_id"], name: "index_keyword_subscribes_on_account_id"
t.index ["list_id"], name: "index_keyword_subscribes_on_list_id"
end