about summary refs log tree commit diff
path: root/app/assets/javascripts/components/features
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/components/features')
-rw-r--r--app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx11
-rw-r--r--app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx15
-rw-r--r--app/assets/javascripts/components/features/ui/components/compose_form.jsx53
-rw-r--r--app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx10
4 files changed, 65 insertions, 24 deletions
diff --git a/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx b/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
new file mode 100644
index 000000000..9ea7f190f
--- /dev/null
+++ b/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
@@ -0,0 +1,11 @@
+import Avatar from '../../../components/avatar';
+import DisplayName from '../../../components/display_name';
+
+const AutosuggestAccount = ({ account }) => (
+  <div style={{ overflow: 'hidden' }}>
+    <div style={{ float: 'left', marginRight: '5px' }}><Avatar src={account.get('avatar')} size={18} /></div>
+    <DisplayName account={account} />
+  </div>
+);
+
+export default AutosuggestAccount;
diff --git a/app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx b/app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx
new file mode 100644
index 000000000..de76a364d
--- /dev/null
+++ b/app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import AutosuggestAccount from '../components/autosuggest_account';
+import { makeGetAccount } from '../../../selectors';
+
+const makeMapStateToProps = () => {
+  const getAccount = makeGetAccount();
+
+  const mapStateToProps = (state, { id }) => ({
+    account: getAccount(state, id)
+  });
+
+  return mapStateToProps;
+};
+
+export default connect(makeMapStateToProps)(AutosuggestAccount);
diff --git a/app/assets/javascripts/components/features/ui/components/compose_form.jsx b/app/assets/javascripts/components/features/ui/components/compose_form.jsx
index 0655a7c79..20dc32709 100644
--- a/app/assets/javascripts/components/features/ui/components/compose_form.jsx
+++ b/app/assets/javascripts/components/features/ui/components/compose_form.jsx
@@ -1,10 +1,11 @@
-import CharacterCounter   from './character_counter';
-import Button             from '../../../components/button';
-import PureRenderMixin    from 'react-addons-pure-render-mixin';
+import CharacterCounter from './character_counter';
+import Button from '../../../components/button';
+import PureRenderMixin from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
-import ReplyIndicator     from './reply_indicator';
-import UploadButton       from './upload_button';
-import Autosuggest        from 'react-autosuggest';
+import ReplyIndicator from './reply_indicator';
+import UploadButton from './upload_button';
+import Autosuggest from 'react-autosuggest';
+import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container';
 
 const getTokenForSuggestions = (str, caretPosition) => {
   let word;
@@ -31,11 +32,8 @@ const getTokenForSuggestions = (str, caretPosition) => {
   }
 };
 
