diff --git a/app/controllers/api/v1/accounts/exclude_antennas_controller.rb b/app/controllers/api/v1/accounts/exclude_antennas_controller.rb
new file mode 100644
index 0000000000..c1f5c5981c
--- /dev/null
+++ b/app/controllers/api/v1/accounts/exclude_antennas_controller.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class Api::V1::Accounts::ExcludeAntennasController < Api::BaseController
+ before_action -> { doorkeeper_authorize! :read, :'read:lists' }
+ before_action :require_user!
+ before_action :set_account
+
+ def index
+ @antennas = @account.suspended? ? [] : current_account.antennas.where('exclude_accounts @> \'[?]\'', @account.id)
+ render json: @antennas, each_serializer: REST::AntennaSerializer
+ end
+
+ private
+
+ def set_account
+ @account = Account.find(params[:account_id])
+ end
+end
diff --git a/app/javascript/mastodon/actions/antennas.js b/app/javascript/mastodon/actions/antennas.js
index 2b0d5d042a..51002b6c58 100644
--- a/app/javascript/mastodon/actions/antennas.js
+++ b/app/javascript/mastodon/actions/antennas.js
@@ -122,6 +122,10 @@ export const ANTENNA_ADDER_ANTENNAS_FETCH_REQUEST = 'ANTENNA_ADDER_ANTENNAS_FETC
export const ANTENNA_ADDER_ANTENNAS_FETCH_SUCCESS = 'ANTENNA_ADDER_ANTENNAS_FETCH_SUCCESS';
export const ANTENNA_ADDER_ANTENNAS_FETCH_FAIL = 'ANTENNA_ADDER_ANTENNAS_FETCH_FAIL';
+export const ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_REQUEST = 'ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_REQUEST';
+export const ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_SUCCESS = 'ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_SUCCESS';
+export const ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_FAIL = 'ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_FAIL';
+
export const fetchAntenna = id => (dispatch, getState) => {
if (getState().getIn(['antennas', id])) {
return;
@@ -905,6 +909,15 @@ export const setupAntennaAdder = accountId => (dispatch, getState) => {
dispatch(fetchAccountAntennas(accountId));
};
+export const setupExcludeAntennaAdder = accountId => (dispatch, getState) => {
+ dispatch({
+ type: ANTENNA_ADDER_SETUP,
+ account: getState().getIn(['accounts', accountId]),
+ });
+ dispatch(fetchAntennas());
+ dispatch(fetchExcludeAccountAntennas(accountId));
+};
+
export const fetchAccountAntennas = accountId => (dispatch, getState) => {
dispatch(fetchAccountAntennasRequest(accountId));
@@ -930,6 +943,31 @@ export const fetchAccountAntennasFail = (id, err) => ({
err,
});
+export const fetchExcludeAccountAntennas = accountId => (dispatch, getState) => {
+ dispatch(fetchExcludeAccountAntennasRequest(accountId));
+
+ api(getState).get(`/api/v1/accounts/${accountId}/exclude_antennas`)
+ .then(({ data }) => dispatch(fetchExcludeAccountAntennasSuccess(accountId, data)))
+ .catch(err => dispatch(fetchExcludeAccountAntennasFail(accountId, err)));
+};
+
+export const fetchExcludeAccountAntennasRequest = id => ({
+ type:ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_REQUEST,
+ id,
+});
+
+export const fetchExcludeAccountAntennasSuccess = (id, antennas) => ({
+ type: ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_SUCCESS,
+ id,
+ antennas,
+});
+
+export const fetchExcludeAccountAntennasFail = (id, err) => ({
+ type: ANTENNA_ADDER_EXCLUDE_ANTENNAS_FETCH_FAIL,
+ id,
+ err,
+});
+
export const addToAntennaAdder = antennaId => (dispatch, getState) => {
dispatch(addToAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
};
@@ -938,3 +976,11 @@ export const removeFromAntennaAdder = antennaId => (dispatch, getState) => {
dispatch(removeFromAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
};
+export const addExcludeToAntennaAdder = antennaId => (dispatch, getState) => {
+ dispatch(addExcludeToAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
+};
+
+export const removeExcludeFromAntennaAdder = antennaId => (dispatch, getState) => {
+ dispatch(removeExcludeFromAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
+};
+
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index c347cf2f87..9b3424f3e5 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -59,6 +59,7 @@ const messages = defineMessages({
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
add_or_remove_from_antenna: { id: 'account.add_or_remove_from_antenna', defaultMessage: 'Add or Remove from antennas' },
+ add_or_remove_from_exclude_antenna: { id: 'account.add_or_remove_from_exclude_antenna', defaultMessage: 'Add or Remove from antennas as exclusion' },
add_or_remove_from_circle: { id: 'account.add_or_remove_from_circle', defaultMessage: 'Add or Remove from circles' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
@@ -106,6 +107,7 @@ class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired,
onAddToAntenna: PropTypes.func.isRequired,
+ onAddToExcludeAntenna: PropTypes.func.isRequired,
onAddToCircle: PropTypes.func.isRequired,
onEditAccountNote: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired,
@@ -332,6 +334,7 @@ class Header extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
}
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_antenna), action: this.props.onAddToAntenna });
+ menu.push({ text: intl.formatMessage(messages.add_or_remove_from_exclude_antenna), action: this.props.onAddToExcludeAntenna });
if (account.getIn(['relationship', 'followed_by'])) {
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_circle), action: this.props.onAddToCircle });
}
diff --git a/app/javascript/mastodon/features/account_timeline/components/header.jsx b/app/javascript/mastodon/features/account_timeline/components/header.jsx
index ccf8290574..aaa0c57f83 100644
--- a/app/javascript/mastodon/features/account_timeline/components/header.jsx
+++ b/app/javascript/mastodon/features/account_timeline/components/header.jsx
@@ -28,6 +28,7 @@ export default class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired,
onAddToAntenna: PropTypes.func.isRequired,
+ onAddToExcludeAntenna: PropTypes.func.isRequired,
onAddToCircle: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired,
onInteractionModal: PropTypes.func.isRequired,
@@ -102,6 +103,10 @@ export default class Header extends ImmutablePureComponent {
this.props.onAddToAntenna(this.props.account);
};
+ handleAddToExcludeAntenna = () => {
+ this.props.onAddToExcludeAntenna(this.props.account);
+ };
+
handleAddToCircle = () => {
this.props.onAddToCircle(this.props.account);
};
@@ -149,6 +154,7 @@ export default class Header extends ImmutablePureComponent {
onEndorseToggle={this.handleEndorseToggle}
onAddToList={this.handleAddToList}
onAddToAntenna={this.handleAddToAntenna}
+ onAddToExcludeAntenna={this.handleAddToExcludeAntenna}
onAddToCircle={this.handleAddToCircle}
onEditAccountNote={this.handleEditAccountNote}
onChangeLanguages={this.handleChangeLanguages}
diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.jsx b/app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
index b71ee4b945..92bcd167fb 100644
--- a/app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
+++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
@@ -169,6 +169,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
modalType: 'ANTENNA_ADDER',
modalProps: {
accountId: account.get('id'),
+ isExclude: false,
+ },
+ }));
+ },
+
+ onAddToExcludeAntenna (account) {
+ dispatch(openModal({
+ modalType: 'ANTENNA_ADDER',
+ modalProps: {
+ accountId: account.get('id'),
+ isExclude: true,
},
}));
},
diff --git a/app/javascript/mastodon/features/antenna_adder/components/antenna.jsx b/app/javascript/mastodon/features/antenna_adder/components/antenna.jsx
index 2c90c311c1..b7f6134b79 100644
--- a/app/javascript/mastodon/features/antenna_adder/components/antenna.jsx
+++ b/app/javascript/mastodon/features/antenna_adder/components/antenna.jsx
@@ -8,7 +8,7 @@ import { connect } from 'react-redux';
import { Icon } from 'mastodon/components/icon';
-import { removeFromAntennaAdder, addToAntennaAdder } from '../../../actions/antennas';
+import { removeFromAntennaAdder, addToAntennaAdder, removeExcludeFromAntennaAdder, addExcludeToAntennaAdder } from '../../../actions/antennas';
import { IconButton } from '../../../components/icon_button';
const messages = defineMessages({
@@ -24,15 +24,20 @@ const MapStateToProps = (state, { antennaId, added }) => ({
const mapDispatchToProps = (dispatch, { antennaId }) => ({
onRemove: () => dispatch(removeFromAntennaAdder(antennaId)),
onAdd: () => dispatch(addToAntennaAdder(antennaId)),
+ onExcludeRemove: () => dispatch(removeExcludeFromAntennaAdder(antennaId)),
+ onExcludeAdd: () => dispatch(addExcludeToAntennaAdder(antennaId)),
});
class Antenna extends ImmutablePureComponent {
static propTypes = {
antenna: ImmutablePropTypes.map.isRequired,
+ isExclude: PropTypes.bool.isRequired,
intl: PropTypes.object.isRequired,
onRemove: PropTypes.func.isRequired,
onAdd: PropTypes.func.isRequired,
+ onExcludeRemove: PropTypes.func.isRequired,
+ onExcludeAdd: PropTypes.func.isRequired,
added: PropTypes.bool,
};
@@ -40,15 +45,31 @@ class Antenna extends ImmutablePureComponent {
added: false,
};
+ handleRemove = () => {
+ if (this.props.isExclude) {
+ this.props.onExcludeRemove();
+ } else {
+ this.props.onRemove();
+ }
+ };
+
+ handleAdd = () => {
+ if (this.props.isExclude) {
+ this.props.onExcludeAdd();
+ } else {
+ this.props.onAdd();
+ }
+ };
+
render () {
- const { antenna, intl, onRemove, onAdd, added } = this.props;
+ const { antenna, intl, added } = this.props;
let button;
if (added) {
- button =