diff --git a/app/soapbox/features/ui/components/link_footer.js b/app/soapbox/features/ui/components/link_footer.js
deleted file mode 100644
index c6dc0d477..000000000
--- a/app/soapbox/features/ui/components/link_footer.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-import { Link } from 'react-router-dom';
-
-import { logOut } from 'soapbox/actions/auth';
-import { getSoapboxConfig } from 'soapbox/actions/soapbox';
-import { Text } from 'soapbox/components/ui';
-import emojify from 'soapbox/features/emoji/emoji';
-import { getBaseURL } from 'soapbox/utils/accounts';
-import sourceCode from 'soapbox/utils/code';
-import { getFeatures } from 'soapbox/utils/features';
-
-import { openModal } from '../../../actions/modals';
-
-const mapStateToProps = state => {
- const me = state.get('me');
- const account = state.getIn(['accounts', me]);
- const instance = state.get('instance');
- const features = getFeatures(instance);
- const soapboxConfig = getSoapboxConfig(state);
-
- return {
- account,
- soapboxConfig,
- profileDirectory: features.profileDirectory,
- federating: features.federating,
- showAliases: features.accountAliasesAPI,
- importAPI: features.importAPI,
- baseURL: getBaseURL(account),
- };
-};
-
-const mapDispatchToProps = (dispatch, { intl }) => ({
- onOpenHotkeys(e) {
- dispatch(openModal('HOTKEYS'));
- e.preventDefault();
- },
- onClickLogOut(e) {
- dispatch(logOut(intl));
- e.preventDefault();
- },
-});
-
-const LinkFooter = ({ onOpenHotkeys, account, profileDirectory, federating, showAliases, importAPI, onClickLogOut, baseURL, soapboxConfig }) => (
-
-
- {account && <>
- {profileDirectory && }
-
-
- {/* */}
- {federating && }
- {/* */}
- {/* isAdmin(account) && */}
- {/* isAdmin(account) && */}
- {/* */}
- {/* - {importAPI ? (
-
- ) : (
-
- )}
*/}
- {(federating && showAliases) && }
- {/* */}
- >}
- {/* */}
- {account && }
-
-
-
- {soapboxConfig.get('linkFooterMessage') ? (
-
- ) : (
- {sourceCode.repository},
- code_version: sourceCode.version,
- }}
- />
- )}
-
-
-);
-
-LinkFooter.propTypes = {
- account: ImmutablePropTypes.record,
- soapboxConfig: ImmutablePropTypes.map,
- profileDirectory: PropTypes.bool,
- federating: PropTypes.bool,
- showAliases: PropTypes.bool,
- importAPI: PropTypes.bool,
- onOpenHotkeys: PropTypes.func.isRequired,
- onClickLogOut: PropTypes.func.isRequired,
- baseURL: PropTypes.string,
-};
-
-export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LinkFooter));
diff --git a/app/soapbox/features/ui/components/link_footer.tsx b/app/soapbox/features/ui/components/link_footer.tsx
new file mode 100644
index 000000000..84a7c5fc5
--- /dev/null
+++ b/app/soapbox/features/ui/components/link_footer.tsx
@@ -0,0 +1,88 @@
+import classNames from 'classnames';
+import React from 'react';
+import { FormattedMessage, useIntl } from 'react-intl';
+import { useDispatch } from 'react-redux';
+import { Link } from 'react-router-dom';
+
+import { logOut } from 'soapbox/actions/auth';
+import { Text } from 'soapbox/components/ui';
+import emojify from 'soapbox/features/emoji/emoji';
+import { useSoapboxConfig, useOwnAccount, useFeatures } from 'soapbox/hooks';
+import sourceCode from 'soapbox/utils/code';
+
+interface IFooterLink {
+ to: string,
+ className?: string,
+ onClick?: React.EventHandler,
+}
+
+const FooterLink: React.FC = ({ children, className, ...rest }): JSX.Element => {
+ return (
+ {children}
+ );
+};
+
+const LinkFooter: React.FC = (): JSX.Element => {
+ const account = useOwnAccount();
+ const features = useFeatures();
+ const soapboxConfig = useSoapboxConfig();
+
+ const intl = useIntl();
+ const dispatch = useDispatch();
+
+ const onClickLogOut: React.EventHandler = (e) => {
+ dispatch(logOut(intl));
+ e.preventDefault();
+ };
+
+ return (
+
+
+ {account && <>
+ {features.profileDirectory && (
+
+ )}
+
+
+ {features.filters && (
+
+ )}
+ {features.federating && (
+
+ )}
+ {account.locked && (
+
+ )}
+ {features.importAPI && (
+
+ )}
+ {(features.federating && features.accountMoving) && (
+
+ )}
+
+ >}
+
+
+
+ {soapboxConfig.linkFooterMessage ? (
+
+ ) : (
+ {sourceCode.repository},
+ code_version: sourceCode.version,
+ }}
+ />
+ )}
+
+
+ );
+};
+
+export default LinkFooter;
diff --git a/app/soapbox/hooks/index.ts b/app/soapbox/hooks/index.ts
index 1bce19299..d3c5f4700 100644
--- a/app/soapbox/hooks/index.ts
+++ b/app/soapbox/hooks/index.ts
@@ -1,4 +1,5 @@
export { useAppSelector } from './useAppSelector';
+export { useFeatures } from './useFeatures';
export { useOnScreen } from './useOnScreen';
export { useOwnAccount } from './useOwnAccount';
export { useSettings } from './useSettings';
diff --git a/app/soapbox/hooks/useFeatures.ts b/app/soapbox/hooks/useFeatures.ts
new file mode 100644
index 000000000..1fefa6bce
--- /dev/null
+++ b/app/soapbox/hooks/useFeatures.ts
@@ -0,0 +1,9 @@
+import { useAppSelector } from 'soapbox/hooks';
+import { getFeatures } from 'soapbox/utils/features';
+
+import type { Features } from 'soapbox/utils/features';
+
+/** Get features for the current instance */
+export const useFeatures = (): Features => {
+ return useAppSelector((state) => getFeatures(state.instance));
+};
diff --git a/app/soapbox/normalizers/soapbox/soapbox_config.ts b/app/soapbox/normalizers/soapbox/soapbox_config.ts
index d122e7bf4..a56c5e0ce 100644
--- a/app/soapbox/normalizers/soapbox/soapbox_config.ts
+++ b/app/soapbox/normalizers/soapbox/soapbox_config.ts
@@ -112,6 +112,7 @@ export const SoapboxConfigRecord = ImmutableRecord({
authenticatedProfile: true,
singleUserMode: false,
singleUserModeProfile: '',
+ linkFooterMessage: '',
}, 'SoapboxConfig');
type SoapboxConfigMap = ImmutableMap;
diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts
index c9b050f1c..b1f92b99d 100644
--- a/app/soapbox/utils/features.ts
+++ b/app/soapbox/utils/features.ts
@@ -133,7 +133,7 @@ const getInstanceFeatures = (instance: Instance) => {
};
};
-type Features = ReturnType;
+export type Features = ReturnType;
export const getFeatures = createSelector([
(instance: Instance) => instance,