diff --git a/app/soapbox/features/aliases/components/search.js b/app/soapbox/features/aliases/components/search.js
deleted file mode 100644
index 4311685e7..000000000
--- a/app/soapbox/features/aliases/components/search.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import classNames from 'classnames';
-import PropTypes from 'prop-types';
-import React from 'react';
-import { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-
-import Icon from 'soapbox/components/icon';
-import { Button } from 'soapbox/components/ui';
-
-import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from '../../../actions/aliases';
-
-const messages = defineMessages({
- search: { id: 'aliases.search', defaultMessage: 'Search your old account' },
- searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
-});
-
-const mapStateToProps = state => ({
- value: state.getIn(['aliases', 'suggestions', 'value']),
-});
-
-const mapDispatchToProps = dispatch => ({
- onSubmit: value => dispatch(fetchAliasesSuggestions(value)),
- onClear: () => dispatch(clearAliasesSuggestions()),
- onChange: value => dispatch(changeAliasesSuggestions(value)),
-});
-
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-class Search extends React.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);
- }
- }
-
- handleSubmit = () => {
- this.props.onSubmit(this.props.value);
- }
-
- handleClear = () => {
- this.props.onClear();
- }
-
- render() {
- const { value, intl } = this.props;
- const hasValue = value.length > 0;
-
- return (
-
-
-
-
-
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/aliases/components/search.tsx b/app/soapbox/features/aliases/components/search.tsx
new file mode 100644
index 000000000..516a38884
--- /dev/null
+++ b/app/soapbox/features/aliases/components/search.tsx
@@ -0,0 +1,65 @@
+import classNames from 'classnames';
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+import { useDispatch } from 'react-redux';
+
+import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from 'soapbox/actions/aliases';
+import Icon from 'soapbox/components/icon';
+import { Button } from 'soapbox/components/ui';
+import { useAppSelector } from 'soapbox/hooks';
+
+const messages = defineMessages({
+ search: { id: 'aliases.search', defaultMessage: 'Search your old account' },
+ searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
+});
+
+const Search: React.FC = () => {
+ const dispatch = useDispatch();
+ const intl = useIntl();
+
+ const value = useAppSelector(state => state.aliases.getIn(['suggestions', 'value'])) as string;
+
+ const handleChange = (e: React.ChangeEvent) => {
+ dispatch(changeAliasesSuggestions(e.target.value));
+ };
+
+ const handleKeyUp = (e: React.KeyboardEvent) => {
+ if (e.keyCode === 13) {
+ dispatch(fetchAliasesSuggestions(value));
+ }
+ };
+
+ const handleSubmit = () => {
+ dispatch(fetchAliasesSuggestions(value));
+ };
+
+ const handleClear = () => {
+ dispatch(clearAliasesSuggestions());
+ };
+
+ const hasValue = value.length > 0;
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Search;
diff --git a/app/soapbox/features/follow_recommendations/components/account.js b/app/soapbox/features/follow_recommendations/components/account.js
deleted file mode 100644
index c15fc6bcc..000000000
--- a/app/soapbox/features/follow_recommendations/components/account.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { connect } from 'react-redux';
-
-import Avatar from 'soapbox/components/avatar';
-import DisplayName from 'soapbox/components/display_name';
-import Permalink from 'soapbox/components/permalink';
-import ActionButton from 'soapbox/features/ui/components/action_button';
-import { makeGetAccount } from 'soapbox/selectors';
-
-const makeMapStateToProps = () => {
- const getAccount = makeGetAccount();
-
- const mapStateToProps = (state, props) => ({
- account: getAccount(state, props.id),
- });
-
- return mapStateToProps;
-};
-
-const getFirstSentence = str => {
- const arr = str.split(/(([.?!]+\s)|[.。?!\n•])/);
-
- return arr[0];
-};
-
-export default @connect(makeMapStateToProps)
-class Account extends ImmutablePureComponent {
-
- static propTypes = {
- account: ImmutablePropTypes.record.isRequired,
- intl: PropTypes.object.isRequired,
- dispatch: PropTypes.func.isRequired,
- };
-
- render() {
- const { account } = this.props;
-
- return (
-
-
-
-
-
-
-
- {getFirstSentence(account.get('note_plain'))}
-
-
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/follow_recommendations/components/account.tsx b/app/soapbox/features/follow_recommendations/components/account.tsx
new file mode 100644
index 000000000..8ba56b497
--- /dev/null
+++ b/app/soapbox/features/follow_recommendations/components/account.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+
+import Avatar from 'soapbox/components/avatar';
+import DisplayName from 'soapbox/components/display_name';
+import Permalink from 'soapbox/components/permalink';
+import ActionButton from 'soapbox/features/ui/components/action_button';
+import { useAppSelector } from 'soapbox/hooks';
+import { makeGetAccount } from 'soapbox/selectors';
+
+const getAccount = makeGetAccount();
+
+const getFirstSentence = (str: string) => {
+ const arr = str.split(/(([.?!]+\s)|[.。?!\n•])/);
+
+ return arr[0];
+};
+
+interface IAccount {
+ id: string,
+}
+
+const Account: React.FC = ({ id }) => {
+ const account = useAppSelector((state) => getAccount(state, id));
+
+ if (!account) return null;
+
+ return (
+
+
+
+
+
+
+
+ {getFirstSentence(account.get('note_plain'))}
+
+
+
+
+
+ );
+};
+
+export default Account;
diff --git a/app/soapbox/features/follow_recommendations/components/follow_recommendations_container.js b/app/soapbox/features/follow_recommendations/components/follow_recommendations_container.js
deleted file mode 100644
index 59c4f382b..000000000
--- a/app/soapbox/features/follow_recommendations/components/follow_recommendations_container.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import { FormattedMessage } from 'react-intl';
-
-import { Button } from 'soapbox/components/ui';
-
-import FollowRecommendationsList from './follow_recommendations_list';
-
-export default class FollowRecommendationsContainer extends React.Component {
-
- static propTypes = {
- onDone: PropTypes.func.isRequired,
- }
-
- handleDone = () => {
- this.props.onDone();
- }
-
- render() {
- return (
-
- );
- }
-
-}
diff --git a/app/soapbox/features/follow_recommendations/components/follow_recommendations_container.tsx b/app/soapbox/features/follow_recommendations/components/follow_recommendations_container.tsx
new file mode 100644
index 000000000..c3f198f94
--- /dev/null
+++ b/app/soapbox/features/follow_recommendations/components/follow_recommendations_container.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { FormattedMessage } from 'react-intl';
+
+import { Button } from 'soapbox/components/ui';
+
+import FollowRecommendationsList from './follow_recommendations_list';
+
+interface IFollowRecommendationsContainer {
+ onDone: () => void,
+}
+
+const FollowRecommendationsContainer: React.FC = ({ onDone }) => (
+
+);
+
+export default FollowRecommendationsContainer;
diff --git a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.js b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.js
deleted file mode 100644
index 7fc06319b..000000000
--- a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-
-import { fetchSuggestions } from 'soapbox/actions/suggestions';
-import { Spinner } from 'soapbox/components/ui';
-
-import Account from './account';
-
-const mapStateToProps = state => ({
- suggestions: state.getIn(['suggestions', 'items']),
- isLoading: state.getIn(['suggestions', 'isLoading']),
-});
-
-export default @connect(mapStateToProps)
-class FollowRecommendationsList extends ImmutablePureComponent {
-
- static propTypes = {
- dispatch: PropTypes.func.isRequired,
- suggestions: ImmutablePropTypes.list,
- isLoading: PropTypes.bool,
- };
-
- componentDidMount() {
- const { dispatch, suggestions } = this.props;
-
- // Don't re-fetch if we're e.g. navigating backwards to this page,
- // since we don't want followed accounts to disappear from the list
- if (suggestions.size === 0) {
- dispatch(fetchSuggestions(true));
- }
- }
-
- render() {
- const { suggestions, isLoading } = this.props;
-
- if (isLoading) {
- return (
-
-
-
- );
- }
-
- return (
-
- {suggestions.size > 0 ? suggestions.map(suggestion => (
-
- )) : (
-
-
-
- )}
-
- );
-
- }
-
-}
diff --git a/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx
new file mode 100644
index 000000000..4ca838072
--- /dev/null
+++ b/app/soapbox/features/follow_recommendations/components/follow_recommendations_list.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { useEffect } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { useDispatch } from 'react-redux';
+
+import { fetchSuggestions } from 'soapbox/actions/suggestions';
+import { Spinner } from 'soapbox/components/ui';
+import { useAppSelector } from 'soapbox/hooks';
+
+import Account from './account';
+
+const FollowRecommendationsList: React.FC = () => {
+ const dispatch = useDispatch();
+
+ const suggestions = useAppSelector((state) => state.suggestions.get('items'));
+ const isLoading = useAppSelector((state) => state.suggestions.get('isLoading'));
+
+ useEffect(() => {
+ if (suggestions.size === 0) {
+ dispatch(fetchSuggestions());
+ }
+ }, []);
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ {suggestions.size > 0 ? suggestions.map((suggestion: { account: string }) => (
+
+ )) : (
+
+
+
+ )}
+
+ );
+};
+
+export default FollowRecommendationsList;
diff --git a/app/soapbox/features/follow_recommendations/index.js b/app/soapbox/features/follow_recommendations/index.js
deleted file mode 100644
index 2e9e3bcc3..000000000
--- a/app/soapbox/features/follow_recommendations/index.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import { withRouter } from 'react-router-dom';
-
-import Column from 'soapbox/features/ui/components/column';
-
-import FollowRecommendationsContainer from './components/follow_recommendations_container';
-
-export default @withRouter
-class FollowRecommendations extends React.Component {
-
- static propTypes = {
- history: PropTypes.object.isRequired,
- };
-
- onDone = () => {
- this.props.history.push('/');
- }
-
- render() {
- return (
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/follow_recommendations/index.tsx b/app/soapbox/features/follow_recommendations/index.tsx
new file mode 100644
index 000000000..444504532
--- /dev/null
+++ b/app/soapbox/features/follow_recommendations/index.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { useHistory } from 'react-router-dom';
+
+import Column from 'soapbox/features/ui/components/column';
+
+import FollowRecommendationsContainer from './components/follow_recommendations_container';
+
+const FollowRecommendations: React.FC = () => {
+ const history = useHistory();
+
+ const onDone = () => {
+ history.push('/');
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default FollowRecommendations;
diff --git a/app/soapbox/features/intentional_error/index.js b/app/soapbox/features/intentional_error/index.tsx
similarity index 50%
rename from app/soapbox/features/intentional_error/index.js
rename to app/soapbox/features/intentional_error/index.tsx
index b3d7fa0f7..01ae3a5c4 100644
--- a/app/soapbox/features/intentional_error/index.js
+++ b/app/soapbox/features/intentional_error/index.tsx
@@ -4,10 +4,8 @@ import React from 'react';
* IntentionalError:
* For testing logging/monitoring & previewing ErrorBoundary design.
*/
-export default class IntentionalError extends React.Component {
+const IntentionalError: React.FC = () => {
+ throw 'This error is intentional.';
+};
- render() {
- throw 'This error is intentional.';
- }
-
-}
+export default IntentionalError;