{controls}
@@ -206,5 +238,24 @@ module.exports = React.createClass({
);
}
-});
+};
+MessageComposer.propTypes = {
+ tabComplete: React.PropTypes.any,
+
+ // a callback which is called when the height of the composer is
+ // changed due to a change in content.
+ onResize: React.PropTypes.func,
+
+ // js-sdk Room object
+ room: React.PropTypes.object.isRequired,
+
+ // string representing the current voip call state
+ callState: React.PropTypes.string,
+
+ // callback when a file to upload is chosen
+ uploadFile: React.PropTypes.func.isRequired,
+
+ // opacity for dynamic UI fading effects
+ opacity: React.PropTypes.number
+};
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index 8a0ee7d8a8..46abc20ed6 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -72,7 +72,11 @@ export default class MessageComposerInput extends React.Component {
this.onInputClick = this.onInputClick.bind(this);
this.handleReturn = this.handleReturn.bind(this);
this.handleKeyCommand = this.handleKeyCommand.bind(this);
- this.onChange = this.onChange.bind(this);
+ this.setEditorState = this.setEditorState.bind(this);
+ this.onUpArrow = this.onUpArrow.bind(this);
+ this.onDownArrow = this.onDownArrow.bind(this);
+ this.onTab = this.onTab.bind(this);
+ this.onConfirmAutocompletion = this.onConfirmAutocompletion.bind(this);
let isRichtextEnabled = window.localStorage.getItem('mx_editor_rte_enabled');
if(isRichtextEnabled == null) {
@@ -82,7 +86,7 @@ export default class MessageComposerInput extends React.Component {
this.state = {
isRichtextEnabled: isRichtextEnabled,
- editorState: null
+ editorState: null,
};
// bit of a hack, but we need to do this here since createEditorState needs isRichtextEnabled
@@ -93,7 +97,7 @@ export default class MessageComposerInput extends React.Component {
static getKeyBinding(e: SyntheticKeyboardEvent): string {
// C-m => Toggles between rich text and markdown modes
- if(e.keyCode == KEY_M && KeyBindingUtil.isCtrlKeyCommand(e)) {
+ if (e.keyCode === KEY_M && KeyBindingUtil.isCtrlKeyCommand(e)) {
return 'toggle-mode';
}
@@ -207,11 +211,9 @@ export default class MessageComposerInput extends React.Component {
let contentJSON = window.sessionStorage.getItem("mx_messagecomposer_input_" + this.roomId);
if (contentJSON) {
let content = convertFromRaw(JSON.parse(contentJSON));
- component.setState({
- editorState: component.createEditorState(component.state.isRichtextEnabled, content)
- });
+ component.setEditorState(component.createEditorState(component.state.isRichtextEnabled, content));
}
- }
+ },
};
}
@@ -233,7 +235,7 @@ export default class MessageComposerInput extends React.Component {
}
onAction(payload) {
- var editor = this.refs.editor;
+ let editor = this.refs.editor;
switch (payload.action) {
case 'focus_composer':
@@ -251,7 +253,7 @@ export default class MessageComposerInput extends React.Component {
payload.displayname
);
this.setState({
- editorState: EditorState.push(this.state.editorState, contentState, 'insert-characters')
+ editorState: EditorState.push(this.state.editorState, contentState, 'insert-characters'),
});
editor.focus();
}
@@ -344,7 +346,7 @@ export default class MessageComposerInput extends React.Component {
this.refs.editor.focus();
}
- onChange(editorState: EditorState) {
+ setEditorState(editorState: EditorState) {
this.setState({editorState});
if(editorState.getCurrentContent().hasText()) {
@@ -352,20 +354,22 @@ export default class MessageComposerInput extends React.Component {
} else {
this.onFinishedTyping();
}
+
+ if(this.props.onContentChanged) {
+ this.props.onContentChanged(editorState.getCurrentContent().getPlainText(),
+ RichText.selectionStateToTextOffsets(editorState.getSelection(),
+ editorState.getCurrentContent().getBlocksAsArray()));
+ }
}
enableRichtext(enabled: boolean) {
if (enabled) {
let html = mdownToHtml(this.state.editorState.getCurrentContent().getPlainText());
- this.setState({
- editorState: this.createEditorState(enabled, RichText.HTMLtoContentState(html))
- });
+ this.setEditorState(this.createEditorState(enabled, RichText.HTMLtoContentState(html)));
} else {
let markdown = stateToMarkdown(this.state.editorState.getCurrentContent()),
contentState = ContentState.createFromText(markdown);
- this.setState({
- editorState: this.createEditorState(enabled, contentState)
- });
+ this.setEditorState(this.createEditorState(enabled, contentState));
}
window.localStorage.setItem('mx_editor_rte_enabled', enabled);
@@ -408,19 +412,28 @@ export default class MessageComposerInput extends React.Component {
newState = RichUtils.handleKeyCommand(this.state.editorState, command);
if (newState != null) {
- this.onChange(newState);
+ this.setEditorState(newState);
return true;
}
return false;
}
handleReturn(ev) {
- if(ev.shiftKey)
+ if (ev.shiftKey) {
return false;
+ }
+
+ if(this.props.tryComplete) {
+ if(this.props.tryComplete()) {
+ return true;
+ }
+ }
const contentState = this.state.editorState.getCurrentContent();
- if(!contentState.hasText())
+ if (!contentState.hasText()) {
return true;
+ }
+
let contentText = contentState.getPlainText(), contentHTML;
@@ -489,10 +502,49 @@ export default class MessageComposerInput extends React.Component {
return true;
}
+ onUpArrow(e) {
+ if(this.props.onUpArrow) {
+ if(this.props.onUpArrow()) {
+ e.preventDefault();
+ }
+ }
+ }
+
+ onDownArrow(e) {
+ if(this.props.onDownArrow) {
+ if(this.props.onDownArrow()) {
+ e.preventDefault();
+ }
+ }
+ }
+
+ onTab(e) {
+ if (this.props.onTab) {
+ if (this.props.onTab()) {
+ e.preventDefault();
+ }
+ }
+ }
+
+ onConfirmAutocompletion(range, content: string) {
+ let contentState = Modifier.replaceText(
+ this.state.editorState.getCurrentContent(),
+ RichText.textOffsetsToSelectionState(range, this.state.editorState.getCurrentContent().getBlocksAsArray()),
+ content
+ );
+
+ this.setState({
+ editorState: EditorState.push(this.state.editorState, contentState, 'insert-characters'),
+ });
+
+ // for some reason, doing this right away does not update the editor :(
+ setTimeout(() => this.refs.editor.focus(), 50);
+ }
+
render() {
let className = "mx_MessageComposer_input";
- if(this.state.isRichtextEnabled) {
+ if (this.state.isRichtextEnabled) {
className += " mx_MessageComposer_input_rte"; // placeholder indicator for RTE mode
}
@@ -502,11 +554,14 @@ export default class MessageComposerInput extends React.Component {
);
@@ -521,5 +576,14 @@ MessageComposerInput.propTypes = {
onResize: React.PropTypes.func,
// js-sdk Room object
- room: React.PropTypes.object.isRequired
+ room: React.PropTypes.object.isRequired,
+
+ // called with current plaintext content (as a string) whenever it changes
+ onContentChanged: React.PropTypes.func,
+
+ onUpArrow: React.PropTypes.func,
+
+ onDownArrow: React.PropTypes.func,
+
+ onTab: React.PropTypes.func
};