2016-11-13 00:33:21 +11:00
import CharacterCounter from './character_counter' ;
import Button from '../../../components/button' ;
import PureRenderMixin from 'react-addons-pure-render-mixin' ;
2016-09-01 06:58:10 +10:00
import ImmutablePropTypes from 'react-immutable-proptypes' ;
2017-02-23 01:43:07 +11:00
import ReplyIndicatorContainer from '../containers/reply_indicator_container' ;
2016-12-15 04:21:31 +11:00
import AutosuggestTextarea from '../../../components/autosuggest_textarea' ;
2016-11-13 23:13:36 +11:00
import { debounce } from 'react-decoration' ;
2016-11-14 05:08:52 +11:00
import UploadButtonContainer from '../containers/upload_button_container' ;
2016-11-24 08:57:57 +11:00
import { defineMessages , injectIntl , FormattedMessage } from 'react-intl' ;
2016-11-24 04:53:23 +11:00
import Toggle from 'react-toggle' ;
2017-02-14 03:20:18 +11:00
import Collapsable from '../../../components/collapsable' ;
2017-03-25 10:01:43 +11:00
import SpoilerButtonContainer from '../containers/spoiler_button_container' ;
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container' ;
import SensitiveButtonContainer from '../containers/sensitive_button_container' ;
2017-03-02 10:57:55 +11:00
import EmojiPickerDropdown from './emoji_picker_dropdown' ;
2017-03-25 10:01:43 +11:00
import UploadFormContainer from '../containers/upload_form_container' ;
import TextIconButton from './text_icon_button' ;
2016-11-19 01:36:16 +11:00
const messages = defineMessages ( {
placeholder : { id : 'compose_form.placeholder' , defaultMessage : 'What is on your mind?' } ,
2017-01-13 15:54:26 +11:00
spoiler _placeholder : { id : 'compose_form.spoiler_placeholder' , defaultMessage : 'Content warning' } ,
2016-11-19 01:36:16 +11:00
publish : { id : 'compose_form.publish' , defaultMessage : 'Publish' }
} ) ;
2016-10-31 04:13:05 +11:00
2016-09-03 22:01:10 +10:00
const ComposeForm = React . createClass ( {
2016-08-26 03:52:55 +10:00
propTypes : {
2016-12-27 07:52:03 +11:00
intl : React . PropTypes . object . isRequired ,
2016-09-01 00:15:12 +10:00
text : React . PropTypes . string . isRequired ,
2016-11-13 00:33:21 +11:00
suggestion _token : React . PropTypes . string ,
2016-12-15 04:21:31 +11:00
suggestions : ImmutablePropTypes . list ,
2017-01-13 15:54:26 +11:00
spoiler : React . PropTypes . bool ,
2016-12-24 11:22:47 +11:00
private : React . PropTypes . bool ,
2017-02-26 11:23:44 +11:00
unlisted : React . PropTypes . bool ,
spoiler _text : React . PropTypes . string ,
2017-02-23 01:43:07 +11:00
focusDate : React . PropTypes . instanceOf ( Date ) ,
preselectDate : React . PropTypes . instanceOf ( Date ) ,
2016-09-01 06:58:10 +10:00
is _submitting : React . PropTypes . bool ,
2016-09-28 01:02:30 +10:00
is _uploading : React . PropTypes . bool ,
2017-01-17 02:23:45 +11:00
me : React . PropTypes . number ,
2017-02-14 04:38:00 +11:00
needsPrivacyWarning : React . PropTypes . bool ,
mentionedDomains : React . PropTypes . array . isRequired ,
2016-09-01 00:15:12 +10:00
onChange : React . PropTypes . func . isRequired ,
2016-09-01 06:58:10 +10:00
onSubmit : React . PropTypes . func . isRequired ,
2016-11-13 00:33:21 +11:00
onClearSuggestions : React . PropTypes . func . isRequired ,
onFetchSuggestions : React . PropTypes . func . isRequired ,
2016-11-24 04:53:23 +11:00
onSuggestionSelected : React . PropTypes . func . isRequired ,
2017-01-13 15:54:26 +11:00
onChangeSpoilerText : React . PropTypes . func . isRequired ,
2017-03-01 21:56:15 +11:00
onPaste : React . PropTypes . func . isRequired ,
2017-03-02 10:57:55 +11:00
onPickEmoji : React . PropTypes . func . isRequired
2016-08-26 03:52:55 +10:00
} ,
2016-09-01 00:15:12 +10:00
mixins : [ PureRenderMixin ] ,
2016-08-26 03:52:55 +10:00
handleChange ( e ) {
2016-09-01 00:15:12 +10:00
this . props . onChange ( e . target . value ) ;
2016-08-26 03:52:55 +10:00
} ,
2017-01-05 13:29:43 +11:00
handleKeyDown ( e ) {
2016-12-12 09:54:32 +11:00
if ( e . keyCode === 13 && ( e . ctrlKey || e . metaKey ) ) {
2016-09-01 00:15:12 +10:00
this . props . onSubmit ( ) ;
2016-08-26 03:52:55 +10:00
}
} ,
handleSubmit ( ) {
2016-09-01 00:15:12 +10:00
this . props . onSubmit ( ) ;
2016-08-26 03:52:55 +10:00
} ,
2016-10-31 04:13:05 +11:00
onSuggestionsClearRequested ( ) {
this . props . onClearSuggestions ( ) ;
} ,
2016-11-13 23:13:36 +11:00
@ debounce ( 500 )
2016-12-15 04:21:31 +11:00
onSuggestionsFetchRequested ( token ) {
this . props . onFetchSuggestions ( token ) ;
2016-10-31 04:13:05 +11:00
} ,
2016-12-15 04:21:31 +11:00
onSuggestionSelected ( tokenStart , token , value ) {
2017-03-02 10:57:55 +11:00
this . _restoreCaret = null ;
2016-12-15 04:21:31 +11:00
this . props . onSuggestionSelected ( tokenStart , token , value ) ;
2016-11-13 00:33:21 +11:00
} ,
2017-01-13 15:54:26 +11:00
handleChangeSpoilerText ( e ) {
this . props . onChangeSpoilerText ( e . target . value ) ;
} ,
2016-12-15 04:21:31 +11:00
componentDidUpdate ( prevProps ) {
2017-02-23 01:43:07 +11:00
if ( this . props . focusDate !== prevProps . focusDate ) {
2017-01-05 15:04:14 +11:00
// If replying to zero or one users, places the cursor at the end of the textbox.
// If replying to more than one user, selects any usernames past the first;
// this provides a convenient shortcut to drop everyone else from the conversation.
2017-03-02 10:57:55 +11:00
let selectionEnd , selectionStart ;
if ( this . props . preselectDate !== prevProps . preselectDate ) {
selectionEnd = this . props . text . length ;
selectionStart = this . props . text . search ( /\s/ ) + 1 ;
} else if ( typeof this . _restoreCaret === 'number' ) {
selectionStart = this . _restoreCaret ;
selectionEnd = this . _restoreCaret ;
} else {
selectionEnd = this . props . text . length ;
selectionStart = selectionEnd ;
}
2017-01-05 15:04:14 +11:00
2017-01-06 00:06:09 +11:00
this . autosuggestTextarea . textarea . setSelectionRange ( selectionStart , selectionEnd ) ;
2016-12-15 04:21:31 +11:00
this . autosuggestTextarea . textarea . focus ( ) ;
}
} ,
setAutosuggestTextarea ( c ) {
this . autosuggestTextarea = c ;
} ,
2017-03-02 10:57:55 +11:00
handleEmojiPick ( data ) {
const position = this . autosuggestTextarea . textarea . selectionStart ;
this . _restoreCaret = position + data . shortname . length + 1 ;
this . props . onPickEmoji ( position , data ) ;
} ,
2016-08-26 03:52:55 +10:00
render ( ) {
2017-03-01 23:57:30 +11:00
const { intl , needsPrivacyWarning , mentionedDomains , onPaste } = this . props ;
2017-02-14 04:38:00 +11:00
const disabled = this . props . is _submitting || this . props . is _uploading ;
let publishText = '' ;
let privacyWarning = '' ;
2017-02-23 01:43:07 +11:00
let reply _to _other = false ;
2016-09-01 06:58:10 +10:00
2017-02-14 04:38:00 +11:00
if ( needsPrivacyWarning ) {
privacyWarning = (
< div className = 'compose-form__warning' >
< FormattedMessage
id = 'compose_form.privacy_disclaimer'
defaultMessage = 'Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}} to not leak your status?'
values = { { domains : < strong > { mentionedDomains . join ( ', ' ) } < / strong > , domainsCount : mentionedDomains . length } }
/ >
< / div >
) ;
}
2017-01-17 02:23:45 +11:00
2017-02-07 09:16:20 +11:00
if ( this . props . private ) {
publishText = < span > < i className = 'fa fa-lock' / > { intl . formatMessage ( messages . publish ) } < / span > ;
} else {
publishText = intl . formatMessage ( messages . publish ) + ( ! this . props . unlisted ? '!' : '' ) ;
}
2016-08-26 03:52:55 +10:00
return (
2016-09-08 02:17:15 +10:00
< div style = { { padding : '10px' } } >
2017-02-14 03:20:18 +11:00
< Collapsable isVisible = { this . props . spoiler } fullHeight = { 50 } >
< div className = "spoiler-input" >
< input placeholder = { intl . formatMessage ( messages . spoiler _placeholder ) } value = { this . props . spoiler _text } onChange = { this . handleChangeSpoilerText } type = "text" className = "spoiler-input__input" / >
< / div >
< / Collapsable >
2017-01-13 15:54:26 +11:00
2017-02-14 04:38:00 +11:00
{ privacyWarning }
2017-02-23 01:43:07 +11:00
< ReplyIndicatorContainer / >
2016-09-01 06:58:10 +10:00
2017-03-25 10:01:43 +11:00
< div style = { { position : 'relative' } } >
< AutosuggestTextarea
ref = { this . setAutosuggestTextarea }
placeholder = { intl . formatMessage ( messages . placeholder ) }
disabled = { disabled }
value = { this . props . text }
onChange = { this . handleChange }
suggestions = { this . props . suggestions }
onKeyDown = { this . handleKeyDown }
onSuggestionsFetchRequested = { this . onSuggestionsFetchRequested }
onSuggestionsClearRequested = { this . onSuggestionsClearRequested }
onSuggestionSelected = { this . onSuggestionSelected }
onPaste = { onPaste }
/ >
< EmojiPickerDropdown onPickEmoji = { this . handleEmojiPick } / >
< / div >
< div className = 'compose-form__modifiers' >
< UploadFormContainer / >
< / div >
< div style = { { display : 'flex' } } >
< div className = 'compose-form__buttons' >
2017-03-02 10:57:55 +11:00
< UploadButtonContainer / >
2017-03-25 10:01:43 +11:00
< PrivacyDropdownContainer / >
< SensitiveButtonContainer / >
< SpoilerButtonContainer / >
2017-03-02 10:57:55 +11:00
< / div >
2016-11-24 04:53:23 +11:00
2017-03-25 10:01:43 +11:00
< div style = { { paddingTop : '10px' , marginRight : '16px' , lineHeight : '36px' } } > < CharacterCounter max = { 500 } text = { [ this . props . spoiler _text , this . props . text ] . join ( '' ) } / > < / div >
< div style = { { paddingTop : '10px' } } > < Button text = { publishText } onClick = { this . handleSubmit } disabled = { disabled } / > < / div >
< / div >
2016-08-26 03:52:55 +10:00
< / div >
) ;
}
} ) ;
2016-11-17 03:20:52 +11:00
export default injectIntl ( ComposeForm ) ;