You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
4.9 KiB

  1. import CharacterCounter from './character_counter';
  2. import Button from '../../../components/button';
  3. import PureRenderMixin from 'react-addons-pure-render-mixin';
  4. import ImmutablePropTypes from 'react-immutable-proptypes';
  5. import ReplyIndicator from './reply_indicator';
  6. import UploadButton from './upload_button';
  7. import Autosuggest from 'react-autosuggest';
  8. import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container';
  9. import { debounce } from 'react-decoration';
  10. const getTokenForSuggestions = (str, caretPosition) => {
  11. let word;
  12. let left = str.slice(0, caretPosition).search(/\S+$/);
  13. let right = str.slice(caretPosition).search(/\s/);
  14. if (right < 0) {
  15. word = str.slice(left);
  16. } else {
  17. word = str.slice(left, right + caretPosition);
  18. }
  19. if (!word || word.trim().length < 2 || word[0] !== '@') {
  20. return null;
  21. }
  22. word = word.trim().toLowerCase().slice(1);
  23. if (word.length > 0) {
  24. return word;
  25. } else {
  26. return null;
  27. }
  28. };
  29. const getSuggestionValue = suggestionId => suggestionId;
  30. const renderSuggestion = suggestionId => <AutosuggestAccountContainer id={suggestionId} />;
  31. const textareaStyle = {
  32. display: 'block',
  33. boxSizing: 'border-box',
  34. width: '100%',
  35. height: '100px',
  36. resize: 'none',
  37. border: 'none',
  38. color: '#282c37',
  39. padding: '10px',
  40. fontFamily: 'Roboto',
  41. fontSize: '14px',
  42. margin: '0'
  43. };
  44. const renderInputComponent = inputProps => (
  45. <textarea {...inputProps} placeholder='What is on your mind?' className='compose-form__textarea' style={textareaStyle} />
  46. );
  47. const ComposeForm = React.createClass({
  48. propTypes: {
  49. text: React.PropTypes.string.isRequired,
  50. suggestion_token: React.PropTypes.string,
  51. suggestions: React.PropTypes.array,
  52. is_submitting: React.PropTypes.bool,
  53. is_uploading: React.PropTypes.bool,
  54. in_reply_to: ImmutablePropTypes.map,
  55. onChange: React.PropTypes.func.isRequired,
  56. onSubmit: React.PropTypes.func.isRequired,
  57. onCancelReply: React.PropTypes.func.isRequired,
  58. onClearSuggestions: React.PropTypes.func.isRequired,
  59. onFetchSuggestions: React.PropTypes.func.isRequired,
  60. onSuggestionSelected: React.PropTypes.func.isRequired
  61. },
  62. mixins: [PureRenderMixin],
  63. handleChange (e) {
  64. if (typeof e.target.value === 'undefined' || typeof e.target.value === 'number') {
  65. return;
  66. }
  67. this.props.onChange(e.target.value);
  68. },
  69. handleKeyUp (e) {
  70. if (e.keyCode === 13 && e.ctrlKey) {
  71. this.props.onSubmit();
  72. }
  73. },
  74. handleSubmit () {
  75. this.props.onSubmit();
  76. },
  77. componentDidUpdate (prevProps) {
  78. if (prevProps.text !== this.props.text || prevProps.in_reply_to !== this.props.in_reply_to) {
  79. const textarea = this.autosuggest.input;
  80. if (textarea) {
  81. textarea.focus();
  82. }
  83. }
  84. },
  85. onSuggestionsClearRequested () {
  86. this.props.onClearSuggestions();
  87. },
  88. @debounce(500)
  89. onSuggestionsFetchRequested ({ value }) {
  90. const textarea = this.autosuggest.input;
  91. if (textarea) {
  92. const token = getTokenForSuggestions(value, textarea.selectionStart);
  93. if (token !== null) {
  94. this.props.onFetchSuggestions(token);
  95. } else {
  96. this.props.onClearSuggestions();
  97. }
  98. }
  99. },
  100. onSuggestionSelected (e, { suggestionValue }) {
  101. const textarea = this.autosuggest.input;
  102. if (textarea) {
  103. this.props.onSuggestionSelected(textarea.selectionStart, suggestionValue);
  104. }
  105. },
  106. setRef (c) {
  107. this.autosuggest = c;
  108. },
  109. render () {
  110. let replyArea = '';
  111. const disabled = this.props.is_submitting || this.props.is_uploading;
  112. if (this.props.in_reply_to) {
  113. replyArea = <ReplyIndicator status={this.props.in_reply_to} onCancel={this.props.onCancelReply} />;
  114. }
  115. const inputProps = {
  116. placeholder: 'What is on your mind?',
  117. value: this.props.text,
  118. onKeyUp: this.handleKeyUp,
  119. onChange: this.handleChange,
  120. disabled: disabled
  121. };
  122. return (
  123. <div style={{ padding: '10px' }}>
  124. {replyArea}
  125. <Autosuggest
  126. ref={this.setRef}
  127. suggestions={this.props.suggestions}
  128. focusFirstSuggestion={true}
  129. onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
  130. onSuggestionsClearRequested={this.onSuggestionsClearRequested}
  131. onSuggestionSelected={this.onSuggestionSelected}
  132. getSuggestionValue={getSuggestionValue}
  133. renderSuggestion={renderSuggestion}
  134. renderInputComponent={renderInputComponent}
  135. inputProps={inputProps}
  136. />
  137. <div style={{ marginTop: '10px', overflow: 'hidden' }}>
  138. <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={disabled} /></div>
  139. <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div>
  140. </div>
  141. </div>
  142. );
  143. }
  144. });
  145. export default ComposeForm;