Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: #483 特定の公開範囲を無効にするオプション #712

Merged
merged 4 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/controllers/settings/preferences/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ def after_update_redirect_path
end

def user_params
original_user_params.tap do |params|
params[:settings_attributes]&.merge!(disabled_visibilities_params[:settings_attributes] || {})
end
end

def original_user_params
params.require(:user).permit(:locale, :time_zone, chosen_languages: [], settings_attributes: UserSettings.keys)
end

def disabled_visibilities_params
params.require(:user).permit(settings_attributes: { enabled_visibilities: [] })
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class ComposeForm extends ImmutablePureComponent {
const fulltext = this.getFulltextForCharacterCounting();
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;

return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxChars || (isOnlyWhitespace && !anyMedia) || (privacy === 'circle' && !isEditing && !circleId));
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxChars || (isOnlyWhitespace && !anyMedia) || (privacy === 'circle' && !isEditing && !circleId) || privacy === 'banned');
};

handleSubmit = (e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Overlay from 'react-overlays/Overlay';

import CircleIcon from '@/material-icons/400-24px/account_circle.svg?react';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
import PublicUnlistedIcon from '@/material-icons/400-24px/cloud.svg?react';
import MutualIcon from '@/material-icons/400-24px/compare_arrows.svg?react';
import LoginIcon from '@/material-icons/400-24px/key.svg?react';
Expand All @@ -18,7 +19,7 @@ import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import LimitedIcon from '@/material-icons/400-24px/shield.svg?react';
import { Icon } from 'mastodon/components/icon';
import { enableLoginPrivacy, enableLocalPrivacy, enablePublicPrivacy } from 'mastodon/initial_state';
import { enabledVisibilites } from 'mastodon/initial_state';

import { PrivacyDropdownMenu } from './privacy_dropdown_menu';

Expand All @@ -42,6 +43,8 @@ const messages = defineMessages({
reply_long: { id: 'privacy.reply.long', defaultMessage: 'Reply to limited post' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' },
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Everyone mentioned in the post' },
banned_short: { id: 'privacy.banned.short', defaultMessage: 'No posting' },
banned_long: { id: 'privacy.banned.long', defaultMessage: 'All public range submissions are disabled. User settings need to be modified.' },
change_privacy: { id: 'privacy.change', defaultMessage: 'Change post privacy' },
unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.' },
});
Expand Down Expand Up @@ -131,39 +134,41 @@ class PrivacyDropdown extends PureComponent {
UNSAFE_componentWillMount () {
const { intl: { formatMessage } } = this.props;

this.options = [
this.dynamicOptions = [
{ icon: 'reply', iconComponent: ReplyIcon, value: 'reply', text: formatMessage(messages.reply_short), meta: formatMessage(messages.reply_long), extra: formatMessage(messages.limited_short), extraIcomComponent: LimitedIcon },
{ icon: 'ban', iconComponent: BlockIcon, value: 'banned', text: formatMessage(messages.banned_short), meta: formatMessage(messages.banned_long) },
];

this.originalOptions = [
{ icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
{ icon: 'cloud', iconComponent: PublicUnlistedIcon, value: 'public_unlisted', text: formatMessage(messages.public_unlisted_short), meta: formatMessage(messages.public_unlisted_long) },
{ icon: 'key', iconComponent: LoginIcon, value: 'login', text: formatMessage(messages.login_short), meta: formatMessage(messages.login_long) },
{ icon: 'unlock', iconComponent: QuietTimeIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long), extra: formatMessage(messages.unlisted_extra) },
{ icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
{ icon: 'exchange', iconComponent: MutualIcon, value: 'mutual', text: formatMessage(messages.mutual_short), meta: formatMessage(messages.mutual_long), extra: formatMessage(messages.limited_short), extraIcomComponent: LimitedIcon },
{ icon: 'user-circle', iconComponent: CircleIcon, value: 'circle', text: formatMessage(messages.circle_short), meta: formatMessage(messages.circle_long), extra: formatMessage(messages.limited_short), extraIcomComponent: LimitedIcon },
{ icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
...this.dynamicOptions,
];

if (!this.props.noDirect) {
this.options.push(
{ icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
);
this.options = [...this.originalOptions];

if (this.props.noDirect) {
this.options = this.options.filter((opt) => opt.value !== 'direct');
}

if (this.props.noLimited) {
this.options = this.options.filter((opt) => !['mutual', 'circle'].includes(opt.value));
}

if (!enableLoginPrivacy) {
this.options = this.options.filter((opt) => opt.value !== 'login');
if (enabledVisibilites) {
this.options = this.options.filter((opt) => enabledVisibilites.includes(opt.value));
}

if (!enableLocalPrivacy) {
this.options = this.options.filter((opt) => opt.value !== 'public_unlisted');
if (this.options.length === 0) {
this.options.push(this.dynamicOptions.find((opt) => opt.value === 'banned'));
this.props.onChange('banned');
}

if (!enablePublicPrivacy) {
this.options = this.options.filter((opt) => opt.value !== 'public');
}

this.selectableOptions = [...this.options];
}

setTargetRef = c => {
Expand All @@ -183,18 +188,16 @@ class PrivacyDropdown extends PureComponent {
const { open, placement } = this.state;

if (replyToLimited) {
if (!this.selectableOptions.some((op) => op.value === 'reply')) {
this.selectableOptions.unshift(
{ icon: 'reply', iconComponent: ReplyIcon, value: 'reply', text: intl.formatMessage(messages.reply_short), meta: intl.formatMessage(messages.reply_long), extra: intl.formatMessage(messages.limited_short), extraIcomComponent: LimitedIcon },
);
if (!this.options.some((op) => op.value === 'reply')) {
this.options.unshift(this.dynamicOptions.find((opt) => opt.value === 'reply'));
}
} else {
if (this.selectableOptions.some((op) => op.value === 'reply')) {
this.selectableOptions = this.selectableOptions.filter((op) => op.value !== 'reply');
if (this.options.some((op) => op.value === 'reply')) {
this.options = this.options.filter((op) => op.value !== 'reply');
}
}

const valueOption = this.selectableOptions.find(item => item.value === value) || this.selectableOptions[0];
const valueOption = (disabled ? this.originalOptions : this.options).find(item => item.value === value) || this.options[0];

return (
<div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}>
Expand All @@ -217,7 +220,7 @@ class PrivacyDropdown extends PureComponent {
<div {...props}>
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
<PrivacyDropdownMenu
items={this.selectableOptions}
items={this.options}
value={value}
onClose={this.handleClose}
onChange={this.handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import LockOpenIcon from '@/material-icons/400-24px/no_encryption.svg?react';
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { Icon } from 'mastodon/components/icon';
import { enableLocalPrivacy } from 'mastodon/initial_state';
import { enabledVisibilites } from 'mastodon/initial_state';


const messages = defineMessages({
Expand Down Expand Up @@ -236,7 +236,7 @@ class SearchabilityDropdown extends PureComponent {
{ icon: 'at', iconComponent: AlternateEmailIcon, value: 'limited', text: formatMessage(messages.limited_short), meta: formatMessage(messages.limited_long) },
];

if (!enableLocalPrivacy) {
if (!enabledVisibilites.includes('public_unlisted')) {
this.options = this.options.filter((opt) => opt.value !== 'public_unlisted');
}
}
Expand Down
8 changes: 2 additions & 6 deletions app/javascript/mastodon/initial_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,14 @@
* @property {boolean=} delete_modal
* @property {boolean=} disable_swiping
* @property {string=} disabled_account_id
* @property {string[]} enabled_visibilities
* @property {string} display_media
* @property {boolean} display_media_expand
* @property {string} domain
* @property {string} dtl_tag
* @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} enable_public_privacy
* @property {boolean=} expand_spoilers
* @property {string[]} featured_tags
* @property {HideItemsDefinition[]} hide_items
Expand Down Expand Up @@ -121,15 +119,13 @@ export const boostModal = getMeta('boost_modal');
export const deleteModal = getMeta('delete_modal');
export const disableSwiping = getMeta('disable_swiping');
export const disabledAccountId = getMeta('disabled_account_id');
export const enabledVisibilites = getMeta('enabled_visibilities');
export const displayMedia = getMeta('display_media');
export const displayMediaExpand = getMeta('display_media_expand');
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 enablePublicPrivacy = getMeta('enable_public_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');
export const featuredTags = getMeta('featured_tags') || [];
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/mastodon/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@
"poll.votes": "{votes, plural, one {# vote} other {# votes}}",
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"privacy.banned.long": "All public range submissions are disabled. User settings need to be modified.",
"privacy.banned.short": "No posting",
"privacy.change": "Change post privacy",
"privacy.circle.long": "Circle members only",
"privacy.circle.short": "Circle",
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/mastodon/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,8 @@
"poll.votes": "{votes}票",
"poll_button.add_poll": "アンケートを追加",
"poll_button.remove_poll": "アンケートを削除",
"privacy.banned.long": "全ての公開範囲が、ユーザー設定により無効になっています",
"privacy.banned.short": "投稿不可",
"privacy.change": "公開範囲を変更",
"privacy.circle.long": "サークルメンバーのみ閲覧可",
"privacy.circle.short": "サークル (投稿時点)",
Expand Down
28 changes: 23 additions & 5 deletions app/javascript/mastodon/reducers/compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ import {
import { REDRAFT } from '../actions/statuses';
import { STORE_HYDRATE } from '../actions/store';
import { TIMELINE_DELETE } from '../actions/timelines';
import { enableLocalPrivacy, enableLoginPrivacy, enablePublicPrivacy, me } from '../initial_state';
import { enabledVisibilites, me } from '../initial_state';
import { unescapeHTML } from '../utils/html';
import { uuid } from '../uuid';

Expand Down Expand Up @@ -143,17 +143,14 @@ function clearAll(state) {
if (state.get('stay_privacy') && !state.get('in_reply_to')) {
map.set('default_privacy', state.get('privacy'));
}
if ((map.get('privacy') === 'login' && !enableLoginPrivacy) || (map.get('privacy') === 'public_unlisted' && !enableLocalPrivacy)) {
map.set('privacy', enablePublicPrivacy ? 'public' : 'unlisted');
}
if (!state.get('in_reply_to')) {
map.set('posted_on_this_session', true);
}
map.set('reply_to_limited', false);
map.set('limited_scope', null);
map.set('id', null);
map.set('in_reply_to', null);
if (state.get('default_searchability') === 'public_unlisted' && !enableLocalPrivacy) {
if (state.get('default_searchability') === 'public_unlisted' && !enabledVisibilites.includes('public_unlisted')) {
map.set('searchability', 'public');
} else {
map.set('searchability', state.get('default_searchability'));
Expand All @@ -163,6 +160,7 @@ function clearAll(state) {
map.update('media_attachments', list => list.clear());
map.set('poll', null);
map.set('idempotencyKey', uuid());
normalizePrivacy(map);
});
}

Expand Down Expand Up @@ -243,6 +241,22 @@ const sortHashtagsByUse = (state, tags) => {
return sorted;
};

const normalizePrivacy = (map, last) => {
if (!enabledVisibilites) {
return;
}

const current = map.get('privacy');
const invalid = !enabledVisibilites.includes(current);

if (invalid) {
if (enabledVisibilites.length > 0) {
const index = last ? enabledVisibilites.length - 1 : 0;
map.set('privacy', enabledVisibilites[index]);
}
}
};

const insertEmoji = (state, position, emojiData, needsSpace) => {
const oldText = state.get('text');
const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native;
Expand Down Expand Up @@ -467,6 +481,8 @@ export default function compose(state = initialState, action) {
map.set('spoiler', false);
map.set('spoiler_text', '');
}

normalizePrivacy(map);
});
case COMPOSE_SUBMIT_REQUEST:
return state.set('is_submitting', true);
Expand Down Expand Up @@ -532,6 +548,7 @@ export default function compose(state = initialState, action) {
return state.withMutations(map => {
map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' '));
map.set('privacy', 'direct');
normalizePrivacy(map, true);
map.set('focusDate', new Date());
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
Expand Down Expand Up @@ -584,6 +601,7 @@ export default function compose(state = initialState, action) {
map.set('text', action.raw_text || unescapeHTML(expandMentions(action.status)));
map.set('in_reply_to', action.status.get('in_reply_to_id'));
map.set('privacy', action.status.get('visibility_ex'));
normalizePrivacy(map);
map.set('reply_to_limited', action.status.get('limited_scope') === 'reply');
map.set('limited_scope', null);
map.set('media_attachments', action.status.get('media_attachments').map((media) => media.set('unattached', true)));
Expand Down
9 changes: 7 additions & 2 deletions app/javascript/styles/mastodon/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,18 @@ code {

.overridden,
.recommended,
.not_recommended,
.kmyblue {
.not_recommended {
position: absolute;
margin: 0 4px;
margin-top: -2px;
}
}

.kmyblue {
position: absolute;
margin: 0 4px;
margin-top: -2px;
}
}

.row {
Expand Down
12 changes: 8 additions & 4 deletions app/models/concerns/user/has_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ def setting_reaction_deck
settings['reaction_deck']
end

def setting_enable_login_privacy
settings['web.enable_login_privacy']
end

def setting_enable_dtl_menu
settings['web.enable_dtl_menu']
end
Expand Down Expand Up @@ -235,6 +231,14 @@ def setting_default_reblog_privacy
settings['default_reblog_privacy'] || 'unset'
end

def setting_enabled_visibilities
settings['enabled_visibilities']
end

def setting_disabled_visibilities
settings['disabled_visibilities']
end

def setting_default_searchability
settings['default_searchability'] || 'direct'
end
Expand Down
6 changes: 5 additions & 1 deletion app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,11 @@ def searchable_visibility

class << self
def selectable_visibilities
vs = visibilities.keys - %w(direct limited)
selectable_all_visibilities - %w(mutual circle reply direct)
end

def selectable_all_visibilities
vs = %w(public public_unlisted login unlisted private mutual circle reply direct)
vs -= %w(public_unlisted) unless Setting.enable_public_unlisted_visibility
vs -= %w(public) unless Setting.enable_public_visibility
vs
Expand Down
3 changes: 2 additions & 1 deletion app/models/user_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class KeyError < Error; end
setting :default_privacy, default: nil, in: %w(public public_unlisted login unlisted private)
setting :stay_privacy, default: false
setting :default_reblog_privacy, default: nil
setting :disabled_visibilities, default: %w()
setting :default_searchability, default: :direct, in: %w(public private direct limited public_unlisted)
setting :default_searchability_of_search, default: :public, in: %w(public private direct limited)
setting :use_public_index, default: true
Expand All @@ -47,6 +48,7 @@ class KeyError < Error; end
setting_inverse_alias :show_statuses_count, :hide_statuses_count
setting_inverse_alias :show_following_count, :hide_following_count
setting_inverse_alias :show_followers_count, :hide_followers_count
setting_inverse_array :enabled_visibilities, :disabled_visibilities, %w(public public_unlisted login unlisted private mutual circle reply personal direct)

namespace :web do
setting :advanced_layout, default: false
Expand All @@ -57,7 +59,6 @@ class KeyError < Error; end
setting :bookmark_category_needed, default: false
setting :disable_swiping, default: false
setting :delete_modal, default: true
setting :enable_login_privacy, default: false
setting :enable_dtl_menu, default: false
setting :hide_recent_emojis, default: false
setting :enable_emoji_reaction, default: true
Expand Down
4 changes: 4 additions & 0 deletions app/models/user_settings/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def setting_inverse_alias(key, original_key)
@definitions[key] = @definitions[original_key].inverse_of(key)
end

def setting_inverse_array(key, original_key, reverse_array)
@definitions[key] = @definitions[original_key].array_inverse_of(key, reverse_array)
end

def namespace(key, &block)
@definitions ||= {}

Expand Down
Loading
Loading