Add antenna editor page
This commit is contained in:
parent
87a8da93e8
commit
f99dde6df1
25 changed files with 1187 additions and 122 deletions
|
@ -1,13 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Antennas::AccountsController < Api::BaseController
|
||||
# before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' } # , except: [:show]
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_antenna
|
||||
|
||||
# after_action :insert_pagination_headers, only: :show
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
def show
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
ApplicationRecord.transaction do
|
||||
|
@ -32,6 +37,14 @@ class Api::V1::Antennas::AccountsController < Api::BaseController
|
|||
@antenna = Antenna.where(account: current_account).find(params[:antenna_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@antenna.accounts.without_suspended.includes(:account_stat).all
|
||||
else
|
||||
@antenna.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
def antenna_accounts
|
||||
Account.find(account_ids)
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::AntennasController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:index, :show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_antenna, except: [:index]
|
||||
before_action :set_antenna, except: [:index, :create]
|
||||
|
||||
rescue_from ArgumentError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
|
@ -20,9 +20,28 @@ class Api::V1::AntennasController < Api::BaseController
|
|||
render json: @antenna, serializer: REST::AntennaSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@antenna = Antenna.create!(antenna_params.merge(account: current_account, list_id: 0))
|
||||
render json: @antenna, serializer: REST::AntennaSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@antenna.update!(antenna_params)
|
||||
render json: @antenna, serializer: REST::AntennaSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@antenna.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_antenna
|
||||
@antenna = Antenna.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def antenna_params
|
||||
params.permit(:title, :stl, :with_media_only, :ignore_reblog)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,29 +1,82 @@
|
|||
import api from '../api';
|
||||
|
||||
import { showAlertForError } from './alerts';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
|
||||
export const ANTENNA_FETCH_REQUEST = 'ANTENNA_FETCH_REQUEST';
|
||||
export const ANTENNA_FETCH_SUCCESS = 'ANTENNA_FETCH_SUCCESS';
|
||||
export const ANTENNA_FETCH_FAIL = 'ANTENNA_FETCH_FAIL';
|
||||
|
||||
export const ANTENNAS_FETCH_REQUEST = 'ANTENNAS_FETCH_REQUEST';
|
||||
export const ANTENNAS_FETCH_SUCCESS = 'ANTENNAS_FETCH_SUCCESS';
|
||||
export const ANTENNAS_FETCH_FAIL = 'ANTENNAS_FETCH_FAIL';
|
||||
|
||||
export const ANTENNA_EDITOR_TITLE_CHANGE = 'ANTENNA_EDITOR_TITLE_CHANGE';
|
||||
export const ANTENNA_EDITOR_RESET = 'ANTENNA_EDITOR_RESET';
|
||||
export const ANTENNA_EDITOR_SETUP = 'ANTENNA_EDITOR_SETUP';
|
||||
|
||||
export const ANTENNA_CREATE_REQUEST = 'ANTENNA_CREATE_REQUEST';
|
||||
export const ANTENNA_CREATE_SUCCESS = 'ANTENNA_CREATE_SUCCESS';
|
||||
export const ANTENNA_CREATE_FAIL = 'ANTENNA_CREATE_FAIL';
|
||||
|
||||
export const ANTENNA_UPDATE_REQUEST = 'ANTENNA_UPDATE_REQUEST';
|
||||
export const ANTENNA_UPDATE_SUCCESS = 'ANTENNA_UPDATE_SUCCESS';
|
||||
export const ANTENNA_UPDATE_FAIL = 'ANTENNA_UPDATE_FAIL';
|
||||
|
||||
export const ANTENNA_DELETE_REQUEST = 'ANTENNA_DELETE_REQUEST';
|
||||
export const ANTENNA_DELETE_SUCCESS = 'ANTENNA_DELETE_SUCCESS';
|
||||
export const ANTENNA_DELETE_FAIL = 'ANTENNA_DELETE_FAIL';
|
||||
|
||||
export const ANTENNA_ACCOUNTS_FETCH_REQUEST = 'ANTENNA_ACCOUNTS_FETCH_REQUEST';
|
||||
export const ANTENNA_ACCOUNTS_FETCH_SUCCESS = 'ANTENNA_ACCOUNTS_FETCH_SUCCESS';
|
||||
export const ANTENNA_ACCOUNTS_FETCH_FAIL = 'ANTENNA_ACCOUNTS_FETCH_FAIL';
|
||||
|
||||
export const ANTENNA_EDITOR_ADD_ACCOUNT_REQUEST = 'ANTENNA_EDITOR_ADD_ACCOUNT_REQUEST';
|
||||
export const ANTENNA_EDITOR_ADD_ACCOUNT_SUCCESS = 'ANTENNA_EDITOR_ADD_ACCOUNT_SUCCESS';
|
||||
export const ANTENNA_EDITOR_ADD_ACCOUNT_FAIL = 'ANTENNA_EDITOR_ADD_ACCOUNT_FAIL';
|
||||
export const ANTENNA_EDITOR_SUGGESTIONS_CHANGE = 'ANTENNA_EDITOR_SUGGESTIONS_CHANGE';
|
||||
export const ANTENNA_EDITOR_SUGGESTIONS_READY = 'ANTENNA_EDITOR_SUGGESTIONS_READY';
|
||||
export const ANTENNA_EDITOR_SUGGESTIONS_CLEAR = 'ANTENNA_EDITOR_SUGGESTIONS_CLEAR';
|
||||
|
||||
export const ANTENNA_EDITOR_REMOVE_ACCOUNT_REQUEST = 'ANTENNA_EDITOR_REMOVE_ACCOUNT_REQUEST';
|
||||
export const ANTENNA_EDITOR_REMOVE_ACCOUNT_SUCCESS = 'ANTENNA_EDITOR_REMOVE_ACCOUNT_SUCCESS';
|
||||
export const ANTENNA_EDITOR_REMOVE_ACCOUNT_FAIL = 'ANTENNA_EDITOR_REMOVE_ACCOUNT_FAIL';
|
||||
export const ANTENNA_EDITOR_ADD_REQUEST = 'ANTENNA_EDITOR_ADD_REQUEST';
|
||||
export const ANTENNA_EDITOR_ADD_SUCCESS = 'ANTENNA_EDITOR_ADD_SUCCESS';
|
||||
export const ANTENNA_EDITOR_ADD_FAIL = 'ANTENNA_EDITOR_ADD_FAIL';
|
||||
|
||||
export const ANTENNA_EDITOR_REMOVE_REQUEST = 'ANTENNA_EDITOR_REMOVE_REQUEST';
|
||||
export const ANTENNA_EDITOR_REMOVE_SUCCESS = 'ANTENNA_EDITOR_REMOVE_SUCCESS';
|
||||
export const ANTENNA_EDITOR_REMOVE_FAIL = 'ANTENNA_EDITOR_REMOVE_FAIL';
|
||||
|
||||
export const ANTENNA_ADDER_RESET = 'ANTENNA_ADDER_RESET';
|
||||
export const ANTENNA_ADDER_SETUP = 'ANTENNA_ADDER_SETUP';
|
||||
|
||||
export const ANTENNA_ADDER_ANTENNAS_FETCH_REQUEST = 'ANTENNA_ADDER_ANTENNAS_FETCH_REQUEST';
|
||||
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_RESET = 'ANTENNA_ADDER_RESET';
|
||||
export const ANTENNA_ADDER_SETUP = 'ANTENNA_ADDER_SETUP';
|
||||
export const fetchAntenna = id => (dispatch, getState) => {
|
||||
if (getState().getIn(['antennas', id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchAntennaRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/antennas/${id}`)
|
||||
.then(({ data }) => dispatch(fetchAntennaSuccess(data)))
|
||||
.catch(err => dispatch(fetchAntennaFail(id, err)));
|
||||
};
|
||||
|
||||
export const fetchAntennaRequest = id => ({
|
||||
type: ANTENNA_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchAntennaSuccess = antenna => ({
|
||||
type: ANTENNA_FETCH_SUCCESS,
|
||||
antenna,
|
||||
});
|
||||
|
||||
export const fetchAntennaFail = (id, error) => ({
|
||||
type: ANTENNA_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchAntennas = () => (dispatch, getState) => {
|
||||
dispatch(fetchAntennasRequest());
|
||||
|
@ -47,6 +100,113 @@ export const fetchAntennasFail = error => ({
|
|||
error,
|
||||
});
|
||||
|
||||
export const submitAntennaEditor = shouldReset => (dispatch, getState) => {
|
||||
const antennaId = getState().getIn(['antennaEditor', 'antennaId']);
|
||||
const title = getState().getIn(['antennaEditor', 'title']);
|
||||
|
||||
if (antennaId === null) {
|
||||
dispatch(createAntenna(title, shouldReset));
|
||||
} else {
|
||||
dispatch(updateAntenna(antennaId, title, shouldReset));
|
||||
}
|
||||
};
|
||||
|
||||
export const setupAntennaEditor = antennaId => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ANTENNA_EDITOR_SETUP,
|
||||
antenna: getState().getIn(['antennas', antennaId]),
|
||||
});
|
||||
|
||||
dispatch(fetchAntennaAccounts(antennaId));
|
||||
};
|
||||
|
||||
export const changeAntennaEditorTitle = value => ({
|
||||
type: ANTENNA_EDITOR_TITLE_CHANGE,
|
||||
value,
|
||||
});
|
||||
|
||||
export const createAntenna = (title, shouldReset) => (dispatch, getState) => {
|
||||
dispatch(createAntennaRequest());
|
||||
|
||||
api(getState).post('/api/v1/antennas', { title }).then(({ data }) => {
|
||||
dispatch(createAntennaSuccess(data));
|
||||
|
||||
if (shouldReset) {
|
||||
dispatch(resetAntennaEditor());
|
||||
}
|
||||
}).catch(err => dispatch(createAntennaFail(err)));
|
||||
};
|
||||
|
||||
export const createAntennaRequest = () => ({
|
||||
type: ANTENNA_CREATE_REQUEST,
|
||||
});
|
||||
|
||||
export const createAntennaSuccess = antenna => ({
|
||||
type: ANTENNA_CREATE_SUCCESS,
|
||||
antenna,
|
||||
});
|
||||
|
||||
export const createAntennaFail = error => ({
|
||||
type: ANTENNA_CREATE_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const updateAntenna = (id, title, shouldReset, stl, with_media_only, ignore_reblog) => (dispatch, getState) => {
|
||||
dispatch(updateAntennaRequest(id));
|
||||
|
||||
api(getState).put(`/api/v1/antennas/${id}`, { title, stl, with_media_only, ignore_reblog }).then(({ data }) => {
|
||||
dispatch(updateAntennaSuccess(data));
|
||||
|
||||
if (shouldReset) {
|
||||
dispatch(resetAntennaEditor());
|
||||
}
|
||||
}).catch(err => dispatch(updateAntennaFail(id, err)));
|
||||
};
|
||||
|
||||
export const updateAntennaRequest = id => ({
|
||||
type: ANTENNA_UPDATE_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const updateAntennaSuccess = antenna => ({
|
||||
type: ANTENNA_UPDATE_SUCCESS,
|
||||
antenna,
|
||||
});
|
||||
|
||||
export const updateAntennaFail = (id, error) => ({
|
||||
type: ANTENNA_UPDATE_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const resetAntennaEditor = () => ({
|
||||
type: ANTENNA_EDITOR_RESET,
|
||||
});
|
||||
|
||||
export const deleteAntenna = id => (dispatch, getState) => {
|
||||
dispatch(deleteAntennaRequest(id));
|
||||
|
||||
api(getState).delete(`/api/v1/antennas/${id}`)
|
||||
.then(() => dispatch(deleteAntennaSuccess(id)))
|
||||
.catch(err => dispatch(deleteAntennaFail(id, err)));
|
||||
};
|
||||
|
||||
export const deleteAntennaRequest = id => ({
|
||||
type: ANTENNA_DELETE_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const deleteAntennaSuccess = id => ({
|
||||
type: ANTENNA_DELETE_SUCCESS,
|
||||
id,
|
||||
});
|
||||
|
||||
export const deleteAntennaFail = (id, error) => ({
|
||||
type: ANTENNA_DELETE_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchAntennaAccounts = antennaId => (dispatch, getState) => {
|
||||
dispatch(fetchAntennaAccountsRequest(antennaId));
|
||||
|
||||
|
@ -74,97 +234,95 @@ export const fetchAntennaAccountsFail = (id, error) => ({
|
|||
error,
|
||||
});
|
||||
|
||||
export const addAccountToAntenna = (antennaId, accountId) => (dispatch, getState) => {
|
||||
dispatch(addAccountToAntennaRequest(antennaId, accountId));
|
||||
export const fetchAntennaSuggestions = q => (dispatch, getState) => {
|
||||
const params = {
|
||||
q,
|
||||
resolve: false,
|
||||
};
|
||||
|
||||
api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => {
|
||||
dispatch(importFetchedAccounts(data));
|
||||
dispatch(fetchAntennaSuggestionsReady(q, data));
|
||||
}).catch(error => dispatch(showAlertForError(error)));
|
||||
};
|
||||
|
||||
export const fetchAntennaSuggestionsReady = (query, accounts) => ({
|
||||
type: ANTENNA_EDITOR_SUGGESTIONS_READY,
|
||||
query,
|
||||
accounts,
|
||||
});
|
||||
|
||||
export const clearAntennaSuggestions = () => ({
|
||||
type: ANTENNA_EDITOR_SUGGESTIONS_CLEAR,
|
||||
});
|
||||
|
||||
export const changeAntennaSuggestions = value => ({
|
||||
type: ANTENNA_EDITOR_SUGGESTIONS_CHANGE,
|
||||
value,
|
||||
});
|
||||
|
||||
export const addToAntennaEditor = accountId => (dispatch, getState) => {
|
||||
dispatch(addToAntenna(getState().getIn(['antennaEditor', 'antennaId']), accountId));
|
||||
};
|
||||
|
||||
export const addToAntenna = (antennaId, accountId) => (dispatch, getState) => {
|
||||
dispatch(addToAntennaRequest(antennaId, accountId));
|
||||
|
||||
api(getState).post(`/api/v1/antennas/${antennaId}/accounts`, { account_ids: [accountId] })
|
||||
.then(() => dispatch(addAccountToAntennaSuccess(antennaId, accountId)))
|
||||
.catch(err => dispatch(addAccountToAntennaFail(antennaId, accountId, err)));
|
||||
.then(() => dispatch(addToAntennaSuccess(antennaId, accountId)))
|
||||
.catch(err => dispatch(addToAntennaFail(antennaId, accountId, err)));
|
||||
};
|
||||
|
||||
export const addAccountToAntennaRequest = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_ADD_ACCOUNT_REQUEST,
|
||||
export const addToAntennaRequest = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_ADD_REQUEST,
|
||||
antennaId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const addAccountToAntennaSuccess = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_ADD_ACCOUNT_SUCCESS,
|
||||
export const addToAntennaSuccess = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_ADD_SUCCESS,
|
||||
antennaId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const addAccountToAntennaFail = (antennaId, accountId, error) => ({
|
||||
type: ANTENNA_EDITOR_ADD_ACCOUNT_FAIL,
|
||||
export const addToAntennaFail = (antennaId, accountId, error) => ({
|
||||
type: ANTENNA_EDITOR_ADD_FAIL,
|
||||
antennaId,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
export const removeAccountFromAntennaEditor = accountId => (dispatch, getState) => {
|
||||
dispatch(removeAccountFromAntenna(getState().getIn(['antennaEditor', 'antennaId']), accountId));
|
||||
export const removeFromAntennaEditor = accountId => (dispatch, getState) => {
|
||||
dispatch(removeFromAntenna(getState().getIn(['antennaEditor', 'antennaId']), accountId));
|
||||
};
|
||||
|
||||
export const removeAccountFromAntenna = (antennaId, accountId) => (dispatch, getState) => {
|
||||
dispatch(removeAccountFromAntennaRequest(antennaId, accountId));
|
||||
export const removeFromAntenna = (antennaId, accountId) => (dispatch, getState) => {
|
||||
dispatch(removeFromAntennaRequest(antennaId, accountId));
|
||||
|
||||
api(getState).delete(`/api/v1/antennas/${antennaId}/accounts`, { params: { account_ids: [accountId] } })
|
||||
.then(() => dispatch(removeAccountFromAntennaSuccess(antennaId, accountId)))
|
||||
.catch(err => dispatch(removeAccountFromAntennaFail(antennaId, accountId, err)));
|
||||
.then(() => dispatch(removeFromAntennaSuccess(antennaId, accountId)))
|
||||
.catch(err => dispatch(removeFromAntennaFail(antennaId, accountId, err)));
|
||||
};
|
||||
|
||||
export const removeAccountFromAntennaRequest = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_REMOVE_ACCOUNT_REQUEST,
|
||||
export const removeFromAntennaRequest = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_REMOVE_REQUEST,
|
||||
antennaId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const removeAccountFromAntennaSuccess = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_REMOVE_ACCOUNT_SUCCESS,
|
||||
export const removeFromAntennaSuccess = (antennaId, accountId) => ({
|
||||
type: ANTENNA_EDITOR_REMOVE_SUCCESS,
|
||||
antennaId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const removeAccountFromAntennaFail = (antennaId, accountId, error) => ({
|
||||
type: ANTENNA_EDITOR_REMOVE_ACCOUNT_FAIL,
|
||||
export const removeFromAntennaFail = (antennaId, accountId, error) => ({
|
||||
type: ANTENNA_EDITOR_REMOVE_FAIL,
|
||||
antennaId,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
export const addToAntennaAdder = antennaId => (dispatch, getState) => {
|
||||
dispatch(addAccountToAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
|
||||
};
|
||||
|
||||
export const removeFromAntennaAdder = antennaId => (dispatch, getState) => {
|
||||
dispatch(removeAccountFromAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
|
||||
};
|
||||
|
||||
export const fetchAccountAntennas = accountId => (dispatch, getState) => {
|
||||
dispatch(fetchAccountAntennasRequest(accountId));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${accountId}/antennas`)
|
||||
.then(({ data }) => dispatch(fetchAccountAntennasSuccess(accountId, data)))
|
||||
.catch(err => dispatch(fetchAccountAntennasFail(accountId, err)));
|
||||
};
|
||||
|
||||
export const fetchAccountAntennasRequest = id => ({
|
||||
type: ANTENNA_ADDER_ANTENNAS_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchAccountAntennasSuccess = (id, antennas) => ({
|
||||
type: ANTENNA_ADDER_ANTENNAS_FETCH_SUCCESS,
|
||||
id,
|
||||
antennas,
|
||||
});
|
||||
|
||||
export const fetchAccountAntennasFail = (id, err) => ({
|
||||
type: ANTENNA_ADDER_ANTENNAS_FETCH_FAIL,
|
||||
id,
|
||||
err,
|
||||
});
|
||||
|
||||
export const resetAntennaAdder = () => ({
|
||||
type: ANTENNA_ADDER_RESET,
|
||||
});
|
||||
|
@ -178,3 +336,36 @@ export const setupAntennaAdder = accountId => (dispatch, getState) => {
|
|||
dispatch(fetchAccountAntennas(accountId));
|
||||
};
|
||||
|
||||
export const fetchAccountAntennas = accountId => (dispatch, getState) => {
|
||||
dispatch(fetchAccountAntennasRequest(accountId));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${accountId}/antennas`)
|
||||
.then(({ data }) => dispatch(fetchAccountAntennasSuccess(accountId, data)))
|
||||
.catch(err => dispatch(fetchAccountAntennasFail(accountId, err)));
|
||||
};
|
||||
|
||||
export const fetchAccountAntennasRequest = id => ({
|
||||
type:ANTENNA_ADDER_ANTENNAS_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchAccountAntennasSuccess = (id, antennas) => ({
|
||||
type: ANTENNA_ADDER_ANTENNAS_FETCH_SUCCESS,
|
||||
id,
|
||||
antennas,
|
||||
});
|
||||
|
||||
export const fetchAccountAntennasFail = (id, err) => ({
|
||||
type: ANTENNA_ADDER_ANTENNAS_FETCH_FAIL,
|
||||
id,
|
||||
err,
|
||||
});
|
||||
|
||||
export const addToAntennaAdder = antennaId => (dispatch, getState) => {
|
||||
dispatch(addToAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
|
||||
};
|
||||
|
||||
export const removeFromAntennaAdder = antennaId => (dispatch, getState) => {
|
||||
dispatch(removeFromAntenna(antennaId, getState().getIn(['antennaAdder', 'accountId'])));
|
||||
};
|
||||
|
||||
|
|
|
@ -238,7 +238,6 @@ export const fetchListSuggestions = q => (dispatch, getState) => {
|
|||
const params = {
|
||||
q,
|
||||
resolve: false,
|
||||
limit: 4,
|
||||
following: true,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import { injectIntl } from 'react-intl';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Avatar } from '../../../components/avatar';
|
||||
import { DisplayName } from '../../../components/display_name';
|
||||
import { makeGetAccount } from '../../../selectors';
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
const mapStateToProps = (state, { accountId }) => ({
|
||||
account: getAccount(state, accountId),
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
class Account extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account } = this.props;
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps)(injectIntl(Account));
|
|
@ -6,14 +6,14 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { removeFromAntennaAdder, addToAntennaAdder } from '../../../actions/antennas';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||
remove: { id: 'antennas.account.remove', defaultMessage: 'Remove from antenna' },
|
||||
add: { id: 'antennas.account.add', defaultMessage: 'Add to antenna' },
|
||||
});
|
||||
|
||||
const MapStateToProps = (state, { antennaId, added }) => ({
|
||||
|
|
|
@ -8,8 +8,9 @@ import { connect } from 'react-redux';
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
import { setupAntennaAdder, resetAntennaAdder } from '../../actions/antennas';
|
||||
import NewAntennaForm from '../antennas/components/new_antenna_form';
|
||||
import Account from '../list_adder/components/account';
|
||||
|
||||
import Account from './components/account';
|
||||
import Antenna from './components/antenna';
|
||||
// hack
|
||||
|
||||
|
@ -60,8 +61,11 @@ class AntennaAdder extends ImmutablePureComponent {
|
|||
<Account accountId={accountId} />
|
||||
</div>
|
||||
|
||||
<NewAntennaForm />
|
||||
|
||||
|
||||
<div className='list-adder__lists'>
|
||||
{antennaIds.map(AntennaId => <Antenna key={AntennaId} antennaId={AntennaId} />)}
|
||||
{antennaIds.map(antennaId => <Antenna key={antennaId} antennaId={antennaId} />)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { removeFromAntennaEditor, addToAntennaEditor } from '../../../actions/antennas';
|
||||
import { Avatar } from '../../../components/avatar';
|
||||
import { DisplayName } from '../../../components/display_name';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
import { makeGetAccount } from '../../../selectors';
|
||||
|
||||
const messages = defineMessages({
|
||||
remove: { id: 'antennas.account.remove', defaultMessage: 'Remove from antenna' },
|
||||
add: { id: 'antennas.account.add', defaultMessage: 'Add to antenna' },
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
const mapStateToProps = (state, { accountId, added }) => ({
|
||||
account: getAccount(state, accountId),
|
||||
added: typeof added === 'undefined' ? state.getIn(['antennaEditor', 'accounts', 'items']).includes(accountId) : added,
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, { accountId }) => ({
|
||||
onRemove: () => dispatch(removeFromAntennaEditor(accountId)),
|
||||
onAdd: () => dispatch(addToAntennaEditor(accountId)),
|
||||
});
|
||||
|
||||
class Account extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onRemove: PropTypes.func.isRequired,
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
added: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
added: false,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, intl, onRemove, onAdd, added } = this.props;
|
||||
|
||||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
} else {
|
||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
|
||||
<div className='account__relationship'>
|
||||
{button}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(Account));
|
|
@ -0,0 +1,73 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { changeAntennaEditorTitle, submitAntennaEditor } from '../../../actions/antennas';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'antennas.edit.submit', defaultMessage: 'Change title' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
value: state.getIn(['antennaEditor', 'title']),
|
||||
disabled: !state.getIn(['antennaEditor', 'isChanged']) || !state.getIn(['antennaEditor', 'title']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChange: value => dispatch(changeAntennaEditorTitle(value)),
|
||||
onSubmit: () => dispatch(submitAntennaEditor(false)),
|
||||
});
|
||||
|
||||
class AntennaForm extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
this.props.onChange(e.target.value);
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, disabled, intl } = this.props;
|
||||
|
||||
const title = intl.formatMessage(messages.title);
|
||||
|
||||
return (
|
||||
<form className='column-inline-form' onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
className='setting-text'
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
icon='check'
|
||||
title={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(AntennaForm));
|
|
@ -0,0 +1,81 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { fetchAntennaSuggestions, clearAntennaSuggestions, changeAntennaSuggestions } from '../../../actions/antennas';
|
||||
|
||||
const messages = defineMessages({
|
||||
search: { id: 'antennas.search', defaultMessage: 'Search among people you follow' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
value: state.getIn(['antennaEditor', 'suggestions', 'value']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onSubmit: value => dispatch(fetchAntennaSuggestions(value)),
|
||||
onClear: () => dispatch(clearAntennaSuggestions()),
|
||||
onChange: value => dispatch(changeAntennaSuggestions(value)),
|
||||
});
|
||||
|
||||
class Search extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onClear: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
this.props.onChange(e.target.value);
|
||||
};
|
||||
|
||||
handleKeyUp = e => {
|
||||
if (e.keyCode === 13) {
|
||||
this.props.onSubmit(this.props.value);
|
||||
}
|
||||
};
|
||||
|
||||
handleClear = () => {
|
||||
this.props.onClear();
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, intl } = this.props;
|
||||
const hasValue = value.length > 0;
|
||||
|
||||
return (
|
||||
<div className='list-editor__search search'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.search)}</span>
|
||||
|
||||
<input
|
||||
className='search__input'
|
||||
type='text'
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
placeholder={intl.formatMessage(messages.search)}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' className={classNames({ active: !hasValue })} />
|
||||
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Search));
|
83
app/javascript/mastodon/features/antenna_editor/index.jsx
Normal file
83
app/javascript/mastodon/features/antenna_editor/index.jsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { injectIntl } from 'react-intl';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import spring from 'react-motion/lib/spring';
|
||||
|
||||
import { setupAntennaEditor, clearAntennaSuggestions, resetAntennaEditor } from '../../actions/antennas';
|
||||
import Motion from '../ui/util/optional_motion';
|
||||
|
||||
import Account from './components/account';
|
||||
import EditAntennaForm from './components/edit_antenna_form';
|
||||
import Search from './components/search';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['antennaEditor', 'accounts', 'items']),
|
||||
searchAccountIds: state.getIn(['antennaEditor', 'suggestions', 'items']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onInitialize: antennaId => dispatch(setupAntennaEditor(antennaId)),
|
||||
onClear: () => dispatch(clearAntennaSuggestions()),
|
||||
onReset: () => dispatch(resetAntennaEditor()),
|
||||
});
|
||||
|
||||
class AntennaEditor extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
antennaId: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onInitialize: PropTypes.func.isRequired,
|
||||
onClear: PropTypes.func.isRequired,
|
||||
onReset: PropTypes.func.isRequired,
|
||||
accountIds: ImmutablePropTypes.list.isRequired,
|
||||
searchAccountIds: ImmutablePropTypes.list.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { onInitialize, antennaId } = this.props;
|
||||
onInitialize(antennaId);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
const { onReset } = this.props;
|
||||
onReset();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accountIds, searchAccountIds, onClear } = this.props;
|
||||
const showSearch = searchAccountIds.size > 0;
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal list-editor'>
|
||||
<EditAntennaForm />
|
||||
|
||||
<Search />
|
||||
|
||||
<div className='drawer__pager'>
|
||||
<div className='drawer__inner list-editor__accounts'>
|
||||
{accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
|
||||
</div>
|
||||
|
||||
{showSearch && <div role='button' tabIndex={-1} className='drawer__backdrop' onClick={onClear} />}
|
||||
|
||||
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
|
||||
{({ x }) => (
|
||||
<div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
|
||||
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(AntennaEditor));
|
256
app/javascript/mastodon/features/antenna_setting/index.jsx
Normal file
256
app/javascript/mastodon/features/antenna_setting/index.jsx
Normal file
|
@ -0,0 +1,256 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import { fetchAntenna, deleteAntenna, updateAntenna } from 'mastodon/actions/antennas';
|
||||
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import Button from 'mastodon/components/button';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteMessage: { id: 'confirmations.delete_antenna.message', defaultMessage: 'Are you sure you want to permanently delete this antenna?' },
|
||||
deleteConfirm: { id: 'confirmations.delete_antenna.confirm', defaultMessage: 'Delete' },
|
||||
editAccounts: { id: 'antennas.edit_accounts', defaultMessage: 'Edit accounts' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
antenna: state.getIn(['antennas', props.params.id]),
|
||||
});
|
||||
|
||||
class AntennaSetting extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
multiColumn: PropTypes.bool,
|
||||
antenna: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
const { columnId, dispatch } = this.props;
|
||||
|
||||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('ANTENNA', { id: this.props.params.id }));
|
||||
this.context.router.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
handleMove = (dir) => {
|
||||
const { columnId, dispatch } = this.props;
|
||||
dispatch(moveColumn(columnId, dir));
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
|
||||
dispatch(fetchAntenna(id));
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = nextProps.params;
|
||||
|
||||
if (id !== this.props.params.id) {
|
||||
dispatch(fetchAntenna(id));
|
||||
}
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
};
|
||||
|
||||
handleEditClick = () => {
|
||||
this.props.dispatch(openModal({
|
||||
modalType: 'ANTENNA_EDITOR',
|
||||
modalProps: { antennaId: this.props.params.id },
|
||||
}));
|
||||
};
|
||||
|
||||
handleEditAntennaClick = () => {
|
||||
window.open(`/antennas/${this.props.params.id}/edit`, '_blank');
|
||||
}
|
||||
|
||||
handleDeleteClick = () => {
|
||||
const { dispatch, columnId, intl } = this.props;
|
||||
const { id } = this.props.params;
|
||||
|
||||
dispatch(openModal({
|
||||
modalType: 'CONFIRM',
|
||||
modalProps: {
|
||||
message: intl.formatMessage(messages.deleteMessage),
|
||||
confirm: intl.formatMessage(messages.deleteConfirm),
|
||||
onConfirm: () => {
|
||||
dispatch(deleteAntenna(id));
|
||||
|
||||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
this.context.router.history.push('/antennasw');
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
onStlToggle = ({ target }) => {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
dispatch(updateAntenna(id, undefined, false, target.checked, undefined, undefined));
|
||||
};
|
||||
|
||||
onMediaOnlyToggle = ({ target }) => {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
dispatch(updateAntenna(id, undefined, false, undefined, target.checked, undefined));
|
||||
};
|
||||
|
||||
onIgnoreReblogToggle = ({ target }) => {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
dispatch(updateAntenna(id, undefined, false, undefined, undefined, target.checked));
|
||||
};
|
||||
|
||||
render () {
|
||||
const { columnId, multiColumn, antenna, intl } = this.props;
|
||||
const { id } = this.props.params;
|
||||
const pinned = !!columnId;
|
||||
const title = antenna ? antenna.get('title') : id;
|
||||
const isStl = antenna ? antenna.get('stl') : undefined;
|
||||
const isMediaOnly = antenna ? antenna.get('with_media_only') : undefined;
|
||||
const isIgnoreReblog = antenna ? antenna.get('ignore_reblog') : undefined;
|
||||
|
||||
if (typeof antenna === 'undefined') {
|
||||
return (
|
||||
<Column>
|
||||
<div className='scrollable'>
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
} else if (antenna === false) {
|
||||
return (
|
||||
<BundleColumnError multiColumn={multiColumn} errorType='routing' />
|
||||
);
|
||||
}
|
||||
|
||||
let columnSettings;
|
||||
if (!isStl) {
|
||||
columnSettings = (
|
||||
<>
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={`antenna-${id}-mediaonly`} defaultChecked={isMediaOnly} onChange={this.onMediaOnlyToggle} />
|
||||
<label htmlFor={`antenna-${id}-mediaonly`} className='setting-toggle__label'>
|
||||
<FormattedMessage id='antennas.media_only' defaultMessage='Media only' />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={`antenna-${id}-ignorereblog`} defaultChecked={isIgnoreReblog} onChange={this.onIgnoreReblogToggle} />
|
||||
<label htmlFor={`antenna-${id}-ignorereblog`} className='setting-toggle__label'>
|
||||
<FormattedMessage id='antennas.ignore_reblog' defaultMessage='Exclude boosts' />
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
let stlAlert;
|
||||
if (isStl) {
|
||||
stlAlert = (
|
||||
<div class='antenna-setting'>
|
||||
<p><FormattedMessage id='antennas.in_stl_mode' defaultMessage='This antenna is in STL mode.' /></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
|
||||
<ColumnHeader
|
||||
icon='wifi'
|
||||
title={title}
|
||||
onPin={this.handlePin}
|
||||
onMove={this.handleMove}
|
||||
onClick={this.handleHeaderClick}
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
>
|
||||
<div className='column-settings__row column-header__links'>
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditAntennaClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='anntennas.edit' defaultMessage='Edit antenna' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
||||
<Icon id='trash' /> <FormattedMessage id='antennas.delete' defaultMessage='Delete antenna' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={`antenna-${id}-stl`} defaultChecked={isStl} onChange={this.onStlToggle} />
|
||||
<label htmlFor={`antenna-${id}-stl`} className='setting-toggle__label'>
|
||||
<FormattedMessage id='antennas.stl' defaultMessage='STL mode' />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{columnSettings}
|
||||
</ColumnHeader>
|
||||
|
||||
{stlAlert}
|
||||
{!isStl && (
|
||||
<div class='antenna-setting'>
|
||||
{antenna.get('list') ? (
|
||||
<p><FormattedMessage id='antennas.related_list' defaultMessage='This antenna is related to {listTitle}.' values={{ listTitle: antenna.getIn(['list', 'title']) }} /></p>
|
||||
) : (
|
||||
<>
|
||||
<p><FormattedMessage id='antennas.not_related_list' defaultMessage='This antenna is not related list. Posts will appear in home timeline. Open edit page to set list.' /></p>
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditAntennaClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='anntennas.edit' defaultMessage='Edit antenna' />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<h3><FormattedMessage id='antennas.accounts' defaultMessage='{count} accounts' values={{ count: antenna.get('accounts_count') }} /></h3>
|
||||
<Button text={intl.formatMessage(messages.editAccounts)} onClick={this.handleEditClick} />
|
||||
|
||||
<h3><FormattedMessage id='antennas.domains' defaultMessage='{count} domains' values={{ count: antenna.get('domains_count') }} /></h3>
|
||||
<h3><FormattedMessage id='antennas.tags' defaultMessage='{count} tags' values={{ count: antenna.get('tags_count') }} /></h3>
|
||||
<h3><FormattedMessage id='antennas.keywords' defaultMessage='{count} keywords' values={{ count: antenna.get('keywords_count') }} /></h3>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Helmet>
|
||||
<title>{title}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(AntennaSetting));
|
|
@ -0,0 +1,80 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { changeAntennaEditorTitle, submitAntennaEditor } from 'mastodon/actions/antennas';
|
||||
import Button from 'mastodon/components/button';
|
||||
|
||||
const messages = defineMessages({
|
||||
label: { id: 'antennas.new.title_placeholder', defaultMessage: 'New antenna title' },
|
||||
title: { id: 'antennas.new.create', defaultMessage: 'Add antenna' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
value: state.getIn(['antennaEditor', 'title']),
|
||||
disabled: state.getIn(['antennaEditor', 'isSubmitting']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChange: value => dispatch(changeAntennaEditorTitle(value)),
|
||||
onSubmit: () => dispatch(submitAntennaEditor(true)),
|
||||
});
|
||||
|
||||
class NewAntennaForm extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
this.props.onChange(e.target.value);
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, disabled, intl } = this.props;
|
||||
|
||||
const label = intl.formatMessage(messages.label);
|
||||
const title = intl.formatMessage(messages.title);
|
||||
|
||||
return (
|
||||
<form className='column-inline-form' onSubmit={this.handleSubmit}>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
|
||||
<input
|
||||
className='setting-text'
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
placeholder={label}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<Button
|
||||
disabled={disabled || !value}
|
||||
text={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(NewAntennaForm));
|
93
app/javascript/mastodon/features/antennas/index.jsx
Normal file
93
app/javascript/mastodon/features/antennas/index.jsx
Normal file
|
@ -0,0 +1,93 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { fetchAntennas } from 'mastodon/actions/antennas';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||
import ColumnLink from 'mastodon/features/ui/components/column_link';
|
||||
import ColumnSubheading from 'mastodon/features/ui/components/column_subheading';
|
||||
|
||||
import NewAntennaForm from './components/new_antenna_form';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.antennas', defaultMessage: 'Antennas' },
|
||||
subheading: { id: 'antennas.subheading', defaultMessage: 'Your antennas' },
|
||||
});
|
||||
|
||||
const getOrderedAntennas = createSelector([state => state.get('antennas')], antennas => {
|
||||
if (!antennas) {
|
||||
return antennas;
|
||||
}
|
||||
|
||||
return antennas.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')));
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
antennas: getOrderedAntennas(state),
|
||||
});
|
||||
|
||||
class Antennas extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
antennas: ImmutablePropTypes.list,
|
||||
intl: PropTypes.object.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillMount () {
|
||||
this.props.dispatch(fetchAntennas());
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl, antennas, multiColumn } = this.props;
|
||||
|
||||
if (!antennas) {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.antennas' defaultMessage="You don't have any antennas yet. When you create one, it will show up here." />;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='wifi' multiColumn={multiColumn} />
|
||||
|
||||
<NewAntennaForm />
|
||||
|
||||
<ScrollableList
|
||||
scrollKey='antennas'
|
||||
emptyMessage={emptyMessage}
|
||||
prepend={<ColumnSubheading text={intl.formatMessage(messages.subheading)} />}
|
||||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{antennas.map(antenna =>
|
||||
<ColumnLink key={antenna.get('id')} to={`/antennasw/${antenna.get('id')}`} icon='wifi' text={antenna.get('title')} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.heading)}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Antennas));
|
|
@ -11,6 +11,7 @@ import {
|
|||
EmbedModal,
|
||||
ListEditor,
|
||||
ListAdder,
|
||||
AntennaEditor,
|
||||
AntennaAdder,
|
||||
CompareHistoryModal,
|
||||
FilterModal,
|
||||
|
@ -46,6 +47,7 @@ export const MODAL_COMPONENTS = {
|
|||
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
|
||||
'EMBED': EmbedModal,
|
||||
'LIST_EDITOR': ListEditor,
|
||||
'ANTENNA_EDITOR': AntennaEditor,
|
||||
'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
|
||||
'LIST_ADDER': ListAdder,
|
||||
'ANTENNA_ADDER': AntennaAdder,
|
||||
|
|
|
@ -27,6 +27,7 @@ const messages = defineMessages({
|
|||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
|
||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||
antennas: { id: 'navigation_bar.antennas', defaultMessage: 'Antennas' },
|
||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
|
||||
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
|
||||
|
@ -109,6 +110,7 @@ class NavigationPanel extends Component {
|
|||
|
||||
{signedIn && (
|
||||
<>
|
||||
<ColumnLink transparent to='/antennasw' icon='wifi' text={intl.formatMessage(messages.antennas)} />
|
||||
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
|
||||
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
|
||||
<hr />
|
||||
|
|
|
@ -60,6 +60,8 @@ import {
|
|||
Mutes,
|
||||
PinnedStatuses,
|
||||
Lists,
|
||||
Antennas,
|
||||
AntennaSetting,
|
||||
Directory,
|
||||
Explore,
|
||||
ReactionDeck,
|
||||
|
@ -207,6 +209,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
<WrappedRoute path={['/conversations', '/timelines/direct']} component={DirectTimeline} content={children} />
|
||||
<WrappedRoute path='/tags/:id' component={HashtagTimeline} content={children} />
|
||||
<WrappedRoute path='/lists/:id' component={ListTimeline} content={children} />
|
||||
<WrappedRoute path='/antennasw/:id' component={AntennaSetting} content={children} />
|
||||
<WrappedRoute path='/notifications' component={Notifications} content={children} />
|
||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
|
||||
<WrappedRoute path='/emoji_reactions' component={EmojiReactedStatuses} content={children} />
|
||||
|
@ -248,6 +251,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
<WrappedRoute path='/followed_tags' component={FollowedTags} content={children} />
|
||||
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||
<WrappedRoute path='/lists' component={Lists} content={children} />
|
||||
<WrappedRoute path='/antennasw' component={Antennas} content={children} />
|
||||
|
||||
<Route component={BundleColumnError} />
|
||||
</WrappedSwitch>
|
||||
|
|
|
@ -42,6 +42,10 @@ export function Lists () {
|
|||
return import(/* webpackChunkName: "features/lists" */'../../lists');
|
||||
}
|
||||
|
||||
export function Antennas () {
|
||||
return import(/* webpackChunkName: "features/antennas" */'../../antennas');
|
||||
}
|
||||
|
||||
export function Status () {
|
||||
return import(/* webpackChunkName: "features/status" */'../../status');
|
||||
}
|
||||
|
@ -158,6 +162,14 @@ export function AntennaAdder () {
|
|||
return import(/*webpackChunkName: "features/antenna_adder" */'../../antenna_adder');
|
||||
}
|
||||
|
||||
export function AntennaEditor () {
|
||||
return import(/*webpackChunkName: "features/antenna_editor" */'../../antenna_editor');
|
||||
}
|
||||
|
||||
export function AntennaSetting () {
|
||||
return import(/*webpackChunkName: "features/antenna_setting" */'../../antenna_setting');
|
||||
}
|
||||
|
||||
export function Tesseract () {
|
||||
return import(/*webpackChunkName: "tesseract" */'tesseract.js');
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import {
|
|||
ANTENNA_ADDER_ANTENNAS_FETCH_REQUEST,
|
||||
ANTENNA_ADDER_ANTENNAS_FETCH_SUCCESS,
|
||||
ANTENNA_ADDER_ANTENNAS_FETCH_FAIL,
|
||||
ANTENNA_EDITOR_ADD_ACCOUNT_SUCCESS,
|
||||
ANTENNA_EDITOR_REMOVE_ACCOUNT_SUCCESS,
|
||||
ANTENNA_EDITOR_ADD_SUCCESS,
|
||||
ANTENNA_EDITOR_REMOVE_SUCCESS,
|
||||
} from '../actions/antennas';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -38,9 +38,9 @@ export default function antennaAdderReducer(state = initialState, action) {
|
|||
map.set('loaded', true);
|
||||
map.set('items', ImmutableList(action.antennas.map(item => item.id)));
|
||||
}));
|
||||
case ANTENNA_EDITOR_ADD_ACCOUNT_SUCCESS:
|
||||
case ANTENNA_EDITOR_ADD_SUCCESS:
|
||||
return state.updateIn(['antennas', 'items'], antenna => antenna.unshift(action.antennaId));
|
||||
case ANTENNA_EDITOR_REMOVE_ACCOUNT_SUCCESS:
|
||||
case ANTENNA_EDITOR_REMOVE_SUCCESS:
|
||||
return state.updateIn(['antennas', 'items'], antenna => antenna.filterNot(item => item === action.antennaId));
|
||||
default:
|
||||
return state;
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import {
|
||||
ANTENNA_CREATE_REQUEST,
|
||||
ANTENNA_CREATE_FAIL,
|
||||
ANTENNA_CREATE_SUCCESS,
|
||||
ANTENNA_UPDATE_REQUEST,
|
||||
ANTENNA_UPDATE_FAIL,
|
||||
ANTENNA_UPDATE_SUCCESS,
|
||||
ANTENNA_EDITOR_RESET,
|
||||
ANTENNA_EDITOR_SETUP,
|
||||
ANTENNA_EDITOR_TITLE_CHANGE,
|
||||
ANTENNA_ACCOUNTS_FETCH_REQUEST,
|
||||
ANTENNA_ACCOUNTS_FETCH_SUCCESS,
|
||||
ANTENNA_ACCOUNTS_FETCH_FAIL,
|
||||
ANTENNA_EDITOR_SUGGESTIONS_READY,
|
||||
ANTENNA_EDITOR_SUGGESTIONS_CLEAR,
|
||||
ANTENNA_EDITOR_SUGGESTIONS_CHANGE,
|
||||
ANTENNA_EDITOR_ADD_SUCCESS,
|
||||
ANTENNA_EDITOR_REMOVE_SUCCESS,
|
||||
} from '../actions/antennas';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -11,16 +25,51 @@ const initialState = ImmutableMap({
|
|||
isSubmitting: false,
|
||||
isChanged: false,
|
||||
title: '',
|
||||
accountsCount: 0,
|
||||
|
||||
accounts: ImmutableMap({
|
||||
items: ImmutableList(),
|
||||
loaded: false,
|
||||
isLoading: false,
|
||||
}),
|
||||
|
||||
suggestions: ImmutableMap({
|
||||
value: '',
|
||||
items: ImmutableList(),
|
||||
}),
|
||||
});
|
||||
|
||||
export default function antennaEditorReducer(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case ANTENNA_EDITOR_RESET:
|
||||
return initialState;
|
||||
case ANTENNA_EDITOR_SETUP:
|
||||
return state.withMutations(map => {
|
||||
map.set('antennaId', action.antenna.get('id'));
|
||||
map.set('title', action.antenna.get('title'));
|
||||
map.set('accountsCount', action.antenna.get('accounts_count'));
|
||||
map.set('isSubmitting', false);
|
||||
});
|
||||
case ANTENNA_EDITOR_TITLE_CHANGE:
|
||||
return state.withMutations(map => {
|
||||
map.set('title', action.value);
|
||||
map.set('isChanged', true);
|
||||
});
|
||||
case ANTENNA_CREATE_REQUEST:
|
||||
case ANTENNA_UPDATE_REQUEST:
|
||||
return state.withMutations(map => {
|
||||
map.set('isSubmitting', true);
|
||||
map.set('isChanged', false);
|
||||
});
|
||||
case ANTENNA_CREATE_FAIL:
|
||||
case ANTENNA_UPDATE_FAIL:
|
||||
return state.set('isSubmitting', false);
|
||||
case ANTENNA_CREATE_SUCCESS:
|
||||
case ANTENNA_UPDATE_SUCCESS:
|
||||
return state.withMutations(map => {
|
||||
map.set('isSubmitting', false);
|
||||
map.set('antennaId', action.antenna.id);
|
||||
});
|
||||
case ANTENNA_ACCOUNTS_FETCH_REQUEST:
|
||||
return state.setIn(['accounts', 'isLoading'], true);
|
||||
case ANTENNA_ACCOUNTS_FETCH_FAIL:
|
||||
|
@ -31,6 +80,19 @@ export default function antennaEditorReducer(state = initialState, action) {
|
|||
map.set('loaded', true);
|
||||
map.set('items', ImmutableList(action.accounts.map(item => item.id)));
|
||||
}));
|
||||
case ANTENNA_EDITOR_SUGGESTIONS_CHANGE:
|
||||
return state.setIn(['suggestions', 'value'], action.value);
|
||||
case ANTENNA_EDITOR_SUGGESTIONS_READY:
|
||||
return state.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map(item => item.id)));
|
||||
case ANTENNA_EDITOR_SUGGESTIONS_CLEAR:
|
||||
return state.update('suggestions', suggestions => suggestions.withMutations(map => {
|
||||
map.set('items', ImmutableList());
|
||||
map.set('value', '');
|
||||
}));
|
||||
case ANTENNA_EDITOR_ADD_SUCCESS:
|
||||
return state.updateIn(['accounts', 'items'], antenna => antenna.unshift(action.accountId));
|
||||
case ANTENNA_EDITOR_REMOVE_SUCCESS:
|
||||
return state.updateIn(['accounts', 'items'], antenna => antenna.filterNot(item => item === action.accountId));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import {
|
||||
ANTENNA_FETCH_SUCCESS,
|
||||
ANTENNA_FETCH_FAIL,
|
||||
ANTENNAS_FETCH_SUCCESS,
|
||||
ANTENNA_CREATE_SUCCESS,
|
||||
ANTENNA_UPDATE_SUCCESS,
|
||||
ANTENNA_DELETE_SUCCESS,
|
||||
ANTENNA_EDITOR_ADD_SUCCESS,
|
||||
ANTENNA_EDITOR_REMOVE_SUCCESS,
|
||||
} from '../actions/antennas';
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
@ -18,8 +25,19 @@ const normalizeAntennas = (state, antennas) => {
|
|||
|
||||
export default function antennas(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case ANTENNA_FETCH_SUCCESS:
|
||||
case ANTENNA_CREATE_SUCCESS:
|
||||
case ANTENNA_UPDATE_SUCCESS:
|
||||
return normalizeAntenna(state, action.antenna);
|
||||
case ANTENNAS_FETCH_SUCCESS:
|
||||
return normalizeAntennas(state, action.antennas);
|
||||
case ANTENNA_DELETE_SUCCESS:
|
||||
case ANTENNA_FETCH_FAIL:
|
||||
return state.set(action.id, false);
|
||||
case ANTENNA_EDITOR_ADD_SUCCESS:
|
||||
return state.setIn([action.antennaId, 'accounts_count'], state.getIn([action.antennaId, 'accounts_count']) + 1);
|
||||
case ANTENNA_EDITOR_REMOVE_SUCCESS:
|
||||
return state.setIn([action.antennaId, 'accounts_count'], state.getIn([action.antennaId, 'accounts_count']) - 1);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -7364,6 +7364,15 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
.antenna-setting {
|
||||
margin: 8px 16px;
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
margin: 24px 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.reaction_deck_container {
|
||||
&__row {
|
||||
display: flex;
|
||||
|
|
|
@ -33,6 +33,7 @@ class Antenna < ApplicationRecord
|
|||
has_many :antenna_domains, inverse_of: :antenna, dependent: :destroy
|
||||
has_many :antenna_tags, inverse_of: :antenna, dependent: :destroy
|
||||
has_many :antenna_accounts, inverse_of: :antenna, dependent: :destroy
|
||||
has_many :accounts, through: :antenna_accounts
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :list, optional: true
|
||||
|
|
|
@ -1,9 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class REST::AntennaSerializer < ActiveModel::Serializer
|
||||
attributes :id, :title
|
||||
attributes :id, :title, :stl, :with_media_only, :ignore_reblog, :accounts_count, :domains_count, :tags_count, :keywords_count
|
||||
|
||||
class ListSerializer < ActiveModel::Serializer
|
||||
attributes :id, :title
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
end
|
||||
|
||||
has_one :list, serializer: ListSerializer, optional: true
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
|
||||
def accounts_count
|
||||
object.antenna_accounts.count
|
||||
end
|
||||
|
||||
def domains_count
|
||||
object.antenna_domains.count
|
||||
end
|
||||
|
||||
def tags_count
|
||||
object.antenna_tags.count
|
||||
end
|
||||
|
||||
def keywords_count
|
||||
object.keywords&.size || 0
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ Rails.application.routes.draw do
|
|||
/public/remote
|
||||
/conversations
|
||||
/lists/(*any)
|
||||
/antennasw/(*any)
|
||||
/notifications
|
||||
/favourites
|
||||
/emoji_reactions
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue