diff --git a/app/soapbox/features/ui/components/instance_info_panel.js b/app/soapbox/features/ui/components/instance_info_panel.js
deleted file mode 100644
index 20582ddcf..000000000
--- a/app/soapbox/features/ui/components/instance_info_panel.js
+++ /dev/null
@@ -1,84 +0,0 @@
-'use strict';
-
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { injectIntl, defineMessages } from 'react-intl';
-import { connect } from 'react-redux';
-
-import { pinHost, unpinHost } from 'soapbox/actions/remote_timeline';
-import { getSettings } from 'soapbox/actions/settings';
-import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
-import { makeGetRemoteInstance } from 'soapbox/selectors';
-
-const getRemoteInstance = makeGetRemoteInstance();
-
-const messages = defineMessages({
- pinHost: { id: 'remote_instance.pin_host', defaultMessage: 'Pin {host}' },
- unpinHost: { id: 'remote_instance.unpin_host', defaultMessage: 'Unpin {host}' },
-});
-
-const mapStateToProps = (state, { host }) => {
- const settings = getSettings(state);
-
- return {
- instance: state.get('instance'),
- remoteInstance: getRemoteInstance(state, host),
- pinned: settings.getIn(['remote_timeline', 'pinnedHosts']).includes(host),
- };
-};
-
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
-@injectIntl
-class InstanceInfoPanel extends ImmutablePureComponent {
-
- static propTypes = {
- intl: PropTypes.object.isRequired,
- host: PropTypes.string.isRequired,
- instance: ImmutablePropTypes.map,
- remoteInstance: ImmutablePropTypes.map,
- pinned: PropTypes.bool,
- };
-
- handlePinHost = e => {
- const { dispatch, host, pinned } = this.props;
-
- if (!pinned) {
- dispatch(pinHost(host));
- } else {
- dispatch(unpinHost(host));
- }
- }
-
- makeMenu = () => {
- const { intl, host, pinned } = this.props;
-
- return [{
- text: intl.formatMessage(pinned ? messages.unpinHost : messages.pinHost, { host }),
- action: this.handlePinHost,
- icon: require(pinned ? '@tabler/icons/icons/pinned-off.svg' : '@tabler/icons/icons/pin.svg'),
- }];
- }
-
- render() {
- const { remoteInstance, pinned } = this.props;
- const menu = this.makeMenu();
- const icon = pinned ? 'thumbtack' : 'globe-w';
-
- return (
-
-
-
-
- {remoteInstance.get('host')}
-
-
-
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/ui/components/instance_info_panel.tsx b/app/soapbox/features/ui/components/instance_info_panel.tsx
new file mode 100644
index 000000000..a100abc2c
--- /dev/null
+++ b/app/soapbox/features/ui/components/instance_info_panel.tsx
@@ -0,0 +1,68 @@
+'use strict';
+
+import React from 'react';
+import { useIntl, defineMessages } from 'react-intl';
+
+import { pinHost, unpinHost } from 'soapbox/actions/remote_timeline';
+import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
+import { useAppSelector, useAppDispatch, useSettings } from 'soapbox/hooks';
+import { makeGetRemoteInstance } from 'soapbox/selectors';
+
+const getRemoteInstance = makeGetRemoteInstance();
+
+const messages = defineMessages({
+ pinHost: { id: 'remote_instance.pin_host', defaultMessage: 'Pin {host}' },
+ unpinHost: { id: 'remote_instance.unpin_host', defaultMessage: 'Unpin {host}' },
+});
+
+interface IInstanceInfoPanel {
+ /** Hostname (domain) of the remote instance, eg "gleasonator.com" */
+ host: string,
+}
+
+/** Widget that displays information about a remote instance to users. */
+const InstanceInfoPanel: React.FC = ({ host }) => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const settings = useSettings();
+ const remoteInstance: any = useAppSelector(state => getRemoteInstance(state, host));
+ const pinned: boolean = (settings.getIn(['remote_timeline', 'pinnedHosts']) as any).includes(host);
+
+ const handlePinHost: React.MouseEventHandler = () => {
+ if (!pinned) {
+ dispatch(pinHost(host));
+ } else {
+ dispatch(unpinHost(host));
+ }
+ };
+
+ const makeMenu = () => {
+ return [{
+ text: intl.formatMessage(pinned ? messages.unpinHost : messages.pinHost, { host }),
+ action: handlePinHost,
+ icon: require(pinned ? '@tabler/icons/icons/pinned-off.svg' : '@tabler/icons/icons/pin.svg'),
+ }];
+ };
+
+ const menu = makeMenu();
+ const icon = pinned ? 'thumbtack' : 'globe-w';
+
+ if (!remoteInstance) return null;
+
+ return (
+
+
+
+
+ {remoteInstance.get('host')}
+
+
+
+
+
+
+ );
+};
+
+export default InstanceInfoPanel;
diff --git a/app/soapbox/pages/remote_instance_page.tsx b/app/soapbox/pages/remote_instance_page.tsx
index 548e0f9de..e3488908c 100644
--- a/app/soapbox/pages/remote_instance_page.tsx
+++ b/app/soapbox/pages/remote_instance_page.tsx
@@ -13,13 +13,15 @@ import { federationRestrictionsDisclosed } from 'soapbox/utils/state';
import { Layout } from '../components/ui';
interface IRemoteInstancePage {
- params: {
- instance: string,
+ params?: {
+ instance?: string,
},
}
/** Page for viewing a remote instance timeline. */
-const RemoteInstancePage: React.FC = ({ children, params: { instance: host } }) => {
+const RemoteInstancePage: React.FC = ({ children, params }) => {
+ const host = params?.instance;
+
const account = useOwnAccount();
const disclosed = useAppSelector(federationRestrictionsDisclosed);