@@ -27,6 +27,9 @@ marked.setOptions({
2727 smartypants : false
2828} ) ;
2929
30+ import { Editor , EditorState , RichUtils } from 'draft-js' ;
31+ import { stateToHTML } from 'draft-js-export-html' ;
32+
3033var MatrixClientPeg = require ( "../../../MatrixClientPeg" ) ;
3134var SlashCommands = require ( "../../../SlashCommands" ) ;
3235var Modal = require ( "../../../Modal" ) ;
@@ -36,6 +39,8 @@ var sdk = require('../../../index');
3639var dis = require ( "../../../dispatcher" ) ;
3740var KeyCode = require ( "../../../KeyCode" ) ;
3841
42+ import { contentStateToHTML } from '../../../RichText' ;
43+
3944var TYPING_USER_TIMEOUT = 10000 ;
4045var TYPING_SERVER_TIMEOUT = 30000 ;
4146var MARKDOWN_ENABLED = true ;
@@ -56,26 +61,18 @@ function mdownToHtml(mdown) {
5661/*
5762 * The textInput part of the MessageComposer
5863 */
59- module . exports = React . createClass ( {
60- displayName : 'MessageComposerInput' ,
61-
62- statics : {
63- // the height we limit the composer to
64- MAX_HEIGHT : 100 ,
65- } ,
66-
67- propTypes : {
68- tabComplete : React . PropTypes . any ,
69-
70- // a callback which is called when the height of the composer is
71- // changed due to a change in content.
72- onResize : React . PropTypes . func ,
73-
74- // js-sdk Room object
75- room : React . PropTypes . object . isRequired ,
76- } ,
64+ module . exports = class extends React . Component {
65+ constructor ( props , context ) {
66+ super ( props , context ) ;
67+ this . onAction = this . onAction . bind ( this ) ;
68+ this . onInputClick = this . onInputClick . bind ( this ) ;
69+
70+ this . state = {
71+ editorState : EditorState . createEmpty ( )
72+ } ;
73+ }
7774
78- componentWillMount : function ( ) {
75+ componentWillMount ( ) {
7976 this . oldScrollHeight = 0 ;
8077 this . markdownEnabled = MARKDOWN_ENABLED ;
8178 var self = this ;
@@ -157,21 +154,22 @@ module.exports = React.createClass({
157154 // save the currently entered text in order to restore it later.
158155 // NB: This isn't 'originalText' because we want to restore
159156 // sent history items too!
160- var text = this . element . value ;
161- window . sessionStorage . setItem ( "input_" + this . roomId , text ) ;
157+ console . error ( 'fixme' ) ;
158+ // window.sessionStorage.setItem("input_" + this.roomId, text);
162159 } ,
163160
164161 setLastTextEntry : function ( ) {
165- var text = window . sessionStorage . getItem ( "input_" + this . roomId ) ;
166- if ( text ) {
167- this . element . value = text ;
168- self . resizeInput ( ) ;
169- }
162+ console . error ( 'fixme' ) ;
163+ // var text = window.sessionStorage.getItem("input_" + this.roomId);
164+ // if (text) {
165+ // this.element.value = text;
166+ // self.resizeInput();
167+ // }
170168 }
171169 } ;
172- } ,
170+ }
173171
174- componentDidMount : function ( ) {
172+ componentDidMount ( ) {
175173 this . dispatcherRef = dis . register ( this . onAction ) ;
176174 this . sentHistory . init (
177175 this . refs . textarea ,
@@ -181,18 +179,19 @@ module.exports = React.createClass({
181179 if ( this . props . tabComplete ) {
182180 this . props . tabComplete . setTextArea ( this . refs . textarea ) ;
183181 }
184- } ,
182+ }
185183
186- componentWillUnmount : function ( ) {
184+ componentWillUnmount ( ) {
187185 dis . unregister ( this . dispatcherRef ) ;
188186 this . sentHistory . saveLastTextEntry ( ) ;
189- } ,
187+ }
190188
191- onAction : function ( payload ) {
192- var textarea = this . refs . textarea ;
189+ onAction ( payload ) {
190+ var editor = this . refs . editor ;
193191 switch ( payload . action ) {
194192 case 'focus_composer' :
195- textarea . focus ( ) ;
193+ console . error ( 'fixme' ) ;
194+ editor . focus ( ) ;
196195 break ;
197196 case 'insert_displayname' :
198197 if ( textarea . value . length ) {
@@ -214,9 +213,9 @@ module.exports = React.createClass({
214213 }
215214 break ;
216215 }
217- } ,
216+ }
218217
219- onKeyDown : function ( ev ) {
218+ onKeyDown ( ev ) {
220219 if ( ev . keyCode === KeyCode . ENTER && ! ev . shiftKey ) {
221220 var input = this . refs . textarea . value ;
222221 if ( input . length === 0 ) {
@@ -252,33 +251,34 @@ module.exports = React.createClass({
252251 self . onFinishedTyping ( ) ;
253252 }
254253 } , 10 ) ; // XXX: what is this 10ms setTimeout doing? Looks hacky :(
255- } ,
254+ }
256255
257- resizeInput : function ( ) {
256+ resizeInput ( ) {
257+ console . error ( 'fixme' ) ;
258258 // scrollHeight is at least equal to clientHeight, so we have to
259259 // temporarily crimp clientHeight to 0 to get an accurate scrollHeight value
260- this . refs . textarea . style . height = "20px" ; // 20 hardcoded from CSS
261- var newHeight = Math . min ( this . refs . textarea . scrollHeight ,
262- this . constructor . MAX_HEIGHT ) ;
263- this . refs . textarea . style . height = Math . ceil ( newHeight ) + "px" ;
264- this . oldScrollHeight = this . refs . textarea . scrollHeight ;
265-
266- if ( this . props . onResize ) {
267- // kick gemini-scrollbar to re-layout
268- this . props . onResize ( ) ;
269- }
270- } ,
260+ // this.refs.textarea.style.height = "20px"; // 20 hardcoded from CSS
261+ // var newHeight = Math.min(this.refs.textarea.scrollHeight,
262+ // this.constructor.MAX_HEIGHT);
263+ // this.refs.textarea.style.height = Math.ceil(newHeight) + "px";
264+ // this.oldScrollHeight = this.refs.textarea.scrollHeight;
265+ //
266+ // if (this.props.onResize) {
267+ // // kick gemini-scrollbar to re-layout
268+ // this.props.onResize();
269+ // }
270+ }
271271
272- onKeyUp : function ( ev ) {
272+ onKeyUp ( ev ) {
273273 if ( this . refs . textarea . scrollHeight !== this . oldScrollHeight ||
274274 ev . keyCode === KeyCode . DELETE ||
275275 ev . keyCode === KeyCode . BACKSPACE )
276276 {
277277 this . resizeInput ( ) ;
278278 }
279- } ,
279+ }
280280
281- onEnter : function ( ev ) {
281+ onEnter ( ev ) {
282282 var contentText = this . refs . textarea . value ;
283283
284284 // bodge for now to set markdown state on/off. We probably want a separate
@@ -365,42 +365,42 @@ module.exports = React.createClass({
365365 this . refs . textarea . value = '' ;
366366 this . resizeInput ( ) ;
367367 ev . preventDefault ( ) ;
368- } ,
368+ }
369369
370- onTypingActivity : function ( ) {
370+ onTypingActivity ( ) {
371371 this . isTyping = true ;
372372 if ( ! this . userTypingTimer ) {
373373 this . sendTyping ( true ) ;
374374 }
375375 this . startUserTypingTimer ( ) ;
376376 this . startServerTypingTimer ( ) ;
377- } ,
377+ }
378378
379- onFinishedTyping : function ( ) {
379+ onFinishedTyping ( ) {
380380 this . isTyping = false ;
381381 this . sendTyping ( false ) ;
382382 this . stopUserTypingTimer ( ) ;
383383 this . stopServerTypingTimer ( ) ;
384- } ,
384+ }
385385
386- startUserTypingTimer : function ( ) {
386+ startUserTypingTimer ( ) {
387387 this . stopUserTypingTimer ( ) ;
388388 var self = this ;
389389 this . userTypingTimer = setTimeout ( function ( ) {
390390 self . isTyping = false ;
391391 self . sendTyping ( self . isTyping ) ;
392392 self . userTypingTimer = null ;
393393 } , TYPING_USER_TIMEOUT ) ;
394- } ,
394+ }
395395
396- stopUserTypingTimer : function ( ) {
396+ stopUserTypingTimer ( ) {
397397 if ( this . userTypingTimer ) {
398398 clearTimeout ( this . userTypingTimer ) ;
399399 this . userTypingTimer = null ;
400400 }
401- } ,
401+ }
402402
403- startServerTypingTimer : function ( ) {
403+ startServerTypingTimer ( ) {
404404 if ( ! this . serverTypingTimer ) {
405405 var self = this ;
406406 this . serverTypingTimer = setTimeout ( function ( ) {
@@ -410,39 +410,90 @@ module.exports = React.createClass({
410410 }
411411 } , TYPING_SERVER_TIMEOUT / 2 ) ;
412412 }
413- } ,
413+ }
414414
415- stopServerTypingTimer : function ( ) {
415+ stopServerTypingTimer ( ) {
416416 if ( this . serverTypingTimer ) {
417417 clearTimeout ( this . servrTypingTimer ) ;
418418 this . serverTypingTimer = null ;
419419 }
420- } ,
420+ }
421421
422- sendTyping : function ( isTyping ) {
422+ sendTyping ( isTyping ) {
423423 MatrixClientPeg . get ( ) . sendTyping (
424424 this . props . room . roomId ,
425425 this . isTyping , TYPING_SERVER_TIMEOUT
426426 ) . done ( ) ;
427- } ,
427+ }
428428
429- refreshTyping : function ( ) {
429+ refreshTyping ( ) {
430430 if ( this . typingTimeout ) {
431431 clearTimeout ( this . typingTimeout ) ;
432432 this . typingTimeout = null ;
433433 }
434- } ,
434+ }
435+
436+ onInputClick ( ev ) {
437+ this . refs . editor . focus ( ) ;
438+ }
439+
440+ onChange ( editorState ) {
441+ this . setState ( { editorState} ) ;
442+ }
443+
444+ handleKeyCommand ( command ) {
445+ const newState = RichUtils . handleKeyCommand ( this . state . editorState , command ) ;
446+ if ( newState ) {
447+ this . onChange ( newState ) ;
448+ return true ;
449+ }
450+ return false ;
451+ }
452+
453+ handleReturn ( ev ) {
454+ if ( ev . shiftKey )
455+ return false ;
435456
436- onInputClick : function ( ev ) {
437- this . refs . textarea . focus ( ) ;
438- } ,
457+ const contentState = this . state . editorState . getCurrentContent ( ) ;
458+ if ( ! contentState . hasText ( ) )
459+ return false ;
439460
440- render : function ( ) {
461+ const contentText = contentState . getPlainText ( ) ,
462+ contentHTML = contentStateToHTML ( contentState ) ;
463+
464+ MatrixClientPeg . get ( ) . sendHtmlMessage ( this . props . room . roomId , contentText , contentHTML ) ;
465+
466+ this . setState ( {
467+ editorState : EditorState . createEmpty ( )
468+ } ) ;
469+
470+ return true ;
471+ }
472+
473+ render ( ) {
441474 return (
442- < div className = "mx_MessageComposer_input" onClick = { this . onInputClick } >
443- < textarea autoFocus ref = "textarea" rows = "1" onKeyDown = { this . onKeyDown } onKeyUp = { this . onKeyUp } placeholder = "Type a message..." />
475+ < div className = "mx_MessageComposer_input" onClick = { this . onInputClick } style = { { overflow : 'auto' } } >
476+ < Editor ref = "editor"
477+ editorState = { this . state . editorState }
478+ onChange = { ( state ) => this . onChange ( state ) }
479+ handleKeyCommand = { ( command ) => this . handleKeyCommand ( command ) }
480+ handleReturn = { ev => this . handleReturn ( ev ) } />
444481 </ div >
445482 ) ;
446483 }
447- } ) ;
484+ } ;
485+
486+ module . exports . propTypes = {
487+ tabComplete : React . PropTypes . any ,
488+
489+ // a callback which is called when the height of the composer is
490+ // changed due to a change in content.
491+ onResize : React . PropTypes . func ,
492+
493+ // js-sdk Room object
494+ room : React . PropTypes . object . isRequired
495+ } ;
496+
497+ // the height we limit the composer to
498+ module . exports . MAX_HEIGHT = 100 ;
448499
0 commit comments