Add: #62 ローカルタイムラインを無効にする管理者向け設定 (#179)

* Bump version to 8.0

* Add: 他のサーバーに公開する情報に、制限設定などを追加

* Fix: `quote_of_id`のインデックス

* Fix: #172 他のサーバーからの相乗り絵文字削除が反映されない

* Test: #166 リモートから自分の絵文字を受け取った時、ライセンスが上書きされないことを確認するテスト

* Add: #62 ローカルタイムラインを無効にする管理者設定(内部挙動のみ)

* Add: 画面部分を追加
This commit is contained in:
KMY(雪あすか) 2023-10-27 08:08:50 +09:00 committed by GitHub
parent ae865975d4
commit 1d8862712a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 238 additions and 16 deletions

View file

@ -27,6 +27,7 @@ module KmyblueCapabilitiesHelper
capabilities << :enable_wide_emoji_reaction
end
capabilities << :kmyblue_visibility_public_unlisted if Setting.enable_public_unlisted_visibility
capabilities << :timeline_no_local unless Setting.enable_local_timeline
capabilities
end
@ -58,6 +59,7 @@ module KmyblueCapabilitiesHelper
capabilities << :emoji_reaction
capabilities << :enable_wide_emoji_reaction
end
capabilities << :timeline_no_local unless Setting.enable_local_timeline
capabilities
end

View file