-const getSuggestionValue = suggestion => suggestion.completion;
-
-const renderSuggestion = suggestion => (
-  <span>{suggestion.label}</span>
-);
+const getSuggestionValue = suggestionId => suggestionId;
+const renderSuggestion   = suggestionId => <AutosuggestAccountContainer id={suggestionId} />;
 
 const textareaStyle = {
   display: 'block',
@@ -59,18 +57,26 @@ const ComposeForm = React.createClass({
 
   propTypes: {
     text: React.PropTypes.string.isRequired,
+    suggestion_token: React.PropTypes.string,
     suggestions: React.PropTypes.array,
     is_submitting: React.PropTypes.bool,
     is_uploading: React.PropTypes.bool,
     in_reply_to: ImmutablePropTypes.map,
     onChange: React.PropTypes.func.isRequired,
     onSubmit: React.PropTypes.func.isRequired,
-    onCancelReply: React.PropTypes.func.isRequired
+    onCancelReply: React.PropTypes.func.isRequired,
+    onClearSuggestions: React.PropTypes.func.isRequired,
+    onFetchSuggestions: React.PropTypes.func.isRequired,
+    onSuggestionSelected: React.PropTypes.func.isRequired
   },
 
   mixins: [PureRenderMixin],
 
   handleChange (e) {
+    if (typeof e.target.value === 'undefined' || typeof e.target.value === 'number') {
+      return;
+    }
+
     this.props.onChange(e.target.value);
   },
 
@@ -86,8 +92,7 @@ const ComposeForm = React.createClass({
 
   componentDidUpdate (prevProps) {
     if (prevProps.text !== this.props.text || prevProps.in_reply_to !== this.props.in_reply_to) {
-      const node     = ReactDOM.findDOMNode(this.refs.autosuggest);
-      const textarea = node.querySelector('textarea');
+      const textarea = this.autosuggest.input;
 
       if (textarea) {
         textarea.focus();
@@ -100,28 +105,31 @@ const ComposeForm = React.createClass({
   },
 
   onSuggestionsFetchRequested ({ value }) {
-    const node     = ReactDOM.findDOMNode(this.refs.autosuggest);
-    const textarea = node.querySelector('textarea');
+    const textarea = this.autosuggest.input;
 
     if (textarea) {
       const token = getTokenForSuggestions(value, textarea.selectionStart);
 
       if (token !== null) {
         this.props.onFetchSuggestions(token);
+      } else {
+        this.props.onClearSuggestions();
       }
     }
   },
 
-  onSuggestionSelected (e, { suggestionValue, method }) {
-    const node     = ReactDOM.findDOMNode(this.refs.autosuggest);
-    const textarea = node.querySelector('textarea');
+  onSuggestionSelected (e, { suggestionValue }) {
+    const textarea = this.autosuggest.input;
 
     if (textarea) {
-      const str = this.props.text;
-      this.props.onChange([str.slice(0, textarea.selectionStart), suggestionValue, str.slice(textarea.selectionStart)].join(''));
+      this.props.onSuggestionSelected(textarea.selectionStart, suggestionValue);
     }
   },
 
+  setRef (c) {
+    this.autosuggest = c;
+  },
+
   render () {
     let replyArea  = '';
     const disabled = this.props.is_submitting || this.props.is_uploading;
@@ -143,8 +151,9 @@ const ComposeForm = React.createClass({
         {replyArea}
 
         <Autosuggest
-          ref='autosuggest'
+          ref={this.setRef}
           suggestions={this.props.suggestions}
+          focusFirstSuggestion={true}
           onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
           onSuggestionsClearRequested={this.onSuggestionsClearRequested}
           onSuggestionSelected={this.onSuggestionSelected}
diff --git a/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx
index dcfeef752..87bcd6b99 100644
--- a/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx
+++ b/app/assets/javascripts/components/features/ui/containers/compose_form_container.jsx
@@ -5,7 +5,8 @@ import {
   submitCompose,
   cancelReplyCompose,
   clearComposeSuggestions,
-  fetchComposeSuggestions
+  fetchComposeSuggestions,
+  selectComposeSuggestion
 } from '../../../actions/compose';
 import { makeGetStatus } from '../../../selectors';
 
@@ -15,7 +16,8 @@ const makeMapStateToProps = () => {
   const mapStateToProps = function (state, props) {
     return {
       text: state.getIn(['compose', 'text']),
-      suggestions: state.getIn(['compose', 'suggestions']),
+      suggestion_token: state.getIn(['compose', 'suggestion_token']),
+      suggestions: state.getIn(['compose', 'suggestions']).toJS(),
       is_submitting: state.getIn(['compose', 'is_submitting']),
       is_uploading: state.getIn(['compose', 'is_uploading']),
       in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to']))
@@ -45,6 +47,10 @@ const mapDispatchToProps = function (dispatch) {
 
     onFetchSuggestions (token) {
       dispatch(fetchComposeSuggestions(token));
+    },
+
+    onSuggestionSelected (position, accountId) {
+      dispatch(selectComposeSuggestion(position, accountId));
     }
   }
 };