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' ;
import Autosuggest from 'react-autosuggest' ;
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' ;
2016-11-19 01:36:16 +11:00
const messages = defineMessages ( {
placeholder : { id : 'compose_form.placeholder' , defaultMessage : 'What is on your mind?' } ,
publish : { id : 'compose_form.publish' , defaultMessage : 'Publish' }
} ) ;
2016-10-31 04:13:05 +11:00
const getTokenForSuggestions = ( str , caretPosition ) => {
let word ;
let left = str . slice ( 0 , caretPosition ) . search ( /\S+$/ ) ;
let right = str . slice ( caretPosition ) . search ( /\s/ ) ;
if ( right < 0 ) {
word = str . slice ( left ) ;
} else {
word = str . slice ( left , right + caretPosition ) ;
}
if ( ! word || word . trim ( ) . length < 2 || word [ 0 ] !== '@' ) {
return null ;
}
word = word . trim ( ) . toLowerCase ( ) . slice ( 1 ) ;
if ( word . length > 0 ) {
return word ;
} else {
return null ;
}
} ;
2016-11-13 00:33:21 +11:00
const getSuggestionValue = suggestionId => suggestionId ;
const renderSuggestion = suggestionId => < AutosuggestAccountContainer id = { suggestionId } / > ;
2016-10-31 04:13:05 +11:00
const textareaStyle = {
display : 'block' ,
boxSizing : 'border-box' ,
width : '100%' ,
height : '100px' ,
resize : 'none' ,
border : 'none' ,
color : '#282c37' ,
padding : '10px' ,
fontFamily : 'Roboto' ,
fontSize : '14px' ,
2016-11-26 04:17:47 +11:00
margin : '0' ,
resize : 'vertical'
2016-10-31 04:13:05 +11:00
} ;
const renderInputComponent = inputProps => (
2016-11-19 01:36:16 +11:00
< textarea { ...inputProps } className = 'compose-form__textarea' style = { textareaStyle } / >
2016-10-31 04:13:05 +11:00
) ;
2016-08-26 03:52:55 +10:00
2016-09-03 22:01:10 +10:00
const ComposeForm = React . createClass ( {
2016-08-26 03:52:55 +10:00
propTypes : {
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-10-31 04:13:05 +11:00
suggestions : React . PropTypes . array ,
2016-11-24 04:53:23 +11:00
sensitive : React . PropTypes . bool ,
2016-12-01 07:32:11 +11:00
unlisted : React . PropTypes . bool ,
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-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 ,
onChangeVisibility : 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-11-13 00:33:21 +11:00
if ( typeof e . target . value === 'undefined' || typeof e . target . value === 'number' ) {
return ;
}
2016-09-01 00:15:12 +10:00
this . props . onChange ( e . target . value ) ;
2016-08-26 03:52:55 +10:00
} ,
handleKeyUp ( e ) {
2016-12-12 09:39:25 +11:00
if ( e . keyCode === 13 && ( e . ctrlKey || ( e . metaKey && 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-09-22 08:09:21 +10:00
componentDidUpdate ( prevProps ) {
if ( prevProps . text !== this . props . text || prevProps . in _reply _to !== this . props . in _reply _to ) {
2016-11-13 00:33:21 +11:00
const textarea = this . autosuggest . input ;
2016-10-31 04:13:05 +11:00
if ( textarea ) {
textarea . focus ( ) ;
}
}
} ,
onSuggestionsClearRequested ( ) {
this . props . onClearSuggestions ( ) ;
} ,
2016-11-13 23:13:36 +11:00
@ debounce ( 500 )
2016-10-31 04:13:05 +11:00
onSuggestionsFetchRequested ( { value } ) {
2016-11-13 00:33:21 +11:00
const textarea = this . autosuggest . input ;
2016-10-31 04:13:05 +11:00
if ( textarea ) {
const token = getTokenForSuggestions ( value , textarea . selectionStart ) ;
if ( token !== null ) {
this . props . onFetchSuggestions ( token ) ;
2016-11-13 00:33:21 +11:00
} else {
this . props . onClearSuggestions ( ) ;
2016-10-31 04:13:05 +11:00
}
}
} ,
2016-11-13 00:33:21 +11:00
onSuggestionSelected ( e , { suggestionValue } ) {
const textarea = this . autosuggest . input ;
2016-10-31 04:13:05 +11:00
if ( textarea ) {
2016-11-13 00:33:21 +11:00
this . props . onSuggestionSelected ( textarea . selectionStart , suggestionValue ) ;
2016-09-22 08:09:21 +10:00
}
} ,
2016-11-13 00:33:21 +11:00
setRef ( c ) {
this . autosuggest = c ;
} ,
2016-11-24 04:53:23 +11:00
handleChangeSensitivity ( e ) {
this . props . onChangeSensitivity ( e . target . checked ) ;
} ,
2016-12-01 07:32:11 +11:00
handleChangeVisibility ( e ) {
this . props . onChangeVisibility ( e . target . checked ) ;
} ,
2016-08-26 03:52:55 +10:00
render ( ) {
2016-11-17 03:20:52 +11:00
const { intl } = this . props ;
2016-09-28 01:02:30 +10:00
let replyArea = '' ;
const disabled = this . props . is _submitting || this . props . is _uploading ;
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 } / > ;
}
2016-10-31 04:13:05 +11:00
const inputProps = {
2016-11-19 01:36:16 +11:00
placeholder : intl . formatMessage ( messages . placeholder ) ,
2016-10-31 04:13:05 +11:00
value : this . props . text ,
onKeyUp : this . handleKeyUp ,
onChange : this . handleChange ,
disabled : disabled
} ;
2016-08-26 03:52:55 +10:00
return (
2016-09-08 02:17:15 +10:00
< div style = { { padding : '10px' } } >
2016-09-01 06:58:10 +10:00
{ replyArea }
2016-10-31 04:13:05 +11:00
< Autosuggest
2016-11-13 00:33:21 +11:00
ref = { this . setRef }
2016-10-31 04:13:05 +11:00
suggestions = { this . props . suggestions }
2016-11-13 00:33:21 +11:00
focusFirstSuggestion = { true }
2016-10-31 04:13:05 +11:00
onSuggestionsFetchRequested = { this . onSuggestionsFetchRequested }
onSuggestionsClearRequested = { this . onSuggestionsClearRequested }
onSuggestionSelected = { this . onSuggestionSelected }
getSuggestionValue = { getSuggestionValue }
renderSuggestion = { renderSuggestion }
renderInputComponent = { renderInputComponent }
inputProps = { inputProps }
/ >
2016-08-26 03:52:55 +10:00
< div style = { { marginTop : '10px' , overflow : 'hidden' } } >
2016-11-19 01:36:16 +11:00
< div style = { { float : 'right' } } > < Button text = { intl . formatMessage ( messages . publish ) } onClick = { this . handleSubmit } disabled = { disabled } / > < / div >
2016-09-25 23:26:56 +10:00
< div style = { { float : 'right' , marginRight : '16px' , lineHeight : '36px' } } > < CharacterCounter max = { 500 } text = { this . props . text } / > < / 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
2016-12-01 07:32:11 +11:00
< label style = { { display : 'block' , lineHeight : '24px' , verticalAlign : 'middle' , marginTop : '10px' , borderTop : '1px solid #282c37' , paddingTop : '10px' } } >
< Toggle checked = { this . props . unlisted } onChange = { this . handleChangeVisibility } / >
2016-12-02 04:19:58 +11:00
< span style = { { display : 'inline-block' , verticalAlign : 'middle' , marginBottom : '14px' , marginLeft : '8px' , color : '#9baec8' } } > < FormattedMessage id = 'compose_form.unlisted' defaultMessage = 'Do not show on public timeline' / > < / span >
2016-12-01 07:32:11 +11:00
< / label >
< label style = { { display : 'block' , lineHeight : '24px' , verticalAlign : 'middle' } } >
2016-11-24 04:53:23 +11:00
< Toggle checked = { this . props . sensitive } onChange = { this . handleChangeSensitivity } / >
2016-11-24 08:57:57 +11:00
< span style = { { display : 'inline-block' , verticalAlign : 'middle' , marginBottom : '14px' , marginLeft : '8px' , color : '#9baec8' } } > < FormattedMessage id = 'compose_form.sensitive' defaultMessage = 'Mark content as sensitive' / > < / span >
2016-11-24 04:53:23 +11:00
< / label >
2016-08-26 03:52:55 +10:00
< / div >
) ;
}
} ) ;
2016-11-17 03:20:52 +11:00
export default injectIntl ( ComposeForm ) ;