1
0
Fork 0
forked from gitea/nas

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

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

* Fix test partically

* Complete
This commit is contained in:
KMY(雪あすか) 2024-04-18 12:40:18 +09:00 committed by GitHub
parent 7f9ec2e510
commit f79fb3adae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 177 additions and 59 deletions

View file

@ -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) => {

View file

@ -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';
@ -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';
@ -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.' },
});
@ -131,7 +134,12 @@ 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) },
@ -139,31 +147,28 @@ class PrivacyDropdown extends PureComponent {
{ 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 => {
@ -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}>
@ -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}

View file

@ -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({
@ -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');
}
}

View file

@ -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
@ -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') || [];

View file

@ -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",

View file

@ -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": "サークル (投稿時点)",

View file

@ -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';
@ -143,9 +143,6 @@ 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);
}
@ -153,7 +150,7 @@ function clearAll(state) {
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'));
@ -163,6 +160,7 @@ function clearAll(state) {
map.update('media_attachments', list => list.clear());
map.set('poll', null);
map.set('idempotencyKey', uuid());
normalizePrivacy(map);
});
}
@ -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;
@ -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);
@ -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());
@ -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)));

View file

@ -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 {