From 6d9f8ee11e5bd5773ffd046eaa78d62a983b1924 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 10 Nov 2016 00:03:33 +0100 Subject: [PATCH] Improve filtering of public/hashtag timelines, both in backlog and real-time --- app/channels/application_cable/channel.rb | 2 +- app/controllers/api/v1/accounts_controller.rb | 2 +- app/lib/feed_manager.rb | 14 ++++++++++++ app/models/status.rb | 22 ++++++++++++++----- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index 43adadbd90..28bf0d9d32 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -14,7 +14,7 @@ module ApplicationCable end def filter?(status) - !status.nil? && (current_user.account.blocking?(status.account) || (status.reblog? && current_user.account.blocking?(status.reblog.account))) + !status.nil? && FeedManager.instance.filter?(:public, status, current_user.account) end end end diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index f2049cd554..5cc0817f63 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -16,7 +16,7 @@ class Api::V1::AccountsController < ApiController def following results = Follow.where(account: @account).paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id]) - @accounts = Account.where(id: results.map(&:account_id)).with_counters.to_a + @accounts = Account.where(id: results.map(&:target_account_id)).with_counters.to_a next_path = following_api_v1_account_url(max_id: results.last.id) if results.size == DEFAULT_ACCOUNTS_LIMIT prev_path = following_api_v1_account_url(since_id: results.first.id) if results.size > 0 diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index af7b2380e0..fe88c0fb93 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -14,6 +14,8 @@ class FeedManager filter_from_home?(status, receiver) elsif timeline_type == :mentions filter_from_mentions?(status, receiver) + elsif timeline_type == :public + filter_from_public?(status, receiver) else false end @@ -80,4 +82,16 @@ class FeedManager should_filter = should_filter || receiver.blocking?(status.account) # or it's from someone I blocked should_filter end + + def filter_from_public?(status, receiver) + should_filter = receiver.blocking?(status.account) + + if status.reply? && !status.thread.account.nil? + should_filter = should_filter || receiver.blocking?(status.thread.account) + elsif status.reblog? + should_filter = should_filter || receiver.blocking?(status.reblog.account) + end + + should_filter + end end diff --git a/app/models/status.rb b/app/models/status.rb index 9db7a0a3a7..41c3b0a92f 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -88,12 +88,10 @@ class Status < ApplicationRecord end def as_public_timeline(account = nil) - query = joins('LEFT OUTER JOIN statuses AS reblogs ON statuses.reblog_of_id = reblogs.id') - .joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') - .where('accounts.silenced = FALSE') + query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id').where('accounts.silenced = FALSE') unless account.nil? - query = query.where('(reblogs.account_id IS NULL OR reblogs.account_id NOT IN (SELECT target_account_id FROM blocks WHERE account_id = ?)) AND statuses.account_id NOT IN (SELECT target_account_id FROM blocks WHERE account_id = ?)', account.id, account.id) + query = filter_timeline(query, account) end query.with_includes.with_counters @@ -101,12 +99,11 @@ class Status < ApplicationRecord def as_tag_timeline(tag, account = nil) query = tag.statuses - .joins('LEFT OUTER JOIN statuses AS reblogs ON statuses.reblog_of_id = reblogs.id') .joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id') .where('accounts.silenced = FALSE') unless account.nil? - query = query.where('(reblogs.account_id IS NULL OR reblogs.account_id NOT IN (SELECT target_account_id FROM blocks WHERE account_id = ?)) AND statuses.account_id NOT IN (SELECT target_account_id FROM blocks WHERE account_id = ?)', account.id, account.id) + query = filter_timeline(query, account) end query.with_includes.with_counters @@ -119,6 +116,19 @@ class Status < ApplicationRecord def reblogs_map(status_ids, account_id) select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).map { |s| [s.reblog_of_id, true] }.to_h end + + private + + def filter_timeline(query, account) + blocked = Block.where(account: account).pluck(:target_account_id) + + query + .joins('LEFT OUTER JOIN statuses AS parents ON statuses.in_reply_to_id = parents.id') + .joins('LEFT OUTER JOIN statuses AS reblogs ON statuses.reblog_of_id = reblogs.id') + .where('parents.account_id NOT IN (?)', blocked) + .where('statuses.account_id NOT IN (?)', blocked) + .where('(reblogs.id IS NULL OR reblogs.account_id NOT IN (?))', blocked) + end end before_validation do