@ -25,6 +25,7 @@ const messages = defineMessages({
title: { id: 'column.about', defaultMessage: 'About' },
rules: { id: 'about.rules', defaultMessage: 'Server rules' },
blocks: { id: 'about.blocks', defaultMessage: 'Moderated servers' },
localTimeline: { id: 'column.community', defaultMessage: 'Local timeline' },
noop: { id: 'about.domain_blocks.noop.title', defaultMessage: 'Soft limited' },
noopExplanation: { id: 'about.domain_blocks.noop.explanation', defaultMessage: 'This server is limited partically.' },
silenced: { id: 'about.domain_blocks.silenced.title', defaultMessage: 'Limited' },
@ -133,6 +134,7 @@ class About extends PureComponent {
const fedibirdCapabilities = server.get('fedibird_capabilities') || []; // thinking about isLoading is true
const isPublicUnlistedVisibility = fedibirdCapabilities.includes('kmyblue_visibility_public_unlisted');
const isEmojiReaction = fedibirdCapabilities.includes('emoji_reaction');
const isLocalTimeline = !fedibirdCapabilities.includes('timeline_no_local');
return (
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
@ -204,6 +206,9 @@ class About extends PureComponent {
<li>
<span className='rules-list__text'>{intl.formatMessage(messages.publicUnlistedVisibility)}: {intl.formatMessage(isPublicUnlistedVisibility ? messages.enabled : messages.disabled)}</span>
</li>
<li>
<span className='rules-list__text'>{intl.formatMessage(messages.localTimeline)}: {intl.formatMessage(isLocalTimeline ? messages.enabled : messages.disabled)}</span>
</li>
</ol>
)}
</Section>

View file

@ -46,6 +46,7 @@ import ColumnHeader from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import { enableLocalTimeline } from 'mastodon/initial_state';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import RadioPanel from './components/radio_panel';
@ -401,7 +402,7 @@ class AntennaSetting extends PureComponent {
</button>
</div>
{!isLtl && (
{!isLtl && (enableLocalTimeline || isStl) && (
<div className='setting-toggle'>
<Toggle id={`antenna-${id}-stl`} defaultChecked={isStl} onChange={this.onStlToggle} />
<label htmlFor={`antenna-${id}-stl`} className='setting-toggle__label'>
@ -410,7 +411,7 @@ class AntennaSetting extends PureComponent {
</div>
)}
{!isStl && (
{!isStl && (enableLocalTimeline || isLtl) && (
<div className='setting-toggle'>
<Toggle id={`antenna-${id}-ltl`} defaultChecked={isLtl} onChange={this.onLtlToggle} />
<label htmlFor={`antenna-${id}-ltl`} className='setting-toggle__label'>

View file

@ -13,7 +13,7 @@ import { changeSetting } from 'mastodon/actions/settings';
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import initialState, { domain } from 'mastodon/initial_state';
import initialState, { domain, enableLocalTimeline } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import Column from '../../components/column';
@ -173,9 +173,11 @@ const Firehose = ({ feedType, multiColumn }) => {
</ColumnHeader>
<div className='account__section-headline'>
<NavLink exact to='/public/local'>
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
</NavLink>
{enableLocalTimeline && (
<NavLink exact to='/public/local'>
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
</NavLink>
)}
<NavLink exact to='/public/remote'>
<FormattedMessage tagName='div' id='firehose.remote' defaultMessage='Other servers' />

View file

@ -21,7 +21,7 @@ import { ReactComponent as AntennaIcon } from '@material-symbols/svg-600/outline
import { WordmarkLogo } from 'mastodon/components/logo';
import { NavigationPortal } from 'mastodon/components/navigation_portal';
import { enableDtlMenu, timelinePreview, trendsEnabled, dtlTag } from 'mastodon/initial_state';
import { enableDtlMenu, timelinePreview, trendsEnabled, dtlTag, enableLocalTimeline } from 'mastodon/initial_state';
import { transientSingleColumn } from 'mastodon/is_mobile';
import ColumnLink from './column_link';
@ -108,10 +108,13 @@ class NavigationPanel extends Component {
<>
<ColumnLink transparent to='/home' icon='home' iconComponent={HomeIcon} text={intl.formatMessage(messages.home)} />
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
<ColumnLink transparent to='/public/local/fixed' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.local)} />
</>
)}
{signedIn && enableLocalTimeline && (
<ColumnLink transparent to='/public/local/fixed' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.local)} />
)}
{signedIn && enableDtlMenu && dtlTag && (
<ColumnLink transparent to={`/tags/${dtlTag}`} icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.deepLocal)} />
)}
@ -123,7 +126,7 @@ class NavigationPanel extends Component {
)}
{(!signedIn && timelinePreview) && (
<ColumnLink transparent to='/public/local' isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
<ColumnLink transparent to={enableLocalTimeline ? '/public/local' : '/public'} isActive={this.isFirehoseActive} icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.firehose)} />
)}
{signedIn && (

View file

@ -62,6 +62,7 @@
* @property {boolean} enable_emoji_reaction
* @property {boolean} enable_login_privacy
* @property {boolean} enable_local_privacy
* @property {boolean} enable_local_timeline
* @property {boolean} enable_dtl_menu
* @property {boolean=} expand_spoilers
* @property {boolean} hide_blocking_quote
@ -136,6 +137,7 @@ export const domain = getMeta('domain');
export const dtlTag = getMeta('dtl_tag');
export const enableEmojiReaction = getMeta('enable_emoji_reaction');
export const enableLocalPrivacy = getMeta('enable_local_privacy');
export const enableLocalTimeline = getMeta('enable_local_timeline');
export const enableLoginPrivacy = getMeta('enable_login_privacy');
export const enableDtlMenu = getMeta('enable_dtl_menu');
export const expandSpoilers = getMeta('expand_spoilers');

View file

@ -51,6 +51,7 @@ class Form::AdminSettings
check_lts_version_only
enable_public_unlisted_visibility
unlocked_friend
enable_local_timeline
).freeze
INTEGER_KEYS = %i(
@ -81,6 +82,7 @@ class Form::AdminSettings
enable_public_unlisted_visibility
unlocked_friend
stranger_mention_from_local_ng
enable_local_timeline
).freeze
UPLOAD_KEYS = %i(

View file

@ -19,6 +19,8 @@ class PublicFeed
# @param [Integer] min_id
# @return [Array<Status>]
def get(limit, max_id = nil, since_id = nil, min_id = nil)
return [] if local_only? && !Setting.enable_local_timeline
scope = public_scope
scope.merge!(without_replies_scope) unless with_replies?

View file

@ -23,6 +23,8 @@ class TagFeed < PublicFeed
# @param [Integer] min_id
# @return [Array<Status>]
def get(limit, max_id = nil, since_id = nil, min_id = nil)
return [] if local_only? && !Setting.enable_local_timeline
scope = public_search_scope
scope.merge!(tagged_with_any_scope)

View file

@ -38,6 +38,7 @@ class InitialStateSerializer < ActiveModel::Serializer
sso_redirect: sso_redirect,
dtl_tag: DTL_ENABLED ? DTL_TAG : nil,
enable_local_privacy: Setting.enable_public_unlisted_visibility,
enable_local_timeline: Setting.enable_local_timeline,
}
if object.current_account

View file

@ -27,7 +27,8 @@ class DeliveryAntennaService
return if must_dtl_tag && !DTL_ENABLED
tag_ids = @status.tags.pluck(:id)
domain = @account.domain || Rails.configuration.x.local_domain
domain = @account.domain
domain ||= Rails.configuration.x.local_domain if Setting.enable_local_timeline
follower_ids = @status.unlisted_visibility? ? @status.account.followers.pluck(:id) : []
antennas = Antenna.availables
@ -77,6 +78,8 @@ class DeliveryAntennaService
end
def delivery_stl!
return unless Setting.enable_local_timeline
antennas = Antenna.available_stls
antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago))
@ -101,6 +104,7 @@ class DeliveryAntennaService
return if %i(public public_unlisted login).exclude?(@status.visibility.to_sym)
return unless @account.local?
return if @status.reblog?
return unless Setting.enable_local_timeline
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))

