class StatusFilter
attr_reader :status, :account
- def initialize(status, account)
- @status = status
- @account = account
+ def initialize(status, account, preloaded_relations = {})
+ @status = status
+ @account = account
+ @preloaded_relations = preloaded_relations
end
def filtered?
end
def blocking_account?
- account.blocking? status.account_id
+ @preloaded_relations[:blocking] ? @preloaded_relations[:blocking][status.account_id] : account.blocking?(status.account_id)
end
def blocking_domain?
- account.domain_blocking? status.account_domain
+ @preloaded_relations[:domain_blocking_by_domain] ? @preloaded_relations[:domain_blocking_by_domain][status.account_domain] : account.domain_blocking?(status.account_domain)
end
def muting_account?
- account.muting? status.account_id
+ @preloaded_relations[:muting] ? @preloaded_relations[:muting][status.account_id] : account.muting?(status.account_id)
end
def silenced_account?
end
def account_following_status_account?
- account&.following? status.account_id
+ @preloaded_relations[:following] ? @preloaded_relations[:following][status.account_id] : account&.following?(status.account_id)
end
def blocked_by_policy?
end
def policy_allows_show?
- StatusPolicy.new(account, status).show?
+ StatusPolicy.new(account, status, @preloaded_relations).show?
end
end
follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
end
+ def blocked_by_map(target_account_ids, account_id)
+ follow_mapping(Block.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
+ end
+
def muting_map(target_account_ids, account_id)
Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping|
mapping[mute.target_account_id] = {
def domain_blocking_map(target_account_ids, account_id)
accounts_map = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h
- blocked_domains = AccountDomainBlock.where(account_id: account_id, domain: accounts_map.values).pluck(:domain)
- accounts_map.map { |id, domain| [id, blocked_domains.include?(domain)] }.to_h
+ blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
+ accounts_map.map { |id, domain| [id, blocked_domains[domain]] }.to_h
+ end
+
+ def domain_blocking_map_by_domain(target_domains, account_id)
+ follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
end
private
end
def find_statuses_from_tree_path(ids, account)
- statuses = statuses_with_accounts(ids).to_a
+ statuses = statuses_with_accounts(ids).to_a
+ account_ids = statuses.map(&:account_id).uniq
+ domains = statuses.map(&:account_domain).compact.uniq
- # FIXME: n+1 bonanza
- statuses.reject! { |status| filter_from_context?(status, account) }
+ relations = if account.present?
+ {
+ blocking: Account.blocking_map(account_ids, account.id),
+ blocked_by: Account.blocked_by_map(account_ids, account.id),
+ muting: Account.muting_map(account_ids, account.id),
+ following: Account.following_map(account_ids, account.id),
+ domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
+ }
+ end
+
+ statuses.reject! { |status| filter_from_context?(status, account, relations) }
# Order ancestors/descendants by tree path
statuses.sort_by! { |status| ids.index(status.id) }
Status.where(id: ids).includes(:account)
end
- def filter_from_context?(status, account)
- StatusFilter.new(status, account).filtered?
+ def filter_from_context?(status, account, relations)
+ StatusFilter.new(status, account, relations).filtered?
end
end
# frozen_string_literal: true
class StatusPolicy < ApplicationPolicy
+ def initialize(current_account, record, preloaded_relations = {})
+ super(current_account, record)
+
+ @preloaded_relations = preloaded_relations
+ end
+
def index?
staff?
end
def show?
if direct?
- owned? || record.mentions.where(account: current_account).exists?
+ owned? || mention_exists?
elsif private?
- owned? || current_account&.following?(author) || record.mentions.where(account: current_account).exists?
+ owned? || following_author? || mention_exists?
else
- current_account.nil? || !author.blocking?(current_account)
+ current_account.nil? || !author_blocking?
end
end
def reblog?
- !direct? && (!private? || owned?) && show? && !current_account&.blocking?(author)
+ !direct? && (!private? || owned?) && show? && !blocking_author?
end
def favourite?
- show? && !current_account&.blocking?(author)
+ show? && !blocking_author?
end
def destroy?
record.private_visibility?
end
+ def mention_exists?
+ return false if current_account.nil?
+
+ if record.mentions.loaded?
+ record.mentions.any? { |mention| mention.account_id == current_account.id }
+ else
+ record.mentions.where(account: current_account).exists?
+ end
+ end
+
+ def blocking_author?
+ return false if current_account.nil?
+
+ @preloaded_relations[:blocking] ? @preloaded_relations[:blocking][author.id] : current_account.blocking?(author)
+ end
+
+ def author_blocking?
+ return false if current_account.nil?
+
+ @preloaded_relations[:blocked_by] ? @preloaded_relations[:blocked_by][author.id] : author.blocking?(current_account)
+ end
+
+ def following_author?
+ return false if current_account.nil?
+
+ @preloaded_relations[:following] ? @preloaded_relations[:following][author.id] : current_account.following?(author)
+ end
+
def author
record.account
end