def filter?(timeline_type, status, receiver_id)
if timeline_type == :home
- filter_from_home?(status, receiver_id)
+ filter_from_home?(status, receiver_id, build_crutches(receiver_id, [status]))
elsif timeline_type == :mentions
filter_from_mentions?(status, receiver_id)
+ elsif timeline_type == :direct
+ filter_from_direct?(status, receiver_id)
else
false
end
def push_to_list(list, status)
if status.reply? && status.in_reply_to_account_id != status.account_id
should_filter = status.in_reply_to_account_id != list.account_id
- should_filter &&= !ListAccount.where(list_id: list.id, account_id: status.in_reply_to_account_id).exists?
+ should_filter &&= !list.show_all_replies?
+ should_filter &&= !(list.show_list_replies? && ListAccount.where(list_id: list.id, account_id: status.in_reply_to_account_id).exists?)
return false if should_filter
end
+
return false unless add_to_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?)
+
trim(:list, list.id)
PushUpdateWorker.perform_async(list.account_id, status.id, "timeline:list:#{list.id}") if push_update_required?("timeline:list:#{list.id}")
true
thumbnail
hero
mascot
+ show_reblogs_in_public_timelines
+ show_replies_in_public_timelines
spam_check_enabled
trends
+ trendable_by_default
show_domain_blocks
show_domain_blocks_rationale
noindex
show_known_fediverse_at_about_page
preview_sensitive_media
profile_directory
+ hide_followers_count
+ enable_keybase
+ show_reblogs_in_public_timelines
+ show_replies_in_public_timelines
spam_check_enabled
trends
+ trendable_by_default
noindex
).freeze
where(language: nil).or where(language: account.chosen_languages)
end
- def as_home_timeline(account)
- where(account: [account] + account.following).where(visibility: [:public, :unlisted, :private])
- end
-
+ def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false)
+ # direct timeline is mix of direct message from_me and to_me.
+ # 2 queries are executed with pagination.
+ # constant expression using arel_table is required for partial index
+
+ # _from_me part does not require any timeline filters
+ query_from_me = where(account_id: account.id)
+ .where(Status.arel_table[:visibility].eq(3))
+ .limit(limit)
+ .order('statuses.id DESC')
+
+ # _to_me part requires mute and block filter.
+ # FIXME: may we check mutes.hide_notifications?
+ query_to_me = Status
+ .joins(:mentions)
+ .merge(Mention.where(account_id: account.id))
+ .where(Status.arel_table[:visibility].eq(3))
+ .limit(limit)
+ .order('mentions.status_id DESC')
+ .not_excluded_by_account(account)
+
+ if max_id.present?
+ query_from_me = query_from_me.where('statuses.id < ?', max_id)
+ query_to_me = query_to_me.where('mentions.status_id < ?', max_id)
+ end
+
+ if since_id.present?
+ query_from_me = query_from_me.where('statuses.id > ?', since_id)
+ query_to_me = query_to_me.where('mentions.status_id > ?', since_id)
+ end
+
+ if cache_ids
+ # returns array of cache_ids object that have id and updated_at
+ (query_from_me.cache_ids.to_a + query_to_me.cache_ids.to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
+ else
+ # returns ActiveRecord.Relation
+ items = (query_from_me.select(:id).to_a + query_to_me.select(:id).to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
+ Status.where(id: items.map(&:id))
+ end
+ end
+
def as_public_timeline(account = nil, local_only = false)
- query = timeline_scope(local_only).without_replies
+ query = timeline_scope(local_only)
+ query = query.without_replies unless Setting.show_replies_in_public_timelines
apply_timeline_filters(query, account, local_only)
end
%p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}
%button.status__content__spoiler-link= t('statuses.show_more')
- .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
- .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }
++ .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
- if status.preloadable_poll
= react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
open: Anyone can sign up
title: Registrations mode
show_known_fediverse_at_about_page:
- desc_html: When toggled, it will show toots from all the known fediverse on preview. Otherwise it will only show local toots.
- title: Show known fediverse on timeline preview
+ desc_html: When disabled, restricts the public timeline linked from the landing page to showing only local content
+ title: Include federated content on unauthenticated public timeline page
+ show_reblogs_in_public_timelines:
+ desc_html: Show public boosts of public toots in local and public timelines.
+ title: Show boosts in public timelines
+ show_replies_in_public_timelines:
+ desc_html: In addition to public self-replies (threads), show public replies in local and public timelines.
+ title: Show replies in public timelines
show_staff_badge:
desc_html: Show a staff badge on a user page
title: Show staff badge
end
end
- describe '.as_home_timeline' do
- let(:account) { Fabricate(:account) }
- let(:followed) { Fabricate(:account) }
- let(:not_followed) { Fabricate(:account) }
-
- before do
- Fabricate(:follow, account: account, target_account: followed)
-
- @self_status = Fabricate(:status, account: account, visibility: :public)
- @self_direct_status = Fabricate(:status, account: account, visibility: :direct)
- @followed_status = Fabricate(:status, account: followed, visibility: :public)
- @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
- @not_followed_status = Fabricate(:status, account: not_followed, visibility: :public)
-
- @results = Status.as_home_timeline(account)
- end
-
- it 'includes statuses from self' do
- expect(@results).to include(@self_status)
- end
-
- it 'does not include direct statuses from self' do
- expect(@results).to_not include(@self_direct_status)
- end
-
- it 'includes statuses from followed' do
- expect(@results).to include(@followed_status)
- end
-
- it 'does not include direct statuses mentioning recipient from followed' do
- Fabricate(:mention, account: account, status: @followed_direct_status)
- expect(@results).to_not include(@followed_direct_status)
- end
-
- it 'does not include direct statuses not mentioning recipient from followed' do
- expect(@results).not_to include(@followed_direct_status)
- end
-
- it 'does not include statuses from non-followed' do
- expect(@results).not_to include(@not_followed_status)
- end
- end
-
+ describe '.as_direct_timeline' do
+ let(:account) { Fabricate(:account) }
+ let(:followed) { Fabricate(:account) }
+ let(:not_followed) { Fabricate(:account) }
+
+ before do
+ Fabricate(:follow, account: account, target_account: followed)
+
+ @self_public_status = Fabricate(:status, account: account, visibility: :public)
+ @self_direct_status = Fabricate(:status, account: account, visibility: :direct)
+ @followed_public_status = Fabricate(:status, account: followed, visibility: :public)
+ @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
+ @not_followed_direct_status = Fabricate(:status, account: not_followed, visibility: :direct)
+
+ @results = Status.as_direct_timeline(account)
+ end
+
+ it 'does not include public statuses from self' do
+ expect(@results).to_not include(@self_public_status)
+ end
+
+ it 'includes direct statuses from self' do
+ expect(@results).to include(@self_direct_status)
+ end
+
+ it 'does not include public statuses from followed' do
+ expect(@results).to_not include(@followed_public_status)
+ end
+
+ it 'does not include direct statuses not mentioning recipient from followed' do
+ expect(@results).to_not include(@followed_direct_status)
+ end
+
+ it 'does not include direct statuses not mentioning recipient from non-followed' do
+ expect(@results).to_not include(@not_followed_direct_status)
+ end
+
+ it 'includes direct statuses mentioning recipient from followed' do
+ Fabricate(:mention, account: account, status: @followed_direct_status)
+ results2 = Status.as_direct_timeline(account)
+ expect(results2).to include(@followed_direct_status)
+ end
+
+ it 'includes direct statuses mentioning recipient from non-followed' do
+ Fabricate(:mention, account: account, status: @not_followed_direct_status)
+ results2 = Status.as_direct_timeline(account)
+ expect(results2).to include(@not_followed_direct_status)
+ end
+ end
+
describe '.as_public_timeline' do
it 'only includes statuses with public visibility' do
public_status = Fabricate(:status, visibility: :public)