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' ;
2016-11-13 00:33:21 +11:00
import ReplyIndicator from './reply_indicator' ;
import UploadButton from './upload_button' ;
2016-12-15 04:21:31 +11:00
import AutosuggestTextarea from '../../../components/autosuggest_textarea' ;
2016-11-13 00:33:21 +11:00
import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container' ;
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' ;
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 ,
2016-11-24 04:53:23 +11:00
sensitive : React . PropTypes . bool ,
2017-01-13 15:54:26 +11:00
spoiler : React . PropTypes . bool ,
spoiler _text : React . PropTypes . string ,
2016-12-01 07:32:11 +11:00
unlisted : React . PropTypes . bool ,
2016-12-24 11:22:47 +11:00
private : React . PropTypes . bool ,
2017-01-03 19:36:48 +11:00
fileDropDate : 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 ,
2016-09-01 06:58:10 +10:00
in _reply _to : ImmutablePropTypes . map ,
2016-12-24 11:22:47 +11:00
media _count : React . PropTypes . number ,
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
onCancelReply : React . PropTypes . func . isRequired ,
onClearSuggestions : React . PropTypes . func . isRequired ,
onFetchSuggestions : React . PropTypes . func . isRequired ,
2016-11-24 04:53:23 +11:00
onSuggestionSelected : React . PropTypes . func . isRequired ,
2016-12-01 07:32:11 +11:00
onChangeSensitivity : React . PropTypes . func . isRequired ,
2017-01-13 15:54:26 +11:00
onChangeSpoilerness : React . PropTypes . func . isRequired ,
onChangeSpoilerText : React . PropTypes . func . isRequired ,
2016-12-24 01:20:16 +11:00
onChangeVisibility : React . PropTypes . func . isRequired ,
onChangeListability : 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 ) {
this . props . onSuggestionSelected ( tokenStart , token , value ) ;
2016-11-13 00:33:21 +11:00
} ,
2016-11-24 04:53:23 +11:00
handleChangeSensitivity ( e ) {
this . props . onChangeSensitivity ( e . target . checked ) ;
} ,
2017-01-13 15:54:26 +11:00
handleChangeSpoilerness ( e ) {
this . props . onChangeSpoilerness ( e . target . checked ) ;
this . props . onChangeSpoilerText ( '' ) ;
} ,
handleChangeSpoilerText ( e ) {
this . props . onChangeSpoilerText ( e . target . value ) ;
} ,
2016-12-01 07:32:11 +11:00
handleChangeVisibility ( e ) {
this . props . onChangeVisibility ( e . target . checked ) ;
} ,
2016-12-24 11:22:47 +11:00
2016-12-24 01:20:16 +11:00
handleChangeListability ( e ) {
this . props . onChangeListability ( e . target . checked ) ;
} ,
2016-12-01 07:32:11 +11:00
2016-12-15 04:21:31 +11:00
componentDidUpdate ( prevProps ) {
2017-01-06 00:23:59 +11:00
if ( ( prevProps . in _reply _to === null && this . props . in _reply _to !== null ) || ( prevProps . in _reply _to !== null && this . props . in _reply _to !== null && prevProps . in _reply _to . get ( 'id' ) !== this . props . in _reply _to . get ( 'id' ) ) ) {
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-01-06 00:06:09 +11:00
const selectionStart = this . props . text . search ( /\s/ ) + 1 ;
2017-01-06 00:23:59 +11:00
const selectionEnd = this . props . text . length ;
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 ;
} ,
2016-08-26 03:52:55 +10:00
render ( ) {
2017-02-14 04:38:00 +11:00
const { intl , needsPrivacyWarning , mentionedDomains } = this . props ;
const disabled = this . props . is _submitting || this . props . is _uploading ;
let replyArea = '' ;
let publishText = '' ;
let privacyWarning = '' ;
let reply _to _other = ! ! this . props . in _reply _to && ( this . props . in _reply _to . getIn ( [ 'account' , 'id' ] ) !== this . props . me ) ;
2016-09-01 06:58:10 +10:00
if ( this . props . in _reply _to ) {
replyArea = < ReplyIndicator status = { this . props . in _reply _to } onCancel = { this . props . onCancelReply } / > ;
}
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 }
2016-09-01 06:58:10 +10:00
{ replyArea }
2016-12-15 04:21:31 +11:00
< AutosuggestTextarea
ref = { this . setAutosuggestTextarea }
placeholder = { intl . formatMessage ( messages . placeholder ) }
disabled = { disabled }
2017-01-03 19:36:48 +11:00
fileDropDate = { this . props . fileDropDate }
2016-12-15 04:21:31 +11:00
value = { this . props . text }
onChange = { this . handleChange }
2016-10-31 04:13:05 +11:00
suggestions = { this . props . suggestions }
2017-01-05 13:29:43 +11:00
onKeyDown = { this . handleKeyDown }
2016-10-31 04:13:05 +11:00
onSuggestionsFetchRequested = { this . onSuggestionsFetchRequested }
onSuggestionsClearRequested = { this . onSuggestionsClearRequested }
onSuggestionSelected = { this . onSuggestionSelected }
/ >
2016-08-26 03:52:55 +10:00
< div style = { { marginTop : '10px' , overflow : 'hidden' } } >
2017-02-07 09:16:20 +11:00
< div style = { { float : 'right' } } > < Button text = { publishText } onClick = { this . handleSubmit } disabled = { disabled } / > < / div >
2017-01-25 10:49:08 +11:00
< div style = { { float : 'right' , marginRight : '16px' , lineHeight : '36px' } } > < CharacterCounter max = { 500 } text = { [ this . props . spoiler _text , this . props . text ] . join ( '' ) } / > < / div >
2016-11-14 05:08:52 +11:00
< UploadButtonContainer style = { { paddingTop : '4px' } } / >
2016-08-26 03:52:55 +10:00
< / div >
2016-11-24 04:53:23 +11:00
2017-02-09 11:20:09 +11:00
< label className = 'compose-form__label with-border' style = { { marginTop : '10px' } } >
2017-02-04 01:54:23 +11:00
< Toggle checked = { this . props . spoiler } onChange = { this . handleChangeSpoilerness } / >
2017-02-09 11:20:09 +11:00
< span className = 'compose-form__label__text' > < FormattedMessage id = 'compose_form.spoiler' defaultMessage = 'Hide text behind warning' / > < / span >
2016-12-01 07:32:11 +11:00
< / label >
2016-12-24 01:20:16 +11:00
2017-02-09 11:20:09 +11:00
< label className = 'compose-form__label with-border' >
2017-02-04 01:54:23 +11:00
< Toggle checked = { this . props . private } onChange = { this . handleChangeVisibility } / >
2017-02-09 11:20:09 +11:00
< span className = 'compose-form__label__text' > < FormattedMessage id = 'compose_form.private' defaultMessage = 'Mark as private' / > < / span >
2017-01-13 15:54:26 +11:00
< / label >
2017-02-14 03:20:18 +11:00
< Collapsable isVisible = { ! ( this . props . private || reply _to _other ) } fullHeight = { 39.5 } >
< label className = 'compose-form__label' >
< Toggle checked = { this . props . unlisted } onChange = { this . handleChangeListability } / >
2017-02-20 08:42:38 +11:00
< span className = 'compose-form__label__text' > < FormattedMessage id = 'compose_form.unlisted' defaultMessage = 'Do not display on public timelines' / > < / span >
2017-02-14 03:20:18 +11:00
< / label >
< / Collapsable >
< Collapsable isVisible = { this . props . media _count > 0 } fullHeight = { 39.5 } >
< label className = 'compose-form__label' >
< Toggle checked = { this . props . sensitive } onChange = { this . handleChangeSensitivity } / >
< span className = 'compose-form__label__text' > < FormattedMessage id = 'compose_form.sensitive' defaultMessage = 'Mark media as sensitive' / > < / span >
< / label >
< / Collapsable >
2016-08-26 03:52:55 +10:00
< / div >
) ;
}
} ) ;
2016-11-17 03:20:52 +11:00
export default injectIntl ( ComposeForm ) ;