diff --git a/app/soapbox/features/list_adder/components/account.js b/app/soapbox/features/list_adder/components/account.js
deleted file mode 100644
index 184339572..000000000
--- a/app/soapbox/features/list_adder/components/account.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-
-import Avatar from '../../../components/avatar';
-import DisplayName from '../../../components/display-name';
-import { makeGetAccount } from '../../../selectors';
-
-const makeMapStateToProps = () => {
- const getAccount = makeGetAccount();
-
- const mapStateToProps = (state, { accountId }) => ({
- account: getAccount(state, accountId),
- });
-
- return mapStateToProps;
-};
-
-
-export default @connect(makeMapStateToProps)
-@injectIntl
-class Account extends ImmutablePureComponent {
-
- static propTypes = {
- account: ImmutablePropTypes.record.isRequired,
- };
-
- render() {
- const { account } = this.props;
- return (
-
- );
- }
-
-}
diff --git a/app/soapbox/features/list_adder/components/account.tsx b/app/soapbox/features/list_adder/components/account.tsx
new file mode 100644
index 000000000..304b32207
--- /dev/null
+++ b/app/soapbox/features/list_adder/components/account.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+
+import DisplayName from 'soapbox/components/display-name';
+import { Avatar } from 'soapbox/components/ui';
+import { useAppSelector } from 'soapbox/hooks';
+import { makeGetAccount } from 'soapbox/selectors';
+
+const getAccount = makeGetAccount();
+
+interface IAccount {
+ accountId: string,
+}
+
+const Account: React.FC = ({ accountId }) => {
+ const account = useAppSelector((state) => getAccount(state, accountId));
+
+ if (!account) return null;
+
+ return (
+
+ );
+};
+
+export default Account;
diff --git a/app/soapbox/features/list_adder/components/list.js b/app/soapbox/features/list_adder/components/list.js
deleted file mode 100644
index fd9930180..000000000
--- a/app/soapbox/features/list_adder/components/list.js
+++ /dev/null
@@ -1,70 +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 { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-
-import { removeFromListAdder, addToListAdder } from 'soapbox/actions/lists';
-import Icon from 'soapbox/components/icon';
-import IconButton from 'soapbox/components/icon_button';
-
-const messages = defineMessages({
- remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
- add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
-});
-
-const MapStateToProps = (state, { listId, added }) => ({
- list: state.get('lists').get(listId),
- added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added,
-});
-
-const mapDispatchToProps = (dispatch, { listId }) => ({
- onRemove: () => dispatch(removeFromListAdder(listId)),
- onAdd: () => dispatch(addToListAdder(listId)),
-});
-
-export default @connect(MapStateToProps, mapDispatchToProps)
-@injectIntl
-class List extends ImmutablePureComponent {
-
- static propTypes = {
- list: ImmutablePropTypes.map.isRequired,
- intl: PropTypes.object.isRequired,
- onRemove: PropTypes.func.isRequired,
- onAdd: PropTypes.func.isRequired,
- added: PropTypes.bool,
- };
-
- static defaultProps = {
- added: false,
- };
-
- render() {
- const { list, intl, onRemove, onAdd, added } = this.props;
-
- let button;
-
- if (added) {
- button = ;
- } else {
- button = ;
- }
-
- return (
-
-
-
-
- {list.get('title')}
-
-
-
- {button}
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/list_adder/components/list.tsx b/app/soapbox/features/list_adder/components/list.tsx
new file mode 100644
index 000000000..822833476
--- /dev/null
+++ b/app/soapbox/features/list_adder/components/list.tsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+
+import { removeFromListAdder, addToListAdder } from 'soapbox/actions/lists';
+import Icon from 'soapbox/components/icon';
+import IconButton from 'soapbox/components/icon_button';
+import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
+
+const messages = defineMessages({
+ remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
+ add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
+});
+
+interface IList {
+ listId: string,
+}
+
+const List: React.FC = ({ listId }) => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const list = useAppSelector((state) => state.lists.get(listId));
+ const added = useAppSelector((state) => state.listAdder.lists.items.includes(listId));
+
+ const onRemove = () => dispatch(removeFromListAdder(listId));
+ const onAdd = () => dispatch(addToListAdder(listId));
+
+ if (!list) return null;
+
+ let button;
+
+ if (added) {
+ button = ;
+ } else {
+ button = ;
+ }
+
+ return (
+
+
+
+ {list.title}
+
+ {button}
+
+ );
+};
+
+export default List;
diff --git a/app/soapbox/features/list_adder/index.js b/app/soapbox/features/list_adder/index.js
deleted file mode 100644
index 7b7a6fb55..000000000
--- a/app/soapbox/features/list_adder/index.js
+++ /dev/null
@@ -1,105 +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 { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-import { createSelector } from 'reselect';
-
-import { setupListAdder, resetListAdder } from 'soapbox/actions/lists';
-import { CardHeader, CardTitle, Modal } from 'soapbox/components/ui';
-
-import NewListForm from '../lists/components/new_list_form';
-
-import Account from './components/account';
-import List from './components/list';
-
-// hack
-const getOrderedLists = createSelector([state => state.get('lists')], lists => {
- if (!lists) {
- return lists;
- }
-
- return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')));
-});
-
-const mapStateToProps = (state, { accountId }) => ({
- listIds: getOrderedLists(state).map(list=>list.get('id')),
- account: state.getIn(['accounts', accountId]),
-});
-
-const mapDispatchToProps = dispatch => ({
- onInitialize: accountId => dispatch(setupListAdder(accountId)),
- onReset: () => dispatch(resetListAdder()),
-});
-
-const messages = defineMessages({
- close: { id: 'lightbox.close', defaultMessage: 'Close' },
- subheading: { id: 'lists.subheading', defaultMessage: 'Your lists' },
- add: { id: 'lists.new.create', defaultMessage: 'Add List' },
-});
-
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-class ListAdder extends ImmutablePureComponent {
-
- static propTypes = {
- accountId: PropTypes.string.isRequired,
- onClose: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired,
- onInitialize: PropTypes.func.isRequired,
- onReset: PropTypes.func.isRequired,
- listIds: ImmutablePropTypes.list.isRequired,
- account: ImmutablePropTypes.record,
- };
-
- componentDidMount() {
- const { onInitialize, accountId } = this.props;
- onInitialize(accountId);
- }
-
- componentWillUnmount() {
- const { onReset } = this.props;
- onReset();
- }
-
- onClickClose = () => {
- this.props.onClose('LIST_ADDER');
- };
-
- render() {
- const { accountId, listIds, intl } = this.props;
-
- return (
- }
- onClose={this.onClickClose}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {listIds.map(ListId =>
)}
-
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/list_adder/index.tsx b/app/soapbox/features/list_adder/index.tsx
new file mode 100644
index 000000000..10d706519
--- /dev/null
+++ b/app/soapbox/features/list_adder/index.tsx
@@ -0,0 +1,83 @@
+import React from 'react';
+import { useEffect } from 'react';
+import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
+import { createSelector } from 'reselect';
+
+import { setupListAdder, resetListAdder } from 'soapbox/actions/lists';
+import { CardHeader, CardTitle, Modal } from 'soapbox/components/ui';
+import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
+
+import NewListForm from '../lists/components/new_list_form';
+
+import Account from './components/account';
+import List from './components/list';
+
+import type { List as ImmutableList } from 'immutable';
+import type { RootState } from 'soapbox/store';
+import type { List as ListEntity } from 'soapbox/types/entities';
+
+const messages = defineMessages({
+ close: { id: 'lightbox.close', defaultMessage: 'Close' },
+ subheading: { id: 'lists.subheading', defaultMessage: 'Your lists' },
+ add: { id: 'lists.new.create', defaultMessage: 'Add List' },
+});
+
+// hack
+const getOrderedLists = createSelector([(state: RootState) => state.lists], lists => {
+ if (!lists) {
+ return lists;
+ }
+
+ return lists.toList().filter(item => !!item).sort((a, b) => (a as ListEntity).title.localeCompare((b as ListEntity).title)) as ImmutableList;
+});
+
+interface IListAdder {
+ accountId: string,
+ onClose: (type: string) => void,
+}
+
+const ListAdder: React.FC = ({ accountId, onClose }) => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const listIds = useAppSelector((state) => getOrderedLists(state).map(list => list.id));
+
+ useEffect(() => {
+ dispatch(setupListAdder(accountId));
+
+ return () => {
+ dispatch(resetListAdder());
+ };
+ }, []);
+
+ const onClickClose = () => {
+ onClose('LIST_ADDER');
+ };
+
+ return (
+ }
+ onClose={onClickClose}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {listIds.map(ListId =>
)}
+
+
+ );
+};
+
+export default ListAdder;
diff --git a/app/soapbox/features/list_editor/components/account.js b/app/soapbox/features/list_editor/components/account.js
deleted file mode 100644
index e73a465ca..000000000
--- a/app/soapbox/features/list_editor/components/account.js
+++ /dev/null
@@ -1,78 +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 { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-
-import { removeFromListEditor, addToListEditor } from '../../../actions/lists';
-import Avatar from '../../../components/avatar';
-import DisplayName from '../../../components/display-name';
-import IconButton from '../../../components/icon_button';
-import { makeGetAccount } from '../../../selectors';
-
-const messages = defineMessages({
- remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
- add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
-});
-
-const makeMapStateToProps = () => {
- const getAccount = makeGetAccount();
-
- const mapStateToProps = (state, { accountId, added }) => ({
- account: getAccount(state, accountId),
- added: typeof added === 'undefined' ? state.getIn(['listEditor', 'accounts', 'items']).includes(accountId) : added,
- });
-
- return mapStateToProps;
-};
-
-const mapDispatchToProps = (dispatch, { accountId }) => ({
- onRemove: () => dispatch(removeFromListEditor(accountId)),
- onAdd: () => dispatch(addToListEditor(accountId)),
-});
-
-export default @connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
-class Account extends ImmutablePureComponent {
-
- static propTypes = {
- account: ImmutablePropTypes.record.isRequired,
- intl: PropTypes.object.isRequired,
- onRemove: PropTypes.func.isRequired,
- onAdd: PropTypes.func.isRequired,
- added: PropTypes.bool,
- };
-
- static defaultProps = {
- added: false,
- };
-
- render() {
- const { account, intl, onRemove, onAdd, added } = this.props;
-
- let button;
-
- if (added) {
- button = ;
- } else {
- button = ;
- }
-
- return (
-
- );
- }
-
-}
diff --git a/app/soapbox/features/list_editor/components/account.tsx b/app/soapbox/features/list_editor/components/account.tsx
new file mode 100644
index 000000000..f3c52d9f3
--- /dev/null
+++ b/app/soapbox/features/list_editor/components/account.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+
+import { removeFromListEditor, addToListEditor } from 'soapbox/actions/lists';
+import DisplayName from 'soapbox/components/display-name';
+import IconButton from 'soapbox/components/icon_button';
+import { Avatar } from 'soapbox/components/ui';
+import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
+import { makeGetAccount } from 'soapbox/selectors';
+
+const messages = defineMessages({
+ remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
+ add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
+});
+
+const getAccount = makeGetAccount();
+
+interface IAccount {
+ accountId: string,
+}
+
+const Account: React.FC = ({ accountId }) => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const account = useAppSelector((state) => getAccount(state, accountId));
+ const isAdded = useAppSelector((state) => state.listEditor.accounts.items.includes(accountId));
+
+ const onRemove = () => dispatch(removeFromListEditor(accountId));
+ const onAdd = () => dispatch(addToListEditor(accountId));
+
+ if (!account) return null;
+
+ let button;
+
+ if (isAdded) {
+ button = ;
+ } else {
+ button = ;
+ }
+
+ return (
+
+ );
+};
+
+export default Account;
diff --git a/app/soapbox/features/list_editor/components/edit_list_form.js b/app/soapbox/features/list_editor/components/edit_list_form.js
deleted file mode 100644
index 0553172d1..000000000
--- a/app/soapbox/features/list_editor/components/edit_list_form.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-
-import { changeListEditorTitle, submitListEditor } from '../../../actions/lists';
-import { Button } from '../../../components/ui';
-
-const messages = defineMessages({
- title: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
- save: { id: 'lists.new.save_title', defaultMessage: 'Save Title' },
-});
-
-const mapStateToProps = state => ({
- value: state.getIn(['listEditor', 'title']),
- disabled: !state.getIn(['listEditor', 'isChanged']),
-});
-
-const mapDispatchToProps = dispatch => ({
- onChange: value => dispatch(changeListEditorTitle(value)),
- onSubmit: () => dispatch(submitListEditor(false)),
-});
-
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-class ListForm extends React.PureComponent {
-
- static propTypes = {
- value: PropTypes.string.isRequired,
- disabled: PropTypes.bool,
- intl: PropTypes.object.isRequired,
- onChange: PropTypes.func.isRequired,
- onSubmit: PropTypes.func.isRequired,
- };
-
- handleChange = e => {
- this.props.onChange(e.target.value);
- }
-
- handleSubmit = e => {
- e.preventDefault();
- this.props.onSubmit();
- }
-
- handleClick = () => {
- this.props.onSubmit();
- }
-
- render() {
- const { value, disabled, intl } = this.props;
- const save = intl.formatMessage(messages.save);
-
- return (
-
- );
- }
-
-}
diff --git a/app/soapbox/features/list_editor/components/edit_list_form.tsx b/app/soapbox/features/list_editor/components/edit_list_form.tsx
new file mode 100644
index 000000000..ba365e40b
--- /dev/null
+++ b/app/soapbox/features/list_editor/components/edit_list_form.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+
+import { changeListEditorTitle, submitListEditor } from 'soapbox/actions/lists';
+import { Button, Form, HStack, Input } from 'soapbox/components/ui';
+import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
+
+const messages = defineMessages({
+ title: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
+ save: { id: 'lists.new.save_title', defaultMessage: 'Save Title' },
+});
+
+const ListForm = () => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const value = useAppSelector((state) => state.listEditor.title);
+ const disabled = useAppSelector((state) => !state.listEditor.isChanged);
+
+ const handleChange: React.ChangeEventHandler = e => {
+ dispatch(changeListEditorTitle(e.target.value));
+ };
+
+ const handleSubmit: React.FormEventHandler = e => {
+ e.preventDefault();
+ dispatch(submitListEditor(false));
+ };
+
+ const handleClick = () => {
+ dispatch(submitListEditor(false));
+ };
+
+ const save = intl.formatMessage(messages.save);
+
+ return (
+
+ );
+};
+
+export default ListForm;
diff --git a/app/soapbox/features/list_editor/components/search.js b/app/soapbox/features/list_editor/components/search.js
deleted file mode 100644
index 3314fa4e3..000000000
--- a/app/soapbox/features/list_editor/components/search.js
+++ /dev/null
@@ -1,83 +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 { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from 'soapbox/actions/lists';
-import Icon from 'soapbox/components/icon';
-import { Button } from 'soapbox/components/ui';
-
-const messages = defineMessages({
- search: { id: 'lists.search', defaultMessage: 'Search among people you follow' },
- searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
-});
-
-const mapStateToProps = state => ({
- value: state.getIn(['listEditor', 'suggestions', 'value']),
-});
-
-const mapDispatchToProps = dispatch => ({
- onSubmit: value => dispatch(fetchListSuggestions(value)),
- onClear: () => dispatch(clearListSuggestions()),
- onChange: value => dispatch(changeListSuggestions(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/list_editor/components/search.tsx b/app/soapbox/features/list_editor/components/search.tsx
new file mode 100644
index 000000000..edd78250f
--- /dev/null
+++ b/app/soapbox/features/list_editor/components/search.tsx
@@ -0,0 +1,58 @@
+import classNames from 'classnames';
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+
+import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from 'soapbox/actions/lists';
+import Icon from 'soapbox/components/icon';
+import { Button, Form, HStack, Input } from 'soapbox/components/ui';
+import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
+
+const messages = defineMessages({
+ search: { id: 'lists.search', defaultMessage: 'Search among people you follow' },
+ searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
+});
+
+const Search = () => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const value = useAppSelector((state) => state.listEditor.suggestions.value);
+
+ const handleChange: React.ChangeEventHandler = e => {
+ dispatch(changeListSuggestions(e.target.value));
+ };
+
+ const handleSubmit = () => {
+ dispatch(fetchListSuggestions(value));
+ };
+
+ const handleClear = () => {
+ dispatch(clearListSuggestions());
+ };
+
+ const hasValue = value.length > 0;
+
+ return (
+
+ );
+};
+
+export default Search;
diff --git a/app/soapbox/features/list_editor/index.js b/app/soapbox/features/list_editor/index.js
deleted file mode 100644
index 82c943d21..000000000
--- a/app/soapbox/features/list_editor/index.js
+++ /dev/null
@@ -1,105 +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 { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-
-import { setupListEditor, clearListSuggestions, resetListEditor } from 'soapbox/actions/lists';
-import { CardHeader, CardTitle, Modal } from 'soapbox/components/ui';
-
-import Account from './components/account';
-import EditListForm from './components/edit_list_form';
-import Search from './components/search';
-
-const mapStateToProps = state => ({
- accountIds: state.getIn(['listEditor', 'accounts', 'items']),
- searchAccountIds: state.getIn(['listEditor', 'suggestions', 'items']),
-});
-
-const mapDispatchToProps = dispatch => ({
- onInitialize: listId => dispatch(setupListEditor(listId)),
- onClear: () => dispatch(clearListSuggestions()),
- onReset: () => dispatch(resetListEditor()),
-});
-
-const messages = defineMessages({
- close: { id: 'lightbox.close', defaultMessage: 'Close' },
- changeTitle: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
- addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
- removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
- editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
-});
-
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-class ListEditor extends ImmutablePureComponent {
-
- static propTypes = {
- listId: PropTypes.string.isRequired,
- onClose: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired,
- onInitialize: PropTypes.func.isRequired,
- onClear: PropTypes.func.isRequired,
- onReset: PropTypes.func.isRequired,
- accountIds: ImmutablePropTypes.list.isRequired,
- searchAccountIds: ImmutablePropTypes.list.isRequired,
- };
-
- componentDidMount() {
- const { onInitialize, listId } = this.props;
- onInitialize(listId);
- }
-
- componentWillUnmount() {
- const { onReset } = this.props;
- onReset();
- }
-
- onClickClose = () => {
- this.props.onClose('LIST_ADDER');
- };
-
- render() {
- const { accountIds, searchAccountIds, intl } = this.props;
-
- return (
- }
- onClose={this.onClickClose}
- >
-
-
-
-
-
-
-
-
- {
- accountIds.size > 0 &&
-
-
-
-
-
- {accountIds.map(accountId =>
)}
-
-
- }
-
-
-
-
-
-
-
- {searchAccountIds.map(accountId =>
)}
-
-
-
-
- );
- }
-
-}
diff --git a/app/soapbox/features/list_editor/index.tsx b/app/soapbox/features/list_editor/index.tsx
new file mode 100644
index 000000000..d495f4b2f
--- /dev/null
+++ b/app/soapbox/features/list_editor/index.tsx
@@ -0,0 +1,79 @@
+import React from 'react';
+import { useEffect } from 'react';
+import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
+
+import { setupListEditor, resetListEditor } from 'soapbox/actions/lists';
+import { CardHeader, CardTitle, Modal } from 'soapbox/components/ui';
+import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
+
+import Account from './components/account';
+import EditListForm from './components/edit_list_form';
+import Search from './components/search';
+
+const messages = defineMessages({
+ close: { id: 'lightbox.close', defaultMessage: 'Close' },
+ changeTitle: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
+ addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
+ removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
+ editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
+});
+
+interface IListEditor {
+ listId: string,
+ onClose: (type: string) => void,
+}
+
+const ListEditor: React.FC = ({ listId, onClose }) => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+
+ const accountIds = useAppSelector((state) => state.listEditor.accounts.items);
+ const searchAccountIds = useAppSelector((state) => state.listEditor.suggestions.items);
+
+ useEffect(() => {
+ dispatch(setupListEditor(listId));
+
+ return () => {
+ dispatch(resetListEditor());
+ };
+ }, []);
+
+ const onClickClose = () => {
+ onClose('LIST_ADDER');
+ };
+
+ return (
+ }
+ onClose={onClickClose}
+ >
+
+
+
+
+
+
+ {accountIds.size > 0 && (
+
+
+
+
+
+ {accountIds.map(accountId =>
)}
+
+
+ )}
+
+
+
+
+
+
+
+ {searchAccountIds.map(accountId =>
)}
+
+
+ );
+};
+
+export default ListEditor;
diff --git a/app/soapbox/features/list_timeline/index.tsx b/app/soapbox/features/list_timeline/index.tsx
index 85441c98f..9402de86a 100644
--- a/app/soapbox/features/list_timeline/index.tsx
+++ b/app/soapbox/features/list_timeline/index.tsx
@@ -65,7 +65,7 @@ const ListTimeline: React.FC = () => {
// }));
// };
- const title = list ? list.get('title') : id;
+ const title = list ? list.title : id;
if (typeof list === 'undefined') {
return (
diff --git a/app/soapbox/features/lists/components/new_list_form.tsx b/app/soapbox/features/lists/components/new_list_form.tsx
index b6385ecf3..eb19d1e87 100644
--- a/app/soapbox/features/lists/components/new_list_form.tsx
+++ b/app/soapbox/features/lists/components/new_list_form.tsx
@@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { changeListEditorTitle, submitListEditor } from 'soapbox/actions/lists';
-import { Button } from 'soapbox/components/ui';
+import { Button, Form, HStack, Input } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
@@ -23,7 +23,7 @@ const NewListForm: React.FC = () => {
dispatch(changeListEditorTitle(e.target.value));
};
- const handleSubmit = (e: React.FormEvent) => {
+ const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
dispatch(submitListEditor(true));
};
@@ -32,27 +32,28 @@ const NewListForm: React.FC = () => {
const create = intl.formatMessage(messages.create);
return (
-
+ onClick={handleSubmit}
+ >
+ {create}
+
+
+
);
};
diff --git a/app/soapbox/features/lists/index.tsx b/app/soapbox/features/lists/index.tsx
index 82f4b48a9..1f3930c6c 100644
--- a/app/soapbox/features/lists/index.tsx
+++ b/app/soapbox/features/lists/index.tsx
@@ -93,13 +93,13 @@ const Lists: React.FC = () => {
itemClassName='py-2'
>
{lists.map((list: any) => (
-
+
- {list.get('title')}
+ {list.title}
-
-
+
+
))}
diff --git a/app/soapbox/features/soapbox_config/components/color-picker.tsx b/app/soapbox/features/soapbox_config/components/color-picker.tsx
index 8b203a901..4ac3943fc 100644
--- a/app/soapbox/features/soapbox_config/components/color-picker.tsx
+++ b/app/soapbox/features/soapbox_config/components/color-picker.tsx
@@ -30,7 +30,7 @@ const ColorPicker: React.FC = ({ style, value, onClose, onChange }
document.removeEventListener('click', handleDocumentClick, false);
document.removeEventListener('touchend', handleDocumentClick);
};
- });
+ }, []);
const pickerStyle: React.CSSProperties = {
...style,
diff --git a/app/soapbox/types/entities.ts b/app/soapbox/types/entities.ts
index c65cecaa4..3df8b2eb0 100644
--- a/app/soapbox/types/entities.ts
+++ b/app/soapbox/types/entities.ts
@@ -9,6 +9,7 @@ import {
EmojiRecord,
FieldRecord,
InstanceRecord,
+ ListRecord,
MentionRecord,
NotificationRecord,
PollRecord,
@@ -28,6 +29,7 @@ type ChatMessage = ReturnType;
type Emoji = ReturnType;
type Field = ReturnType;
type Instance = ReturnType;
+type List = ReturnType;
type Mention = ReturnType;
type Notification = ReturnType;
type Poll = ReturnType;
@@ -61,6 +63,7 @@ export {
Emoji,
Field,
Instance,
+ List,
Mention,
Notification,
Poll,
diff --git a/app/styles/application.scss b/app/styles/application.scss
index 74da239c8..12e286976 100644
--- a/app/styles/application.scss
+++ b/app/styles/application.scss
@@ -44,7 +44,6 @@
@import 'components/status';
@import 'components/reply-mentions';
@import 'components/detailed-status';
-@import 'components/list-forms';
@import 'components/media-gallery';
@import 'components/notification';
@import 'components/display-name';
diff --git a/app/styles/components/list-forms.scss b/app/styles/components/list-forms.scss
deleted file mode 100644
index 354d170ec..000000000
--- a/app/styles/components/list-forms.scss
+++ /dev/null
@@ -1,105 +0,0 @@
-.list-editor {
- flex-direction: column;
- width: 100%;
- overflow: hidden;
- height: 100%;
- overflow-y: auto;
-
- h4 {
- padding: 15px 0;
- background: var(--background-color);
- font-weight: 500;
- font-size: 16px;
- text-align: center;
- border-radius: 8px 8px 0 0;
- }
-
- &__content {
- padding: 0;
- }
-
- &__accounts {
- background: var(--background-color);
- overflow-y: auto;
- max-height: 200px;
- }
-
- .account__display-name {
- &:hover strong {
- text-decoration: none;
- }
- }
-
- .account__avatar {
- cursor: default;
- }
-
- .search {
- display: flex;
- flex-direction: row;
- margin: 10px;
-
- > label {
- flex: 1 1;
- }
-
- > .search__icon {
- position: relative;
- }
-
- > .button {
- width: 80px;
- margin-left: 10px;
- }
- }
-}
-
-.list-adder {
- flex-direction: column;
- width: 100%;
- overflow: hidden;
- height: 100%;
- overflow-y: auto;
-
- &__account {
- background: var(--background-color);
- border-radius: 4px;
- }
-
- &__lists {
- background: var(--background-color);
- }
-
- .list {
- padding: 4px;
- border-bottom: 1px solid var(--brand-color--med);
- }
-
- .list__wrapper {
- display: flex;
-
- .account__relationship {
- padding: 8px 5px 0;
- }
- }
-
- .list__display-name {
- flex: 1 1 auto;
- overflow: hidden;
- text-decoration: none;
- font-size: 16px;
- padding: 10px;
- }
-}
-
-.new-list-form,
-.edit-list-form {
- &__btn {
- margin-left: 6px;
- width: 112px;
- }
-
- &__input {
- height: 36px;
- }
-}
diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss
index 47f0d9b30..9974c7fa0 100644
--- a/app/styles/components/modal.scss
+++ b/app/styles/components/modal.scss
@@ -339,33 +339,6 @@
}
}
-.compose-modal__content {
- display: flex;
- flex-direction: row;
- flex: 1;
- overflow-y: hidden;
-
- &--scroll {
- display: block;
- overflow-y: auto;
- }
-
- .timeline-compose-block {
- background: transparent !important;
- width: 100%;
- padding: 0;
- margin-bottom: 0;
-
- .compose-form {
- max-height: 100%;
- max-width: 100%;
- display: flex;
- flex-direction: column;
- padding: 0 !important;
- }
- }
-}
-
.reply-mentions-modal__accounts {
display: block;
flex-direction: row;
diff --git a/app/styles/components/search.scss b/app/styles/components/search.scss
index 9c2ee3de7..7dcf461c1 100644
--- a/app/styles/components/search.scss
+++ b/app/styles/components/search.scss
@@ -118,13 +118,3 @@ input.search__input {
right: 24px;
}
}
-
-.list-editor__search {
- .search__icon {
- position: relative;
-
- .svg-icon {
- right: 0;
- }
- }
-}