The code powering m.abunchtell.com https://m.abunchtell.com
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

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