diff --git a/app/soapbox/features/ui/components/instance_info_panel.js b/app/soapbox/features/ui/components/instance_info_panel.js
new file mode 100644
index 000000000..f27105ee9
--- /dev/null
+++ b/app/soapbox/features/ui/components/instance_info_panel.js
@@ -0,0 +1,191 @@
+'use strict';
+
+import React from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { FormattedMessage } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import Icon from 'soapbox/components/icon';
+import { makeGetRemoteInstance } from 'soapbox/selectors';
+
+const hasRestrictions = remoteInstance => {
+ return remoteInstance
+ .get('federation')
+ .deleteAll(['accept', 'reject_deletes', 'report_removal'])
+ .reduce((acc, value) => acc || value, false);
+};
+
+const getRemoteInstance = makeGetRemoteInstance();
+
+const mapStateToProps = (state, { host }) => {
+ return {
+ instance: state.get('instance'),
+ remoteInstance: getRemoteInstance(state, host),
+ };
+};
+
+export default @connect(mapStateToProps, null, null, { forwardRef: true })
+class InstanceInfoPanel extends ImmutablePureComponent {
+
+ static propTypes = {
+ intl: PropTypes.object.isRequired,
+ host: PropTypes.string.isRequired,
+ instance: ImmutablePropTypes.map,
+ remoteInstance: ImmutablePropTypes.map,
+ };
+
+ renderRestrictions = () => {
+ const { remoteInstance } = this.props;
+ const items = [];
+
+ const {
+ avatar_removal,
+ banner_removal,
+ federated_timeline_removal,
+ followers_only,
+ media_nsfw,
+ media_removal,
+ } = remoteInstance.get('federation').toJS();
+
+ const fullMediaRemoval = media_removal && avatar_removal && banner_removal;
+ const partialMediaRemoval = media_removal || avatar_removal || banner_removal;
+
+ if (followers_only) {
+ items.push((
+
+ ));
+ } else if (federated_timeline_removal) {
+ items.push((
+
+ ));
+ }
+
+ if (fullMediaRemoval) {
+ items.push((
+
+ ));
+ } else if (partialMediaRemoval) {
+ items.push((
+
+ ));
+ }
+
+ if (!fullMediaRemoval && media_nsfw) {
+ items.push((
+
+ ));
+ }
+
+ return items;
+ }
+
+ renderContent = () => {
+ const { host, instance, remoteInstance } = this.props;
+ if (!instance || !remoteInstance) return null;
+
+ if (remoteInstance.getIn(['federation', 'reject']) === true) {
+ return (
+
+
+
+
+ );
+ } else if (hasRestrictions(remoteInstance)) {
+ return [
+ (
+
+
+
+ ),
+ this.renderRestrictions(),
+ ];
+ } else {
+ return (
+
+
+
+
+ );
+ }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ {this.renderContent()}
+
+
+ );
+ }
+
+}
diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js
index fd522c07f..2837cd902 100644
--- a/app/soapbox/features/ui/index.js
+++ b/app/soapbox/features/ui/index.js
@@ -34,6 +34,7 @@ import HomePage from 'soapbox/pages/home_page';
import DefaultPage from 'soapbox/pages/default_page';
import EmptyPage from 'soapbox/pages/default_page';
import AdminPage from 'soapbox/pages/admin_page';
+import RemoteInstancePage from 'soapbox/pages/remote_instance_page';
import SidebarMenu from '../../components/sidebar_menu';
import { connectUserStream } from '../../actions/streaming';
import { Redirect } from 'react-router-dom';
@@ -194,7 +195,7 @@ class SwitchingColumnsArea extends React.PureComponent {
-
+
{/*
diff --git a/app/soapbox/pages/remote_instance_page.js b/app/soapbox/pages/remote_instance_page.js
new file mode 100644
index 000000000..6c34940e0
--- /dev/null
+++ b/app/soapbox/pages/remote_instance_page.js
@@ -0,0 +1,59 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import WhoToFollowPanel from 'soapbox/features/ui/components/who_to_follow_panel';
+import TrendsPanel from 'soapbox/features/ui/components/trends_panel';
+import PromoPanel from 'soapbox/features/ui/components/promo_panel';
+import FeaturesPanel from 'soapbox/features/ui/components/features_panel';
+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';
+
+const mapStateToProps = state => {
+ const features = getFeatures(state.get('instance'));
+
+ return {
+ showTrendsPanel: features.trends,
+ showWhoToFollowPanel: features.suggestions,
+ };
+};
+
+export default @connect(mapStateToProps)
+class RemoteInstancePage extends ImmutablePureComponent {
+
+ render() {
+ const { children, showTrendsPanel, showWhoToFollowPanel, params: { instance: host } } = this.props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {showTrendsPanel &&
}
+ {showWhoToFollowPanel &&
}
+
+
+
+
+
+
+
+
+ );
+ }
+
+}
diff --git a/app/soapbox/selectors/index.js b/app/soapbox/selectors/index.js
index 9b222921e..b1bd570f7 100644
--- a/app/soapbox/selectors/index.js
+++ b/app/soapbox/selectors/index.js
@@ -1,5 +1,6 @@
import { createSelector } from 'reselect';
-import { List as ImmutableList } from 'immutable';
+import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import { getDomain } from 'soapbox/utils/accounts';
const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
@@ -215,3 +216,26 @@ export const makeGetOtherAccounts = () => {
}, ImmutableList());
});
};
+
+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())
+ .map(hosts => hosts.includes(host))
+);
+
+export const makeGetRemoteInstance = () => {
+ return createSelector([
+ getRemoteInstanceFavicon,
+ getSimplePolicy,
+ ], (favicon, federation) => {
+ return ImmutableMap({
+ favicon,
+ federation,
+ });
+ });
+};
diff --git a/app/styles/application.scss b/app/styles/application.scss
index 611eef9c4..f6611fdf0 100644
--- a/app/styles/application.scss
+++ b/app/styles/application.scss
@@ -82,6 +82,7 @@
@import 'components/backups';
@import 'components/crypto-donate';
@import 'components/datepicker';
+@import 'components/remote-timeline';
// Holiday
@import 'holiday/halloween';
diff --git a/app/styles/components/remote-timeline.scss b/app/styles/components/remote-timeline.scss
new file mode 100644
index 000000000..3fb00e731
--- /dev/null
+++ b/app/styles/components/remote-timeline.scss
@@ -0,0 +1,27 @@
+.instance-federation-panel {
+ &__message {
+ margin-bottom: 15px;
+
+ i.fa {
+ padding-right: 10px;
+ }
+ }
+
+ .wtf-panel__content {
+ box-sizing: border-box;
+ padding: 15px;
+ }
+}
+
+.federation-restriction {
+ display: flex;
+ padding: 15px 0;
+
+ &__icon {
+ width: 16px;
+ display: flex;
+ align-items: flex-start;
+ justify-content: center;
+ padding-right: 10px;
+ }
+}