View file

@ -50,12 +50,12 @@ class FanOutOnWriteService < BaseService
deliver_to_all_followers!
deliver_to_lists!
deliver_to_antennas! if !@account.dissubscribable || (@status.dtl? && DTL_ENABLED && @account.user&.setting_dtl_force_subscribable && @status.tags.exists?(name: DTL_TAG))
deliver_to_stl_antennas!
deliver_to_ltl_antennas!
deliver_to_stl_antennas! if Setting.enable_local_timeline
deliver_to_ltl_antennas! if Setting.enable_local_timeline
when :limited
deliver_to_lists_mentioned_accounts_only!
deliver_to_antennas! unless @account.dissubscribable
deliver_to_stl_antennas!
deliver_to_stl_antennas! if Setting.enable_local_timeline
deliver_to_mentioned_followers!
else
deliver_to_mentioned_followers!
@ -152,7 +152,7 @@ class FanOutOnWriteService < BaseService
def broadcast_to_hashtag_streams!
@status.tags.map(&:name).each do |hashtag|
redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", anonymous_payload)
redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", anonymous_payload) if @status.local?
redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", anonymous_payload) if @status.local? && Setting.enable_local_timeline
end
end
@ -160,11 +160,13 @@ class FanOutOnWriteService < BaseService
return if @status.reply? && @status.in_reply_to_account_id != @account.id
redis.publish('timeline:public', anonymous_payload)
redis.publish(@status.local? ? 'timeline:public:local' : 'timeline:public:remote', anonymous_payload)
redis.publish('timeline:public:remote', anonymous_payload) unless @status.local?
redis.publish('timeline:public:local', anonymous_payload) if @status.local? && Setting.enable_local_timeline
if @status.with_media?
redis.publish('timeline:public:media', anonymous_payload)
redis.publish(@status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', anonymous_payload)
redis.publish('timeline:public:remote:media', anonymous_payload) unless @status.local?
redis.publish('timeline:public:local:media', anonymous_payload) if @status.local? && Setting.enable_local_timeline
end
end

View file

@ -45,6 +45,9 @@
.fields-group
= f.input :enable_public_unlisted_visibility, as: :boolean, wrapper: :with_label, kmyblue: true, hint: false
.fields-group
= f.input :enable_local_timeline, as: :boolean, wrapper: :with_label, kmyblue: true
%h4= t('admin.settings.discovery.friend_servers')
.fields-group

View file

@ -98,6 +98,7 @@ en:
closed_registrations_message: Displayed when sign-ups are closed
content_cache_retention_period: All posts and boosts from other servers will be deleted after the specified number of days. Some posts may not be recoverable. All related bookmarks, favourites and boosts will also be lost and impossible to undo.
custom_css: You can apply custom styles on the web version of Mastodon.
enable_local_timeline: While enabling this feature will allow for interaction between like-minded users, it may also strengthen the internal atmosphere; since Mastodon is supposed to have a local timeline, we recommend annotating it in the server introduction if it is to be disabled.
enable_public_unlisted_visibility: If true, your community maybe closed-minded. If turn it false, strongly recommend that you disclose that you have disabled this setting!
mascot: Overrides the illustration in the advanced web interface.
media_cache_retention_period: Downloaded media files will be deleted after the specified number of days when set to a positive value, and re-downloaded on demand.
@ -336,6 +337,7 @@ en:
content_cache_retention_period: Content cache retention period
custom_css: Custom CSS
enable_emoji_reaction: Enable stamp function
enable_local_timeline: Enable local timeline
enable_public_unlisted_visibility: Enable public-unlisted visibility
mascot: Custom mascot (legacy)
media_cache_retention_period: Media cache retention period

View file

@ -112,6 +112,7 @@ ja:
closed_registrations_message: アカウント作成を停止している時に表示されます
content_cache_retention_period: 指定した日数が経過した他のサーバーの投稿とブーストを削除します。削除された投稿は再取得できない場合があります。削除された投稿についたブックマークやお気に入り、ブーストも失われ、元に戻せません。
custom_css: ウェブ版のMastodonでカスタムスタイルを適用できます。
enable_local_timeline: 有効にすると気の合ったユーザー同士の交流が捗る反面、内輪の雰囲気が強くなるかもしれません。Mastodonはローカルタイムラインがあるものだと思われているので、無効にする場合はサーバー紹介での注記をおすすめします。
enable_public_unlisted_visibility: 有効にするとあなたのコミュニティは閉鎖的になるかもしれません。この設定はkmyblueの主要機能の1つであり、無効にする場合は概要などに記載することを強くおすすめします。
mascot: 上級者向けWebインターフェースのイラストを上書きします。
media_cache_retention_period: 正の値に設定されている場合、ダウンロードされたメディアファイルは指定された日数の後に削除され、リクエストに応じて再ダウンロードされます。
@ -352,6 +353,7 @@ ja:
content_cache_retention_period: コンテンツキャッシュの保持期間
custom_css: カスタムCSS
enable_emoji_reaction: スタンプ機能を有効にする
enable_local_timeline: ローカルタイムラインを有効にする
enable_public_unlisted_visibility: 公開範囲「ローカル公開」を有効にする
mascot: カスタムマスコット(レガシー)
media_cache_retention_period: メディアキャッシュの保持期間

View file

@ -45,6 +45,7 @@ defaults: &defaults
enable_public_unlisted_visibility: true
unlocked_friend: false
stranger_mention_from_local_ng: true
enable_local_timeline: true
development:
<<: *defaults

View file

@ -130,6 +130,16 @@ RSpec.describe PublicFeed do
it 'includes public_unlisted statuses' do
expect(subject).to include(public_unlisted_status.id)
end
context 'when local timeline is disabled' do
before do
Form::AdminSettings.new(enable_local_timeline: '0').save
end
it 'does not include all statuses' do
expect(subject).to eq []
end
end
end
context 'with a viewer' do
@ -150,6 +160,16 @@ RSpec.describe PublicFeed do
expect(subject).to include(public_unlisted_status.id)
end
end
context 'when local timeline is disabled' do
before do
Form::AdminSettings.new(enable_local_timeline: '0').save
end
it 'does not include all statuses' do
expect(subject).to eq []
end
end
end
context 'with a remote_only option set' do

View file

@ -59,6 +59,12 @@ describe TagFeed, type: :service do
expect(results).to_not include status_tagged_with_cats
end
it 'can restrict to local but local timeline is disabled' do
Form::AdminSettings.new(enable_local_timeline: '0').save
results = described_class.new(tag_cats, nil, any: [tag_dogs.name], local: true).get(20)
expect(results).to_not include status_tagged_with_cats
end
it 'allows replies to be included' do
original = Fabricate(:status)
status = Fabricate(:status, tags: [tag_cats], in_reply_to_id: original.id)

View file

@ -7,9 +7,12 @@ describe 'Public' do
let(:scopes) { 'read:statuses' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
let(:ltl_enabled) { true }
shared_examples 'a successful request to the public timeline' do
it 'returns the expected statuses successfully', :aggregate_failures do
Form::AdminSettings.new(enable_local_timeline: '0').save unless ltl_enabled
subject
expect(response).to have_http_status(200)
@ -47,6 +50,13 @@ describe 'Public' do
let(:expected_statuses) { [local_status, media_status] }
it_behaves_like 'a successful request to the public timeline'
context 'when local timeline is disabled' do
let(:expected_statuses) { [] }
let(:ltl_enabled) { false }
it_behaves_like 'a successful request to the public timeline'
end
end
context 'with remote param' do
@ -54,6 +64,12 @@ describe 'Public' do
let(:expected_statuses) { [remote_status] }
it_behaves_like 'a successful request to the public timeline'
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it_behaves_like 'a successful request to the public timeline'
end
end
context 'with local and remote params' do
@ -61,6 +77,12 @@ describe 'Public' do
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'a successful request to the public timeline'
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it_behaves_like 'a successful request to the public timeline'
end
end
context 'with only_media param' do

View file

@ -5,6 +5,8 @@ require 'rails_helper'
RSpec.describe DeliveryAntennaService, type: :service do
subject { described_class.new }
let(:ltl_enabled) { true }
let(:last_active_at) { Time.now.utc }
let(:last_active_at_tom) { Time.now.utc }
let(:visibility) { :public }
@ -36,6 +38,8 @@ RSpec.describe DeliveryAntennaService, type: :service do
bob.follow!(alice)
alice.block!(ohagi)
Form::AdminSettings.new(enable_local_timeline: '0').save unless ltl_enabled
allow(redis).to receive(:publish)
subject.call(status, false, mode: mode)
@ -124,6 +128,29 @@ RSpec.describe DeliveryAntennaService, type: :service do
end
end
context 'with local domain' do
let(:domain) { nil }
let!(:antenna) { antenna_with_domain(bob, 'cb6e6126.ngrok.io') }
let!(:empty_antenna) { antenna_with_domain(tom, 'ohagi.example.com') }
it 'detecting antenna' do
expect(antenna_feed_of(antenna)).to include status.id
end
it 'not detecting antenna' do
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'not detecting antenna' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
context 'with tag' do
let!(:antenna) { antenna_with_tag(bob, 'hoge') }
let!(:empty_antenna) { antenna_with_tag(tom, 'hog') }

View file

@ -5,6 +5,8 @@ require 'rails_helper'
RSpec.describe FanOutOnWriteService, type: :service do
subject { described_class.new }
let(:ltl_enabled) { true }
let(:last_active_at) { Time.now.utc }
let(:visibility) { 'public' }
let(:searchability) { 'public' }
@ -28,6 +30,8 @@ RSpec.describe FanOutOnWriteService, type: :service do
tom.follow!(alice)
ohagi.follow!(bob)
Form::AdminSettings.new(enable_local_timeline: '0').save unless ltl_enabled
ProcessMentionsService.new.call(status)
ProcessHashtagsService.new.call(status)
@ -86,6 +90,20 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(redis).to have_received(:publish).with('timeline:public:local', anything)
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is broadcast to the hashtag stream' do
expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything)
expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge:local', anything)
end
it 'is broadcast to the public stream' do
expect(redis).to have_received(:publish).with('timeline:public', anything)
expect(redis).to_not have_received(:publish).with('timeline:public:local', anything)
end
end
context 'with list' do
let!(:list) { list_with_account(bob, alice) }
let!(:empty_list) { Fabricate(:list, account: tom) }
@ -130,6 +148,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
end
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
context 'with LTL antenna' do
@ -148,6 +175,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
end
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
end
@ -255,6 +291,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
context 'with LTL antenna' do
@ -263,6 +308,14 @@ RSpec.describe FanOutOnWriteService, type: :service do
it 'is added to the antenna feed of antenna follower' do
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
end
@ -284,6 +337,20 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(redis).to have_received(:publish).with('timeline:public', anything)
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is broadcast to the hashtag stream' do
expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything)
expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge:local', anything)
end
it 'is broadcast to the public stream' do
expect(redis).to have_received(:publish).with('timeline:public', anything)
expect(redis).to_not have_received(:publish).with('timeline:public:local', anything)
end
end
context 'with list' do
let!(:list) { list_with_account(bob, alice) }
let!(:empty_list) { list_with_account(ohagi, bob) }
@ -328,6 +395,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
end
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
context 'with LTL antenna' do
@ -346,6 +422,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
end
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
end
@ -384,6 +469,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
end
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is broadcast to the hashtag stream' do
expect(redis).to have_received(:publish).with('timeline:hashtag:hoge', anything)
expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge:local', anything)
end
end
context 'with list' do
let!(:list) { list_with_account(bob, alice) }
let!(:empty_list) { list_with_account(ohagi, bob) }
@ -412,6 +506,15 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(antenna_feed_of(antenna)).to include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(antenna)).to_not include status.id
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
context 'with LTL antenna' do
@ -420,6 +523,14 @@ RSpec.describe FanOutOnWriteService, type: :service do
it 'is added to the antenna feed of antenna follower' do
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
context 'when local timeline is disabled' do
let(:ltl_enabled) { false }
it 'is not added to the antenna feed of antenna follower' do
expect(antenna_feed_of(empty_antenna)).to_not include status.id
end
end
end
context 'with non-public searchability' do