Merge pull request #734 from kmycode/upstream-20240426
Upstream 20240426
This commit is contained in:
commit
081a9f5e18
22 changed files with 389 additions and 146 deletions
|
@ -133,9 +133,6 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
|
||||||
if (notification.status) {
|
if (notification.status) {
|
||||||
dispatch(importFetchedStatus(notification.status));
|
dispatch(importFetchedStatus(notification.status));
|
||||||
}
|
}
|
||||||
if (notification.statuses) {
|
|
||||||
dispatch(importFetchedStatuses(notification.statuses));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notification.report) {
|
if (notification.report) {
|
||||||
dispatch(importFetchedAccount(notification.report.target_account));
|
dispatch(importFetchedAccount(notification.report.target_account));
|
||||||
|
@ -182,7 +179,6 @@ const excludeTypesFromFilter = filter => {
|
||||||
'status',
|
'status',
|
||||||
'list_status',
|
'list_status',
|
||||||
'update',
|
'update',
|
||||||
'account_warning',
|
|
||||||
'admin.sign_up',
|
'admin.sign_up',
|
||||||
'admin.report',
|
'admin.report',
|
||||||
]);
|
]);
|
||||||
|
@ -241,10 +237,7 @@ export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data.map(item => item.account)));
|
dispatch(importFetchedAccounts(response.data.map(item => item.account)));
|
||||||
dispatch(importFetchedStatuses(
|
dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
|
||||||
response.data.map(item => item.status).filter(status => !!status)
|
|
||||||
.concat(response.data.flatMap(item => item.statuses || []))
|
|
||||||
));
|
|
||||||
dispatch(importFetchedAccounts(response.data.filter(item => item.report).map(item => item.report.target_account)));
|
dispatch(importFetchedAccounts(response.data.filter(item => item.report).map(item => item.report.target_account)));
|
||||||
|
|
||||||
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems));
|
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems));
|
||||||
|
|
88
app/javascript/mastodon/features/explore/components/card.jsx
Normal file
88
app/javascript/mastodon/features/explore/components/card.jsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
import { followAccount, unfollowAccount } from 'mastodon/actions/accounts';
|
||||||
|
import { dismissSuggestion } from 'mastodon/actions/suggestions';
|
||||||
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
|
import { Button } from 'mastodon/components/button';
|
||||||
|
import { DisplayName } from 'mastodon/components/display_name';
|
||||||
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
import { domain } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
|
dismiss: { id: 'follow_suggestions.dismiss', defaultMessage: "Don't show again" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Card = ({ id, source }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const account = useSelector(state => state.getIn(['accounts', id]));
|
||||||
|
const relationship = useSelector(state => state.getIn(['relationships', id]));
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const following = relationship?.get('following') ?? relationship?.get('requested');
|
||||||
|
|
||||||
|
const handleFollow = useCallback(() => {
|
||||||
|
if (following) {
|
||||||
|
dispatch(unfollowAccount(id));
|
||||||
|
} else {
|
||||||
|
dispatch(followAccount(id));
|
||||||
|
}
|
||||||
|
}, [id, following, dispatch]);
|
||||||
|
|
||||||
|
const handleDismiss = useCallback(() => {
|
||||||
|
dispatch(dismissSuggestion(id));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
let label;
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case 'friends_of_friends':
|
||||||
|
label = <FormattedMessage id='follow_suggestions.friends_of_friends_longer' defaultMessage='Popular among people you follow' />;
|
||||||
|
break;
|
||||||
|
case 'similar_to_recently_followed':
|
||||||
|
label = <FormattedMessage id='follow_suggestions.similar_to_recently_followed_longer' defaultMessage='Similar to profiles you recently followed' />;
|
||||||
|
break;
|
||||||
|
case 'featured':
|
||||||
|
label = <FormattedMessage id='follow_suggestions.featured_longer' defaultMessage='Hand-picked by the {domain} team' values={{ domain }} />;
|
||||||
|
break;
|
||||||
|
case 'most_followed':
|
||||||
|
label = <FormattedMessage id='follow_suggestions.popular_suggestion_longer' defaultMessage='Popular on {domain}' values={{ domain }} />;
|
||||||
|
break;
|
||||||
|
case 'most_interactions':
|
||||||
|
label = <FormattedMessage id='follow_suggestions.popular_suggestion_longer' defaultMessage='Popular on {domain}' values={{ domain }} />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='explore__suggestions__card'>
|
||||||
|
<div className='explore__suggestions__card__source'>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='explore__suggestions__card__body'>
|
||||||
|
<Link to={`/@${account.get('acct')}`}><Avatar account={account} size={48} /></Link>
|
||||||
|
|
||||||
|
<div className='explore__suggestions__card__body__main'>
|
||||||
|
<div className='explore__suggestions__card__body__main__name-button'>
|
||||||
|
<Link className='explore__suggestions__card__body__main__name-button__name' to={`/@${account.get('acct')}`}><DisplayName account={account} /></Link>
|
||||||
|
<IconButton iconComponent={CloseIcon} onClick={handleDismiss} title={intl.formatMessage(messages.dismiss)} />
|
||||||
|
<Button text={intl.formatMessage(following ? messages.unfollow : messages.follow)} secondary={following} onClick={handleFollow} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Card.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
source: PropTypes.oneOf(['friends_of_friends', 'similar_to_recently_followed', 'featured', 'most_followed', 'most_interactions']),
|
||||||
|
};
|
|
@ -10,9 +10,10 @@ import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { fetchSuggestions } from 'mastodon/actions/suggestions';
|
import { fetchSuggestions } from 'mastodon/actions/suggestions';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import AccountCard from 'mastodon/features/directory/components/account_card';
|
|
||||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
|
import { Card } from './components/card';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
suggestions: state.getIn(['suggestions', 'items']),
|
suggestions: state.getIn(['suggestions', 'items']),
|
||||||
isLoading: state.getIn(['suggestions', 'isLoading']),
|
isLoading: state.getIn(['suggestions', 'isLoading']),
|
||||||
|
@ -54,7 +55,11 @@ class Suggestions extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='explore__suggestions scrollable' data-nosnippet>
|
<div className='explore__suggestions scrollable' data-nosnippet>
|
||||||
{isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => (
|
{isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => (
|
||||||
<AccountCard key={suggestion.get('account')} id={suggestion.get('account')} />
|
<Card
|
||||||
|
key={suggestion.get('account')}
|
||||||
|
id={suggestion.get('account')}
|
||||||
|
source={suggestion.getIn(['sources', 0])}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import WarningIcon from '@/material-icons/400-24px/warning-fill.svg?react';
|
||||||
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
|
// This needs to be kept in sync with app/models/account_warning.rb
|
||||||
|
const messages = defineMessages({
|
||||||
|
none: {
|
||||||
|
id: 'notification.moderation_warning.action_none',
|
||||||
|
defaultMessage: 'Your account has received a moderation warning.',
|
||||||
|
},
|
||||||
|
disable: {
|
||||||
|
id: 'notification.moderation_warning.action_disable',
|
||||||
|
defaultMessage: 'Your account has been disabled.',
|
||||||
|
},
|
||||||
|
force_cw: {
|
||||||
|
id: 'notification.moderation_warning.action_force_cw',
|
||||||
|
defaultMessage: 'Some of your posts have been added content-warning text.',
|
||||||
|
},
|
||||||
|
mark_statuses_as_sensitive: {
|
||||||
|
id: 'notification.moderation_warning.action_mark_statuses_as_sensitive',
|
||||||
|
defaultMessage: 'Some of your posts have been marked as sensitive.',
|
||||||
|
},
|
||||||
|
delete_statuses: {
|
||||||
|
id: 'notification.moderation_warning.action_delete_statuses',
|
||||||
|
defaultMessage: 'Some of your posts have been removed.',
|
||||||
|
},
|
||||||
|
sensitive: {
|
||||||
|
id: 'notification.moderation_warning.action_sensitive',
|
||||||
|
defaultMessage: 'Your posts will be marked as sensitive from now on.',
|
||||||
|
},
|
||||||
|
silence: {
|
||||||
|
id: 'notification.moderation_warning.action_silence',
|
||||||
|
defaultMessage: 'Your account has been limited.',
|
||||||
|
},
|
||||||
|
suspend: {
|
||||||
|
id: 'notification.moderation_warning.action_suspend',
|
||||||
|
defaultMessage: 'Your account has been suspended.',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
action:
|
||||||
|
| 'none'
|
||||||
|
| 'disable'
|
||||||
|
| 'force_cw'
|
||||||
|
| 'mark_statuses_as_sensitive'
|
||||||
|
| 'delete_statuses'
|
||||||
|
| 'sensitive'
|
||||||
|
| 'silence'
|
||||||
|
| 'suspend';
|
||||||
|
id: string;
|
||||||
|
hidden: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ModerationWarning: React.FC<Props> = ({ action, id, hidden }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={`/disputes/strikes/${id}`}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
className='notification__moderation-warning'
|
||||||
|
>
|
||||||
|
<Icon id='warning' icon={WarningIcon} />
|
||||||
|
|
||||||
|
<div className='notification__moderation-warning__content'>
|
||||||
|
<p>{intl.formatMessage(messages[action])}</p>
|
||||||
|
<span className='link-button'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='notification.moderation-warning.learn_more'
|
||||||
|
defaultMessage='Learn more'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
|
@ -10,7 +10,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
|
|
||||||
import DangerousIcon from '@/material-icons/400-24px/dangerous-fill.svg?react';
|
|
||||||
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
||||||
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
|
@ -30,6 +29,7 @@ import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
import FollowRequestContainer from '../containers/follow_request_container';
|
import FollowRequestContainer from '../containers/follow_request_container';
|
||||||
|
|
||||||
|
import { ModerationWarning } from './moderation_warning';
|
||||||
import { RelationshipsSeveranceEvent } from './relationships_severance_event';
|
import { RelationshipsSeveranceEvent } from './relationships_severance_event';
|
||||||
import Report from './report';
|
import Report from './report';
|
||||||
|
|
||||||
|
@ -44,18 +44,10 @@ const messages = defineMessages({
|
||||||
listStatus: { id: 'notification.list_status', defaultMessage: '{name} post is added on {listName}' },
|
listStatus: { id: 'notification.list_status', defaultMessage: '{name} post is added on {listName}' },
|
||||||
statusReference: { id: 'notification.status_reference', defaultMessage: '{name} refered your post' },
|
statusReference: { id: 'notification.status_reference', defaultMessage: '{name} refered your post' },
|
||||||
update: { id: 'notification.update', defaultMessage: '{name} edited a post' },
|
update: { id: 'notification.update', defaultMessage: '{name} edited a post' },
|
||||||
warning: { id: 'notification.warning', defaultMessage: 'You have been warned and did something. Check your mailbox' },
|
|
||||||
warning_none: { id: 'notification.warning.none', defaultMessage: 'You have been warned. Check your mailbox.' },
|
|
||||||
warning_disable: { id: 'notification.warning.disable', defaultMessage: 'You have been warned and disabled account. Check your mailbox.' },
|
|
||||||
warning_force_cw: { id: 'notification.warning.force_cw', defaultMessage: 'You have been warned and one or more statuses have been added warning messages. Check your mailbox.' },
|
|
||||||
warning_mark_statuses_as_sensitive: { id: 'notification.warning.mark_statuses_as_sensitive', defaultMessage: 'You have been warned and some statuses have been marked as sensitive. Check your mailbox.' },
|
|
||||||
warning_delete_statuses: { id: 'notification.warning.delete_statuses', defaultMessage: 'You have been warned and one or more statuses have been deleted. Check your mailbox.' },
|
|
||||||
warning_sensitive: { id: 'notification.warning.sensitive', defaultMessage: 'You have been warned and your account has been marked as sensitive. Check your mailbox.' },
|
|
||||||
warning_silence: { id: 'notification.warning.silence', defaultMessage: 'You have been warned and your account has been silenced. Check your mailbox.' },
|
|
||||||
warning_suspend: { id: 'notification.warning.suspend', defaultMessage: 'You have been warned and your account has been suspended. Check your mailbox.' },
|
|
||||||
adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
|
adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
|
||||||
adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
|
adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
|
||||||
relationshipsSevered: { id: 'notification.relationships_severance_event', defaultMessage: 'Lost connections with {name}' },
|
relationshipsSevered: { id: 'notification.relationships_severance_event', defaultMessage: 'Lost connections with {name}' },
|
||||||
|
moderationWarning: { id: 'notification.moderation_warning', defaultMessage: 'Your have received a moderation warning' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const notificationForScreenReader = (intl, message, timestamp) => {
|
const notificationForScreenReader = (intl, message, timestamp) => {
|
||||||
|
@ -483,47 +475,6 @@ class Notification extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWarning (notification) {
|
|
||||||
const { intl, unread } = this.props;
|
|
||||||
|
|
||||||
const preMessageKey = `warning_${notification.getIn(['account_warning', 'action'])}`;
|
|
||||||
const messageKey = Object.keys(messages).includes(preMessageKey) ? preMessageKey : 'warning';
|
|
||||||
const text = notification.getIn(['account_warning', 'text']);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
|
||||||
<div className={classNames('notification notification-warning focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.statusReference, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
|
||||||
<div className='notification__message'>
|
|
||||||
<div className='notification__favourite-icon-wrapper'>
|
|
||||||
<Icon id='exclamation-triangle' icon={DangerousIcon} className='star-icon' fixedWidth />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
|
||||||
{intl.formatMessage(messages[messageKey])}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{text && <div className='notification__warning-text'>{text}</div>}
|
|
||||||
|
|
||||||
{notification.get('statuses').map((status_id) => (
|
|
||||||
<StatusContainer
|
|
||||||
key={status_id}
|
|
||||||
id={status_id}
|
|
||||||
muted
|
|
||||||
withDismiss
|
|
||||||
hidden={!!this.props.hidden}
|
|
||||||
getScrollPosition={this.props.getScrollPosition}
|
|
||||||
updateScrollBottom={this.props.updateScrollBottom}
|
|
||||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
|
||||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
|
||||||
withoutEmojiReactions
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</HotKeys>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRelationshipsSevered (notification) {
|
renderRelationshipsSevered (notification) {
|
||||||
const { intl, unread, hidden } = this.props;
|
const { intl, unread, hidden } = this.props;
|
||||||
const event = notification.get('event');
|
const event = notification.get('event');
|
||||||
|
@ -547,6 +498,27 @@ class Notification extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderModerationWarning (notification) {
|
||||||
|
const { intl, unread, hidden } = this.props;
|
||||||
|
const warning = notification.get('moderation_warning');
|
||||||
|
|
||||||
|
if (!warning) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HotKeys handlers={this.getHandlers()}>
|
||||||
|
<div className={classNames('notification notification-moderation-warning focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.moderationWarning), notification.get('created_at'))}>
|
||||||
|
<ModerationWarning
|
||||||
|
action={warning.get('action')}
|
||||||
|
id={warning.get('id')}
|
||||||
|
hidden={hidden}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</HotKeys>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderAdminSignUp (notification, account, link) {
|
renderAdminSignUp (notification, account, link) {
|
||||||
const { intl, unread } = this.props;
|
const { intl, unread } = this.props;
|
||||||
|
|
||||||
|
@ -626,10 +598,10 @@ class Notification extends ImmutablePureComponent {
|
||||||
return this.renderUpdate(notification, link);
|
return this.renderUpdate(notification, link);
|
||||||
case 'poll':
|
case 'poll':
|
||||||
return this.renderPoll(notification, account);
|
return this.renderPoll(notification, account);
|
||||||
case 'warning':
|
|
||||||
return this.renderWarning(notification);
|
|
||||||
case 'severed_relationships':
|
case 'severed_relationships':
|
||||||
return this.renderRelationshipsSevered(notification);
|
return this.renderRelationshipsSevered(notification);
|
||||||
|
case 'moderation_warning':
|
||||||
|
return this.renderModerationWarning(notification);
|
||||||
case 'admin.sign_up':
|
case 'admin.sign_up':
|
||||||
return this.renderAdminSignUp(notification, account, link);
|
return this.renderAdminSignUp(notification, account, link);
|
||||||
case 'admin.report':
|
case 'admin.report':
|
||||||
|
|
|
@ -411,6 +411,8 @@
|
||||||
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
|
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
|
||||||
"follow_suggestions.curated_suggestion": "Staff pick",
|
"follow_suggestions.curated_suggestion": "Staff pick",
|
||||||
"follow_suggestions.dismiss": "Don't show again",
|
"follow_suggestions.dismiss": "Don't show again",
|
||||||
|
"follow_suggestions.featured_longer": "Hand-picked by the {domain} team",
|
||||||
|
"follow_suggestions.friends_of_friends_longer": "Popular among people you follow",
|
||||||
"follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.",
|
"follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.",
|
||||||
"follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.",
|
"follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.",
|
||||||
"follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.",
|
"follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.",
|
||||||
|
@ -418,6 +420,8 @@
|
||||||
"follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.",
|
"follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.",
|
||||||
"follow_suggestions.personalized_suggestion": "Personalized suggestion",
|
"follow_suggestions.personalized_suggestion": "Personalized suggestion",
|
||||||
"follow_suggestions.popular_suggestion": "Popular suggestion",
|
"follow_suggestions.popular_suggestion": "Popular suggestion",
|
||||||
|
"follow_suggestions.popular_suggestion_longer": "Popular on {domain}",
|
||||||
|
"follow_suggestions.similar_to_recently_followed_longer": "Similar to profiles you recently followed",
|
||||||
"follow_suggestions.view_all": "View all",
|
"follow_suggestions.view_all": "View all",
|
||||||
"follow_suggestions.who_to_follow": "Who to follow",
|
"follow_suggestions.who_to_follow": "Who to follow",
|
||||||
"followed_tags": "Followed hashtags",
|
"followed_tags": "Followed hashtags",
|
||||||
|
@ -586,6 +590,16 @@
|
||||||
"notification.follow_request": "{name} has requested to follow you",
|
"notification.follow_request": "{name} has requested to follow you",
|
||||||
"notification.list_status": "{name} post is added to {listName}",
|
"notification.list_status": "{name} post is added to {listName}",
|
||||||
"notification.mention": "{name} mentioned you",
|
"notification.mention": "{name} mentioned you",
|
||||||
|
"notification.moderation-warning.learn_more": "Learn more",
|
||||||
|
"notification.moderation_warning": "Your have received a moderation warning",
|
||||||
|
"notification.moderation_warning.action_delete_statuses": "Some of your posts have been removed.",
|
||||||
|
"notification.moderation_warning.action_disable": "Your account has been disabled.",
|
||||||
|
"notification.moderation_warning.action_force_cw": "Some of your posts have been added content-warning text.",
|
||||||
|
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Some of your posts have been marked as sensitive.",
|
||||||
|
"notification.moderation_warning.action_none": "Your account has received a moderation warning.",
|
||||||
|
"notification.moderation_warning.action_sensitive": "Your posts will be marked as sensitive from now on.",
|
||||||
|
"notification.moderation_warning.action_silence": "Your account has been limited.",
|
||||||
|
"notification.moderation_warning.action_suspend": "Your account has been suspended.",
|
||||||
"notification.own_poll": "Your poll has ended",
|
"notification.own_poll": "Your poll has ended",
|
||||||
"notification.poll": "A poll you have voted in has ended",
|
"notification.poll": "A poll you have voted in has ended",
|
||||||
"notification.reblog": "{name} boosted your post",
|
"notification.reblog": "{name} boosted your post",
|
||||||
|
@ -597,15 +611,6 @@
|
||||||
"notification.status": "{name} just posted",
|
"notification.status": "{name} just posted",
|
||||||
"notification.status_reference": "{name} quoted your post",
|
"notification.status_reference": "{name} quoted your post",
|
||||||
"notification.update": "{name} edited a post",
|
"notification.update": "{name} edited a post",
|
||||||
"notification.warning": "You have been warned and did something. Check your mailbox",
|
|
||||||
"notification.warning.delete_statuses": "You have been warned and one or more statuses have been deleted. Check your mailbox.",
|
|
||||||
"notification.warning.disable": "You have been warned and disabled account. Check your mailbox.",
|
|
||||||
"notification.warning.force_cw": "You have been warned and one or more statuses have been added warning messages. Check your mailbox.",
|
|
||||||
"notification.warning.mark_statuses_as_sensitive": "You have been warned and some statuses have been marked as sensitive. Check your mailbox.",
|
|
||||||
"notification.warning.none": "You have been warned. Check your mailbox.",
|
|
||||||
"notification.warning.sensitive": "You have been warned and your account has been marked as sensitive. Check your mailbox.",
|
|
||||||
"notification.warning.silence": "You have been warned and your account has been silenced. Check your mailbox.",
|
|
||||||
"notification.warning.suspend": "You have been warned and your account has been suspended. Check your mailbox.",
|
|
||||||
"notification_requests.accept": "Accept",
|
"notification_requests.accept": "Accept",
|
||||||
"notification_requests.dismiss": "Dismiss",
|
"notification_requests.dismiss": "Dismiss",
|
||||||
"notification_requests.notifications_from": "Notifications from {name}",
|
"notification_requests.notifications_from": "Notifications from {name}",
|
||||||
|
|
|
@ -585,15 +585,6 @@
|
||||||
"notification.status": "{name}さんが投稿しました",
|
"notification.status": "{name}さんが投稿しました",
|
||||||
"notification.status_reference": "{name}さんがあなたの投稿を引用しました",
|
"notification.status_reference": "{name}さんがあなたの投稿を引用しました",
|
||||||
"notification.update": "{name}さんが投稿を編集しました",
|
"notification.update": "{name}さんが投稿を編集しました",
|
||||||
"notification.warning": "あなたは警告を出され、処分が実行されました。詳細はメールをご確認ください",
|
|
||||||
"notification.warning.none": "あなたは警告を出されました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.delete_statuses": "あなたは警告を出され、1つまたは複数の投稿が削除されました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.disable": "あなたは警告を出され、アカウントが無効化されました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.force_cw": "あなたは警告を出され、投稿に警告文が追加されました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.mark_statuses_as_sensitive": "あなたは警告を出され、投稿が閲覧注意としてマークされました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.sensitive": "あなたは警告を出され、アカウントがセンシティブ指定されました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.silence": "あなたは警告を出され、サイレンスされました。詳細はメールをご確認ください。",
|
|
||||||
"notification.warning.suspended": "あなたは警告を出され、サスペンドされました。詳細はメールをご確認ください。",
|
|
||||||
"notification_requests.accept": "受け入れる",
|
"notification_requests.accept": "受け入れる",
|
||||||
"notification_requests.dismiss": "無視",
|
"notification_requests.dismiss": "無視",
|
||||||
"notification_requests.notifications_from": "{name}からの通知",
|
"notification_requests.notifications_from": "{name}からの通知",
|
||||||
|
|
|
@ -55,11 +55,10 @@ export const notificationToMap = notification => ImmutableMap({
|
||||||
created_at: notification.created_at,
|
created_at: notification.created_at,
|
||||||
emoji_reaction: ImmutableMap(notification.emoji_reaction),
|
emoji_reaction: ImmutableMap(notification.emoji_reaction),
|
||||||
status: notification.status ? notification.status.id : null,
|
status: notification.status ? notification.status.id : null,
|
||||||
statuses: notification.statuses ? notification.statuses.map((status) => status.id) : null,
|
|
||||||
list: notification.list ? ImmutableMap(notification.list) : null,
|
list: notification.list ? ImmutableMap(notification.list) : null,
|
||||||
report: notification.report ? fromJS(notification.report) : null,
|
report: notification.report ? fromJS(notification.report) : null,
|
||||||
account_warning: notification.account_warning ? ImmutableMap(notification.account_warning) : null,
|
|
||||||
event: notification.event ? fromJS(notification.event) : null,
|
event: notification.event ? fromJS(notification.event) : null,
|
||||||
|
moderation_warning: notification.moderation_warning ? fromJS(notification.moderation_warning) : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const normalizeNotification = (state, notification, usePendingItems) => {
|
const normalizeNotification = (state, notification, usePendingItems) => {
|
||||||
|
|
|
@ -2086,11 +2086,10 @@ a .account__avatar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.fa-times {
|
.account__relationship,
|
||||||
color: $ui-secondary-color;
|
.explore__suggestions__card {
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
border: 1px solid var(--background-border-color);
|
border: 1px solid var(--background-border-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -2256,7 +2255,8 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification__relationships-severance-event {
|
.notification__relationships-severance-event,
|
||||||
|
.notification__moderation-warning {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
|
@ -3062,6 +3062,75 @@ $ui-header-logo-wordmark-width: 99px;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.explore__suggestions__card {
|
||||||
|
padding: 12px 16px;
|
||||||
|
gap: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-bottom: 1px solid var(--background-border-color);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__source {
|
||||||
|
padding-inline-start: 60px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: $dark-text-color;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__main {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
&__name-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
display: block;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: $secondary-text-color;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__account {
|
||||||
|
color: $darker-text-color;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $no-gap-breakpoint - 1px) {
|
@media screen and (max-width: $no-gap-breakpoint - 1px) {
|
||||||
.columns-area__panels__pane--compositional {
|
.columns-area__panels__pane--compositional {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -7599,10 +7668,11 @@ a.status-card {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
left: 0;
|
left: 50%;
|
||||||
width: 100%;
|
transform: translateX(-50%);
|
||||||
|
width: 40px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
border-radius: 4px;
|
border-radius: 4px 4px 0 0;
|
||||||
background: $highlight-text-color;
|
background: $highlight-text-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,9 +52,8 @@ class Admin::AccountAction
|
||||||
process_reports!
|
process_reports!
|
||||||
end
|
end
|
||||||
|
|
||||||
process_email!
|
process_notification!
|
||||||
process_queue!
|
process_queue!
|
||||||
notify!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def report
|
def report
|
||||||
|
@ -108,10 +107,6 @@ class Admin::AccountAction
|
||||||
log_action(:create, @warning) if @warning.text.present? && type == 'none'
|
log_action(:create, @warning) if @warning.text.present? && type == 'none'
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify!
|
|
||||||
LocalNotificationWorker.perform_async(target_account.id, @warning.id, 'AccountWarning', 'warning') if @warning && %w(none sensitive silence).include?(type)
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_reports!
|
def process_reports!
|
||||||
# If we're doing "mark as resolved" on a single report,
|
# If we're doing "mark as resolved" on a single report,
|
||||||
# then we want to keep other reports open in case they
|
# then we want to keep other reports open in case they
|
||||||
|
@ -163,8 +158,11 @@ class Admin::AccountAction
|
||||||
queue_suspension_worker! if type == 'suspend'
|
queue_suspension_worker! if type == 'suspend'
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_email!
|
def process_notification!
|
||||||
UserMailer.warning(target_account.user, warning).deliver_later! if warnable?
|
return unless warnable?
|
||||||
|
|
||||||
|
UserMailer.warning(target_account.user, warning).deliver_later!
|
||||||
|
LocalNotificationWorker.perform_async(target_account.id, warning.id, 'AccountWarning', 'moderation_warning')
|
||||||
end
|
end
|
||||||
|
|
||||||
def warnable?
|
def warnable?
|
||||||
|
|
|
@ -17,7 +17,6 @@ class Admin::StatusBatchAction
|
||||||
|
|
||||||
def save!
|
def save!
|
||||||
process_action!
|
process_action!
|
||||||
notify!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -68,7 +67,8 @@ class Admin::StatusBatchAction
|
||||||
statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
|
statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
|
||||||
end
|
end
|
||||||
|
|
||||||
UserMailer.warning(target_account.user, @warning).deliver_later! if warnable?
|
process_notification!
|
||||||
|
|
||||||
RemovalWorker.push_bulk(status_ids) { |status_id| [status_id, { 'preserve' => target_account.local?, 'immediate' => !target_account.local? }] }
|
RemovalWorker.push_bulk(status_ids) { |status_id| [status_id, { 'preserve' => target_account.local?, 'immediate' => !target_account.local? }] }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Admin::StatusBatchAction
|
||||||
text: text
|
text: text
|
||||||
)
|
)
|
||||||
|
|
||||||
UserMailer.warning(target_account.user, @warning).deliver_later! if warnable?
|
process_notification!
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_force_cw!
|
def handle_force_cw!
|
||||||
|
@ -140,7 +140,7 @@ class Admin::StatusBatchAction
|
||||||
text: text
|
text: text
|
||||||
)
|
)
|
||||||
|
|
||||||
UserMailer.warning(target_account.user, @warning).deliver_later! if warnable?
|
process_notification!
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_report!
|
def handle_report!
|
||||||
|
@ -158,10 +158,6 @@ class Admin::StatusBatchAction
|
||||||
report.save!
|
report.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify!
|
|
||||||
LocalNotificationWorker.perform_async(target_account.id, @warning.id, 'AccountWarning', 'warning') if warnable? && @warning
|
|
||||||
end
|
|
||||||
|
|
||||||
def report
|
def report
|
||||||
@report ||= Report.find(report_id) if report_id.present?
|
@report ||= Report.find(report_id) if report_id.present?
|
||||||
end
|
end
|
||||||
|
@ -170,6 +166,13 @@ class Admin::StatusBatchAction
|
||||||
!report.nil?
|
!report.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def process_notification!
|
||||||
|
return unless warnable?
|
||||||
|
|
||||||
|
UserMailer.warning(target_account.user, @warning).deliver_later!
|
||||||
|
LocalNotificationWorker.perform_async(target_account.id, @warning.id, 'AccountWarning', 'moderation_warning')
|
||||||
|
end
|
||||||
|
|
||||||
def warnable?
|
def warnable?
|
||||||
send_email_notification && target_account.local?
|
send_email_notification && target_account.local?
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ class Notification < ApplicationRecord
|
||||||
'EmojiReaction' => :emoji_reaction,
|
'EmojiReaction' => :emoji_reaction,
|
||||||
'StatusReference' => :status_reference,
|
'StatusReference' => :status_reference,
|
||||||
'Poll' => :poll,
|
'Poll' => :poll,
|
||||||
'AccountWarning' => :warning,
|
'AccountWarning' => :moderation_warning,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
PROPERTIES = {
|
PROPERTIES = {
|
||||||
|
@ -70,10 +70,10 @@ class Notification < ApplicationRecord
|
||||||
update: {
|
update: {
|
||||||
filterable: false,
|
filterable: false,
|
||||||
}.freeze,
|
}.freeze,
|
||||||
warning: {
|
severed_relationships: {
|
||||||
filterable: false,
|
filterable: false,
|
||||||
}.freeze,
|
}.freeze,
|
||||||
severed_relationships: {
|
moderation_warning: {
|
||||||
filterable: false,
|
filterable: false,
|
||||||
}.freeze,
|
}.freeze,
|
||||||
'admin.sign_up': {
|
'admin.sign_up': {
|
||||||
|
@ -208,15 +208,6 @@ class Notification < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def from_account_web
|
|
||||||
case activity_type
|
|
||||||
when 'AccountWarning'
|
|
||||||
account_warning&.target_account
|
|
||||||
else
|
|
||||||
from_account
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
after_initialize :set_from_account
|
after_initialize :set_from_account
|
||||||
before_validation :set_from_account
|
before_validation :set_from_account
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::AccountWarningSerializer < ActiveModel::Serializer
|
class REST::AccountWarningSerializer < ActiveModel::Serializer
|
||||||
attributes :id, :action, :text, :status_ids
|
attributes :id, :action, :text, :status_ids, :created_at
|
||||||
|
|
||||||
|
has_one :target_account, serializer: REST::AccountSerializer
|
||||||
|
has_one :appeal, serializer: REST::AppealSerializer
|
||||||
|
|
||||||
|
def id
|
||||||
|
object.id.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_ids
|
||||||
|
object&.status_ids&.map(&:to_s)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
15
app/serializers/rest/appeal_serializer.rb
Normal file
15
app/serializers/rest/appeal_serializer.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class REST::AppealSerializer < ActiveModel::Serializer
|
||||||
|
attributes :text, :state
|
||||||
|
|
||||||
|
def state
|
||||||
|
if object.approved?
|
||||||
|
'approved'
|
||||||
|
elsif object.rejected?
|
||||||
|
'rejected'
|
||||||
|
else
|
||||||
|
'pending'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,20 +3,24 @@
|
||||||
class REST::NotificationSerializer < ActiveModel::Serializer
|
class REST::NotificationSerializer < ActiveModel::Serializer
|
||||||
attributes :id, :type, :created_at
|
attributes :id, :type, :created_at
|
||||||
|
|
||||||
has_many :statuses, serializer: REST::StatusSerializer, if: :warning_type?
|
belongs_to :from_account, key: :account, serializer: REST::AccountSerializer
|
||||||
|
|
||||||
belongs_to :from_account_web, key: :account, serializer: REST::AccountSerializer
|
|
||||||
belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
|
belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
|
||||||
belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
|
belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
|
||||||
belongs_to :emoji_reaction, if: :emoji_reaction_type?, serializer: REST::NotifyEmojiReactionSerializer
|
belongs_to :emoji_reaction, if: :emoji_reaction_type?, serializer: REST::NotifyEmojiReactionSerializer
|
||||||
belongs_to :account_warning, if: :warning_type?, serializer: REST::AccountWarningSerializer
|
|
||||||
belongs_to :list, if: :list_status_type?, serializer: REST::ListSerializer
|
belongs_to :list, if: :list_status_type?, serializer: REST::ListSerializer
|
||||||
belongs_to :account_relationship_severance_event, key: :event, if: :relationship_severance_event?, serializer: REST::AccountRelationshipSeveranceEventSerializer
|
belongs_to :account_relationship_severance_event, key: :event, if: :relationship_severance_event?, serializer: REST::AccountRelationshipSeveranceEventSerializer
|
||||||
|
belongs_to :account_warning, key: :moderation_warning, if: :moderation_warning_event?, serializer: REST::AccountWarningSerializer
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object.id.to_s
|
object.id.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def from_account
|
||||||
|
return object.account if moderation_warning_event? # Hide moderator account
|
||||||
|
|
||||||
|
object.from_account
|
||||||
|
end
|
||||||
|
|
||||||
def status_type?
|
def status_type?
|
||||||
[:favourite, :emoji_reaction, :reaction, :reblog, :status_reference, :status, :list_status, :mention, :poll, :update].include?(object.type)
|
[:favourite, :emoji_reaction, :reaction, :reblog, :status_reference, :status, :list_status, :mention, :poll, :update].include?(object.type)
|
||||||
end
|
end
|
||||||
|
@ -25,10 +29,6 @@ class REST::NotificationSerializer < ActiveModel::Serializer
|
||||||
object.type == :'admin.report'
|
object.type == :'admin.report'
|
||||||
end
|
end
|
||||||
|
|
||||||
def warning_type?
|
|
||||||
object.type == :warning
|
|
||||||
end
|
|
||||||
|
|
||||||
def emoji_reaction_type?
|
def emoji_reaction_type?
|
||||||
object.type == :emoji_reaction
|
object.type == :emoji_reaction
|
||||||
end
|
end
|
||||||
|
@ -45,7 +45,7 @@ class REST::NotificationSerializer < ActiveModel::Serializer
|
||||||
object.type == :severed_relationships
|
object.type == :severed_relationships
|
||||||
end
|
end
|
||||||
|
|
||||||
def statuses
|
def moderation_warning_event?
|
||||||
Status.where(id: object.account_warning.status_ids).to_a
|
object.type == :moderation_warning
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ class NotifyService < BaseService
|
||||||
status_reference
|
status_reference
|
||||||
status
|
status
|
||||||
list_status
|
list_status
|
||||||
warning
|
moderation_warning
|
||||||
# TODO: this probably warrants an email notification
|
# TODO: this probably warrants an email notification
|
||||||
severed_relationships
|
severed_relationships
|
||||||
).freeze
|
).freeze
|
||||||
|
@ -26,7 +26,7 @@ class NotifyService < BaseService
|
||||||
|
|
||||||
def dismiss?
|
def dismiss?
|
||||||
blocked = @recipient.unavailable?
|
blocked = @recipient.unavailable?
|
||||||
blocked ||= from_self? && @notification.type != :poll && @notification.type != :severed_relationships
|
blocked ||= from_self? && %i(poll severed_relationships moderation_warning).exclude?(@notification.type)
|
||||||
|
|
||||||
return blocked if message? && from_staff?
|
return blocked if message? && from_staff?
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ class NotifyService < BaseService
|
||||||
admin.report
|
admin.report
|
||||||
poll
|
poll
|
||||||
update
|
update
|
||||||
|
account_warning
|
||||||
).freeze
|
).freeze
|
||||||
|
|
||||||
def initialize(notification)
|
def initialize(notification)
|
||||||
|
|
|
@ -1507,6 +1507,7 @@ en:
|
||||||
title_actions:
|
title_actions:
|
||||||
delete_statuses: Post removal
|
delete_statuses: Post removal
|
||||||
disable: Freezing of account
|
disable: Freezing of account
|
||||||
|
force_cw: Add content-warning to posts
|
||||||
mark_statuses_as_sensitive: Marking of posts as sensitive
|
mark_statuses_as_sensitive: Marking of posts as sensitive
|
||||||
none: Warning
|
none: Warning
|
||||||
sensitive: Marking of account as sensitive
|
sensitive: Marking of account as sensitive
|
||||||
|
|
|
@ -1500,6 +1500,7 @@ ja:
|
||||||
title_actions:
|
title_actions:
|
||||||
delete_statuses: 投稿の削除
|
delete_statuses: 投稿の削除
|
||||||
disable: アカウント凍結
|
disable: アカウント凍結
|
||||||
|
force_cw: 投稿に警告文を追加
|
||||||
mark_statuses_as_sensitive: 投稿を閲覧注意としてマーク
|
mark_statuses_as_sensitive: 投稿を閲覧注意としてマーク
|
||||||
none: 警告
|
none: 警告
|
||||||
sensitive: アカウントを閲覧注意としてマーク
|
sensitive: アカウントを閲覧注意としてマーク
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class MoveAccountWarningNotifications < ActiveRecord::Migration[7.1]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
class Notification < ApplicationRecord; end
|
||||||
|
|
||||||
|
def up
|
||||||
|
Notification.where(type: 'warning').in_batches.update_all(type: 'moderation_warning')
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
Notification.where(type: 'moderation_warning').in_batches.update_all(type: 'warning')
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.1].define(version: 2024_04_01_222541) do
|
ActiveRecord::Schema[7.1].define(version: 2024_04_26_000034) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ namespace :dangerous do
|
||||||
20240326231854
|
20240326231854
|
||||||
20240327234026
|
20240327234026
|
||||||
20240401222541
|
20240401222541
|
||||||
|
20240426000034
|
||||||
)
|
)
|
||||||
# Removed: account_groups
|
# Removed: account_groups
|
||||||
target_tables = %w(
|
target_tables = %w(
|
||||||
|
|
|
@ -69,22 +69,22 @@ RSpec.describe Admin::AccountAction do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates Admin::ActionLog' do
|
it 'sends notification, log the action, and closes other reports', :aggregate_failures do
|
||||||
|
other_report = Fabricate(:report, target_account: target_account)
|
||||||
|
|
||||||
|
emails = []
|
||||||
expect do
|
expect do
|
||||||
subject
|
emails = capture_emails { subject }
|
||||||
end.to change(Admin::ActionLog, :count).by 1
|
end.to (change(Admin::ActionLog.where(action: type), :count).by 1)
|
||||||
end
|
.and(change { other_report.reload.action_taken? }.from(false).to(true))
|
||||||
|
|
||||||
it 'calls process_email!' do
|
expect(emails).to contain_exactly(
|
||||||
allow(account_action).to receive(:process_email!)
|
have_attributes(
|
||||||
subject
|
to: contain_exactly(target_account.user.email)
|
||||||
expect(account_action).to have_received(:process_email!)
|
)
|
||||||
end
|
)
|
||||||
|
|
||||||
it 'calls process_reports!' do
|
expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(target_account.id, anything, 'AccountWarning', 'moderation_warning')
|
||||||
allow(account_action).to receive(:process_reports!)
|
|
||||||
subject
|
|
||||||
expect(account_action).to have_received(:process_reports!)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue