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