diff --git a/app/soapbox/actions/compose.js b/app/soapbox/actions/compose.js
index 6807cec4b..b77944ef8 100644
--- a/app/soapbox/actions/compose.js
+++ b/app/soapbox/actions/compose.js
@@ -136,6 +136,19 @@ export function directCompose(account, routerHistory) {
};
}
+export function directComposeById(accountId) {
+ return (dispatch, getState) => {
+ const account = getState().getIn(['accounts', accountId]);
+
+ dispatch({
+ type: COMPOSE_DIRECT,
+ account: account,
+ });
+
+ dispatch(openModal('COMPOSE'));
+ };
+}
+
export function handleComposeSubmit(dispatch, getState, data, status) {
if (!dispatch || !getState) return;
diff --git a/app/soapbox/components/autosuggest_account_input.js b/app/soapbox/components/autosuggest_account_input.js
new file mode 100644
index 000000000..95f38ba79
--- /dev/null
+++ b/app/soapbox/components/autosuggest_account_input.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import AutosuggestInput from './autosuggest_input';
+import PropTypes from 'prop-types';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import { connect } from 'react-redux';
+import { injectIntl, defineMessages } from 'react-intl';
+import { OrderedSet as ImmutableOrderedSet } from 'immutable';
+import { accountSearch } from 'soapbox/actions/accounts';
+import { throttle } from 'lodash';
+
+const noOp = () => {};
+
+const messages = defineMessages({
+ placeholder: { id: 'autosuggest_account_input.default_placeholder', defaultMessage: 'Search for an account' },
+});
+
+export default @connect()
+@injectIntl
+class AutosuggestAccountInput extends ImmutablePureComponent {
+
+ static propTypes = {
+ intl: PropTypes.object.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ onSelected: PropTypes.func.isRequired,
+ }
+
+ state = {
+ text: '',
+ accountIds: ImmutableOrderedSet(),
+ }
+
+ handleAccountSearch = throttle(q => {
+ const { dispatch } = this.props;
+
+ const params = {
+ q,
+ resolve: false,
+ limit: 4,
+ };
+
+ dispatch(accountSearch(params))
+ .then(accounts => {
+ const accountIds = accounts.map(account => account.id);
+ this.setState({ accountIds: ImmutableOrderedSet(accountIds) });
+ })
+ .catch(noOp);
+
+ }, 900, { leading: true, trailing: true })
+
+ handleChange = ({ target }) => {
+ this.handleAccountSearch(target.value);
+ this.setState({ text: target.value });
+ }
+
+ handleSelected = (tokenStart, lastToken, accountId) => {
+ this.props.onSelected(accountId);
+ }
+
+ render() {
+ const { intl, ...rest } = this.props;
+ const { text, accountIds } = this.state;
+
+ return (
+
+ );
+ }
+
+}
diff --git a/app/soapbox/features/direct_timeline/index.js b/app/soapbox/features/direct_timeline/index.js
index 27f17c0d6..573ed78fe 100644
--- a/app/soapbox/features/direct_timeline/index.js
+++ b/app/soapbox/features/direct_timeline/index.js
@@ -7,6 +7,8 @@ import ColumnHeader from '../../components/column_header';
import { expandDirectTimeline } from '../../actions/timelines';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connectDirectStream } from '../../actions/streaming';
+import { directComposeById } from 'soapbox/actions/compose';
+import AutosuggestAccountInput from 'soapbox/components/autosuggest_account_input';
const messages = defineMessages({
title: { id: 'column.direct', defaultMessage: 'Direct messages' },
@@ -40,6 +42,10 @@ class DirectTimeline extends React.PureComponent {
}
}
+ handleSuggestion = accountId => {
+ this.props.dispatch(directComposeById(accountId));
+ }
+
handleLoadMore = maxId => {
this.props.dispatch(expandDirectTimeline({ maxId }));
}
@@ -56,6 +62,8 @@ class DirectTimeline extends React.PureComponent {
onPin={this.handlePin}
/>
+
+