diff --git a/app/soapbox/actions/admin.js b/app/soapbox/actions/admin.js index ee957948d..a48636d0e 100644 --- a/app/soapbox/actions/admin.js +++ b/app/soapbox/actions/admin.js @@ -82,10 +82,10 @@ export function updateConfig(configs) { dispatch({ type: ADMIN_CONFIG_UPDATE_REQUEST, configs }); return api(getState) .post('/api/pleroma/admin/config', { configs }) - .then(({ data: { configs } }) => { - dispatch({ type: ADMIN_CONFIG_UPDATE_SUCCESS, configs }); + .then(({ data }) => { + dispatch({ type: ADMIN_CONFIG_UPDATE_SUCCESS, configs: data.configs, needsReboot: data.need_reboot }); }).catch(error => { - dispatch({ type: ADMIN_CONFIG_UPDATE_FAIL, error }); + dispatch({ type: ADMIN_CONFIG_UPDATE_FAIL, error, configs }); }); }; } diff --git a/app/soapbox/actions/mrf.js b/app/soapbox/actions/mrf.js index 9cddf85f8..e4f07a9fd 100644 --- a/app/soapbox/actions/mrf.js +++ b/app/soapbox/actions/mrf.js @@ -1,6 +1,6 @@ -import { fetchInstance } from './instance'; -import { updateConfig } from './admin'; +import { fetchConfig, updateConfig } from './admin'; import { Set as ImmutableSet } from 'immutable'; +import ConfigDB from 'soapbox/utils/config_db'; const simplePolicyMerge = (simplePolicy, host, restrictions) => { return simplePolicy.map((hosts, key) => { @@ -14,31 +14,15 @@ const simplePolicyMerge = (simplePolicy, host, restrictions) => { }); }; -const simplePolicyToConfig = simplePolicy => { - const value = simplePolicy.map((hosts, key) => ( - { tuple: [`:${key}`, hosts.toJS()] } - )).toList(); - - return [{ - group: ':pleroma', - key: ':mrf_simple', - value, - }]; -}; - export function updateMrf(host, restrictions) { return (dispatch, getState) => { - return dispatch(fetchInstance()) + return dispatch(fetchConfig()) .then(() => { - const simplePolicy = getState().getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple']); + const configs = getState().getIn(['admin', 'configs']); + const simplePolicy = ConfigDB.toSimplePolicy(configs); const merged = simplePolicyMerge(simplePolicy, host, restrictions); - const config = simplePolicyToConfig(merged); + const config = ConfigDB.fromSimplePolicy(merged); dispatch(updateConfig(config)); - - // TODO: Make this less insane - setTimeout(() => { - dispatch(fetchInstance()); - }, 1000); }); }; } diff --git a/app/soapbox/features/federation_restrictions/index.js b/app/soapbox/features/federation_restrictions/index.js index 1007e0921..2928b0f52 100644 --- a/app/soapbox/features/federation_restrictions/index.js +++ b/app/soapbox/features/federation_restrictions/index.js @@ -4,21 +4,11 @@ import { defineMessages, injectIntl } from 'react-intl'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Column from '../ui/components/column'; -import { createSelector } from 'reselect'; -import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; import RestrictedInstance from './components/restricted_instance'; import Accordion from 'soapbox/features/ui/components/accordion'; import ScrollableList from 'soapbox/components/scrollable_list'; import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; - -const getHosts = createSelector([ - state => state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()), -], (simplePolicy) => { - return simplePolicy - .deleteAll(['accept', 'reject_deletes', 'report_removal']) - .reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet()) - .sort(); -}); +import { makeGetHosts } from 'soapbox/selectors'; const messages = defineMessages({ heading: { id: 'column.federation_restrictions', defaultMessage: 'Federation Restrictions' }, @@ -28,6 +18,8 @@ const messages = defineMessages({ notDisclosed: { id: 'federation_restrictions.not_disclosed_message', defaultMessage: '{siteTitle} does not disclose federation restrictions through the API.' }, }); +const getHosts = makeGetHosts(); + const mapStateToProps = state => ({ siteTitle: state.getIn(['instance', 'title']), hosts: getHosts(state), diff --git a/app/soapbox/features/ui/components/edit_federation_modal.js b/app/soapbox/features/ui/components/edit_federation_modal.js index abf7c2c3f..ff284316d 100644 --- a/app/soapbox/features/ui/components/edit_federation_modal.js +++ b/app/soapbox/features/ui/components/edit_federation_modal.js @@ -6,7 +6,7 @@ import { connect } from 'react-redux'; import { defineMessages, injectIntl } from 'react-intl'; import { SimpleForm, Checkbox } from 'soapbox/features/forms'; import { makeGetRemoteInstance } from 'soapbox/selectors'; -import { Map as ImmutableMap } from 'immutable'; +import { Map as ImmutableMap, is } from 'immutable'; import { updateMrf } from 'soapbox/actions/mrf'; import snackbar from 'soapbox/actions/snackbar'; @@ -41,11 +41,23 @@ class EditFederationModal extends ImmutablePureComponent { data: ImmutableMap(), } - componentDidMount() { + hydrateState = () => { const { remoteInstance } = this.props; this.setState({ data: remoteInstance.get('federation') }); } + componentDidMount() { + this.hydrateState(); + } + + componentDidUpdate(prevProps) { + const { remoteInstance } = this.props; + + if (!is(prevProps.remoteInstance, remoteInstance)) { + this.hydrateState(); + } + } + handleDataChange = key => { return ({ target }) => { const { data } = this.state; diff --git a/app/soapbox/pages/remote_instance_page.js b/app/soapbox/pages/remote_instance_page.js index f134ad160..218dbcf72 100644 --- a/app/soapbox/pages/remote_instance_page.js +++ b/app/soapbox/pages/remote_instance_page.js @@ -9,9 +9,11 @@ import LinkFooter from 'soapbox/features/ui/components/link_footer'; import { getFeatures } from 'soapbox/utils/features'; import InstanceInfoPanel from 'soapbox/features/ui/components/instance_info_panel'; import { federationRestrictionsDisclosed } from 'soapbox/utils/state'; +import { isAdmin } from 'soapbox/utils/accounts'; const mapStateToProps = state => { const me = state.get('me'); + const account = state.getIn(['accounts', me]); const features = getFeatures(state.get('instance')); return { @@ -19,6 +21,7 @@ const mapStateToProps = state => { showTrendsPanel: features.trends, showWhoToFollowPanel: features.suggestions, disclosed: federationRestrictionsDisclosed(state), + isAdmin: isAdmin(account), }; }; @@ -26,7 +29,7 @@ export default @connect(mapStateToProps) class RemoteInstancePage extends ImmutablePureComponent { render() { - const { me, children, showTrendsPanel, showWhoToFollowPanel, params: { instance: host }, disclosed } = this.props; + const { me, children, showTrendsPanel, showWhoToFollowPanel, params: { instance: host }, disclosed, isAdmin } = this.props; return (
@@ -35,7 +38,7 @@ class RemoteInstancePage extends ImmutablePureComponent {
- {disclosed && } + {(disclosed || isAdmin) && }
diff --git a/app/soapbox/reducers/admin.js b/app/soapbox/reducers/admin.js index b4978c218..4c3a0a09e 100644 --- a/app/soapbox/reducers/admin.js +++ b/app/soapbox/reducers/admin.js @@ -1,5 +1,6 @@ import { ADMIN_CONFIG_FETCH_SUCCESS, + ADMIN_CONFIG_UPDATE_SUCCESS, ADMIN_REPORTS_FETCH_SUCCESS, ADMIN_REPORTS_PATCH_REQUEST, ADMIN_REPORTS_PATCH_SUCCESS, @@ -127,6 +128,7 @@ function handleReportDiffs(state, reports) { export default function admin(state = initialState, action) { switch(action.type) { case ADMIN_CONFIG_FETCH_SUCCESS: + case ADMIN_CONFIG_UPDATE_SUCCESS: return state.set('configs', fromJS(action.configs)); case ADMIN_REPORTS_FETCH_SUCCESS: return importReports(state, action.reports); diff --git a/app/soapbox/reducers/instance.js b/app/soapbox/reducers/instance.js index 85001e577..a29c7c0fd 100644 --- a/app/soapbox/reducers/instance.js +++ b/app/soapbox/reducers/instance.js @@ -51,15 +51,23 @@ const getConfigValue = (instanceConfig, key) => { const importConfigs = (state, configs) => { // FIXME: This is pretty hacked together. Need to make a cleaner map. const config = ConfigDB.find(configs, ':pleroma', ':instance'); - if (!config) return state; - const value = config.get('value', ImmutableList()); + const simplePolicy = ConfigDB.toSimplePolicy(configs); + + if (!config && !simplePolicy) return state; return state.withMutations(state => { - const registrationsOpen = getConfigValue(value, ':registrations_open'); - const approvalRequired = getConfigValue(value, ':account_approval_required'); + if (config) { + const value = config.get('value', ImmutableList()); + const registrationsOpen = getConfigValue(value, ':registrations_open'); + const approvalRequired = getConfigValue(value, ':account_approval_required'); - state.update('registrations', c => typeof registrationsOpen === 'boolean' ? registrationsOpen : c); - state.update('approval_required', c => typeof approvalRequired === 'boolean' ? approvalRequired : c); + state.update('registrations', c => typeof registrationsOpen === 'boolean' ? registrationsOpen : c); + state.update('approval_required', c => typeof approvalRequired === 'boolean' ? approvalRequired : c); + } + + if (simplePolicy) { + state.setIn(['pleroma', 'metadata', 'federation', 'mrf_simple'], simplePolicy); + } }); }; diff --git a/app/soapbox/selectors/index.js b/app/soapbox/selectors/index.js index 5ba6f6088..2591eadd6 100644 --- a/app/soapbox/selectors/index.js +++ b/app/soapbox/selectors/index.js @@ -1,6 +1,11 @@ import { createSelector } from 'reselect'; -import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import { + Map as ImmutableMap, + List as ImmutableList, + OrderedSet as ImmutableOrderedSet, +} from 'immutable'; import { getDomain } from 'soapbox/utils/accounts'; +import ConfigDB from 'soapbox/utils/config_db'; const getAccountBase = (state, id) => state.getIn(['accounts', id], null); const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null); @@ -217,22 +222,38 @@ export const makeGetOtherAccounts = () => { }); }; +const getSimplePolicy = createSelector([ + state => state.getIn(['admin', 'configs'], ImmutableMap()), + state => state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()), +], (configs, instancePolicy) => { + return instancePolicy.merge(ConfigDB.toSimplePolicy(configs)); +}); + const getRemoteInstanceFavicon = (state, host) => ( state.get('accounts') .find(account => getDomain(account) === host, null, ImmutableMap()) .getIn(['pleroma', 'favicon']) ); -const getSimplePolicy = (state, host) => ( - state.getIn(['instance', 'pleroma', 'metadata', 'federation', 'mrf_simple'], ImmutableMap()) +const getRemoteInstanceFederation = (state, host) => ( + getSimplePolicy(state) .map(hosts => hosts.includes(host)) ); +export const makeGetHosts = () => { + return createSelector([getSimplePolicy], (simplePolicy) => { + return simplePolicy + .deleteAll(['accept', 'reject_deletes', 'report_removal']) + .reduce((acc, hosts) => acc.union(hosts), ImmutableOrderedSet()) + .sort(); + }); +}; + export const makeGetRemoteInstance = () => { return createSelector([ (state, host) => host, getRemoteInstanceFavicon, - getSimplePolicy, + getRemoteInstanceFederation, ], (host, favicon, federation) => { return ImmutableMap({ host, diff --git a/app/soapbox/utils/config_db.js b/app/soapbox/utils/config_db.js index 8a1e78a50..4d03aaa17 100644 --- a/app/soapbox/utils/config_db.js +++ b/app/soapbox/utils/config_db.js @@ -1,9 +1,50 @@ +import { + Map as ImmutableMap, + List as ImmutableList, + Set as ImmutableSet, + fromJS, +} from 'immutable'; +import { trimStart } from 'lodash'; + +const find = (configs, group, key) => { + return configs.find(config => + config.isSuperset({ group, key }), + ); +}; + +const toSimplePolicy = configs => { + const config = find(configs, ':pleroma', ':mrf_simple'); + + const reducer = (acc, curr) => { + const { tuple: [key, hosts] } = curr.toJS(); + return acc.set(trimStart(key, ':'), ImmutableSet(hosts)); + }; + + if (config && config.get) { + const value = config.get('value', ImmutableList()); + return value.reduce(reducer, ImmutableMap()); + } else { + return ImmutableMap(); + } +}; + +const fromSimplePolicy = simplePolicy => { + const mapper = (hosts, key) => fromJS({ tuple: [`:${key}`, hosts.toJS()] }); + const value = simplePolicy.map(mapper).toList(); + + return ImmutableList([ + ImmutableMap({ + group: ':pleroma', + key: ':mrf_simple', + value, + }), + ]); +}; + export const ConfigDB = { - find: (configs, group, key) => { - return configs.find(config => - config.isSuperset({ group, key }), - ); - }, + find, + toSimplePolicy, + fromSimplePolicy, }; export default ConfigDB;