diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 576f45a3bc..5f9014db7a 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -42,14 +42,159 @@ const formatButtonList = [
_td("numbered-list"),
];
+function ComposerAvatar(props) {
+ const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
+ return
+
+
;
+}
+
+ComposerAvatar.propTypes = {
+ me: PropTypes.object.isRequired,
+}
+
+function CallButton(props) {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ const onVoiceCallClick = (ev) => {
+ dis.dispatch({
+ action: 'place_call',
+ type: "voice",
+ room_id: props.roomId,
+ });
+ };
+
+ return
+}
+
+CallButton.propTypes = {
+ roomId: PropTypes.string.isRequired
+}
+
+function VideoCallButton(props) {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ const onCallClick = (ev) => {
+ dis.dispatch({
+ action: 'place_call',
+ type: ev.shiftKey ? "screensharing" : "video",
+ room_id: props.roomId,
+ });
+ };
+
+ return ;
+}
+
+VideoCallButton.propTypes = {
+ roomId: PropTypes.string.isRequired,
+};
+
+function HangupButton(props) {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ const onHangupClick = () => {
+ const call = CallHandler.getCallForRoom(props.roomId);
+ if (!call) {
+ return;
+ }
+ dis.dispatch({
+ action: 'hangup',
+ // hangup the call for this room, which may not be the room in props
+ // (e.g. conferences which will hangup the 1:1 room instead)
+ room_id: call.roomId,
+ });
+ };
+ return ;
+}
+
+HangupButton.propTypes = {
+ roomId: PropTypes.string.isRequired,
+}
+
+function FormattingButton(props) {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ return ;
+}
+
+FormattingButton.propTypes = {
+ showFormatting: PropTypes.bool.isRequired,
+ onClickHandler: PropTypes.func.isRequired,
+}
+
+class UploadButton extends React.Component {
+ static propTypes = {
+ roomId: PropTypes.string.isRequired,
+ }
+ constructor(props, context) {
+ super(props, context);
+ this.onUploadClick = this.onUploadClick.bind(this);
+ this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
+ }
+
+ onUploadClick(ev) {
+ if (MatrixClientPeg.get().isGuest()) {
+ dis.dispatch({action: 'require_registration'});
+ return;
+ }
+ this.refs.uploadInput.click();
+ }
+
+ onUploadFileInputChange(ev) {
+ if (ev.target.files.length === 0) return;
+
+ // take a copy so we can safely reset the value of the form control
+ // (Note it is a FileList: we can't use slice or sesnible iteration).
+ const tfiles = [];
+ for (let i = 0; i < ev.target.files.length; ++i) {
+ tfiles.push(ev.target.files[i]);
+ }
+
+ ContentMessages.sharedInstance().sendContentListToRoom(
+ tfiles, this.props.roomId, MatrixClientPeg.get(),
+ );
+
+ // This is the onChange handler for a file form control, but we're
+ // not keeping any state, so reset the value of the form control
+ // to empty.
+ // NB. we need to set 'value': the 'files' property is immutable.
+ ev.target.value = '';
+ }
+
+ render() {
+ const uploadInputStyle = {display: 'none'};
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ return (
+
+
+
+ );
+ }
+}
+
export default class MessageComposer extends React.Component {
constructor(props, context) {
super(props, context);
- this.onCallClick = this.onCallClick.bind(this);
- this.onHangupClick = this.onHangupClick.bind(this);
- this.onUploadClick = this.onUploadClick.bind(this);
- this._onUploadFileInputChange = this._onUploadFileInputChange.bind(this);
- this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
@@ -58,6 +203,8 @@ export default class MessageComposer extends React.Component {
this._onRoomStateEvents = this._onRoomStateEvents.bind(this);
this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
this._onTombstoneClick = this._onTombstoneClick.bind(this);
+ this.renderPlaceholderText = this.renderPlaceholderText.bind(this);
+ this.renderFormatBar = this.renderFormatBar.bind(this);
this.state = {
inputState: {
@@ -136,65 +283,6 @@ export default class MessageComposer extends React.Component {
this.setState({ isQuoting });
}
- onUploadClick(ev) {
- if (MatrixClientPeg.get().isGuest()) {
- dis.dispatch({action: 'require_registration'});
- return;
- }
-
- this.refs.uploadInput.click();
- }
-
- _onUploadFileInputChange(ev) {
- if (ev.target.files.length === 0) return;
-
- // take a copy so we can safely reset the value of the form control
- // (Note it is a FileList: we can't use slice or sesnible iteration).
- const tfiles = [];
- for (let i = 0; i < ev.target.files.length; ++i) {
- tfiles.push(ev.target.files[i]);
- }
-
- ContentMessages.sharedInstance().sendContentListToRoom(
- tfiles, this.props.room.roomId, MatrixClientPeg.get(),
- );
-
- // This is the onChange handler for a file form control, but we're
- // not keeping any state, so reset the value of the form control
- // to empty.
- // NB. we need to set 'value': the 'files' property is immutable.
- ev.target.value = '';
- }
-
- onHangupClick() {
- const call = CallHandler.getCallForRoom(this.props.room.roomId);
- //var call = CallHandler.getAnyActiveCall();
- if (!call) {
- return;
- }
- dis.dispatch({
- action: 'hangup',
- // hangup the call for this room, which may not be the room in props
- // (e.g. conferences which will hangup the 1:1 room instead)
- room_id: call.roomId,
- });
- }
-
- onCallClick(ev) {
- dis.dispatch({
- action: 'place_call',
- type: ev.shiftKey ? "screensharing" : "video",
- room_id: this.props.room.roomId,
- });
- }
-
- onVoiceCallClick(ev) {
- dis.dispatch({
- action: 'place_call',
- type: "voice",
- room_id: this.props.room.roomId,
- });
- }
onInputStateChanged(inputState) {
// Merge the new input state with old to support partial updates
@@ -245,119 +333,94 @@ export default class MessageComposer extends React.Component {
});
}
- render() {
- const uploadInputStyle = {display: 'none'};
- const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
- const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
-
- const controls = [];
-
- if (this.state.me) {
- controls.push(
-
-
-
,
- );
- }
-
- if (this.props.e2eStatus) {
- controls.push(
- );
- }
-
- let callButton;
- let videoCallButton;
- let hangupButton;
-
- const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
- // Call buttons
- if (this.props.callState && this.props.callState !== 'ended') {
- hangupButton =
-
- ;
+ renderPlaceholderText() {
+ const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
+ if (this.state.isQuoting) {
+ if (roomIsEncrypted) {
+ return _t('Send an encrypted reply…');
+ } else {
+ return _t('Send a reply (unencrypted)…');
+ }
} else {
- callButton =
-
- ;
- videoCallButton =
-
- ;
+ if (roomIsEncrypted) {
+ return _t('Send an encrypted message…');
+ } else {
+ return _t('Send a message (unencrypted)…');
+ }
}
+ }
+
+ renderFormatBar() {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ const {marks, blockType} = this.state.inputState;
+ const formatButtons = formatButtonList.map((name) => {
+ // special-case to match the md serializer and the special-case in MessageComposerInput.js
+ const markName = name === 'inline-code' ? 'code' : name;
+ const active = marks.some(mark => mark.type === markName) || blockType === name;
+ const suffix = active ? '-on' : '';
+ const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
+ const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
+ return (
+
+ );
+ })
+
+ return (
+
+
+ { formatButtons }
+
+
+
+
+
+ );
+ }
+
+ render() {
+ const controls = [
+ this.state.me ? : null,
+ this.props.e2eStatus ? : null,
+ ];
if (!this.state.tombstone && this.state.canSendMessages) {
// This also currently includes the call buttons. Really we should
// check separately for whether we can call, but this is slightly
// complex because of conference calls.
- const uploadButton = (
-
-
-
- );
- const formattingButton = this.state.inputState.isRichTextEnabled ? (
-
- ) : null;
-
- const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
- let placeholderText;
- if (this.state.isQuoting) {
- if (roomIsEncrypted) {
- placeholderText = _t('Send an encrypted reply…');
- } else {
- placeholderText = _t('Send a reply (unencrypted)…');
- }
- } else {
- if (roomIsEncrypted) {
- placeholderText = _t('Send an encrypted message…');
- } else {
- placeholderText = _t('Send a message (unencrypted)…');
- }
- }
-
- const stickerpickerButton = ;
+ const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
+ const showFormattingButton = this.state.inputState.isRichTextEnabled;
+ const callInProgress = this.props.callState && this.props.callState !== 'ended';
controls.push(
this.messageComposerInput = c}
key="controls_input"
room={this.props.room}
- placeholder={placeholderText}
+ placeholder={this.renderPlaceholderText()}
onInputStateChanged={this.onInputStateChanged}
permalinkCreator={this.props.permalinkCreator} />,
- formattingButton,
- stickerpickerButton,
- uploadButton,
- hangupButton,
- callButton,
- videoCallButton,
+ showFormattingButton ? : null,
+ ,
+ ,
+ callInProgress ? : null,
+ callInProgress ? null : ,
+ callInProgress ? null : ,
);
} else if (this.state.tombstone) {
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
@@ -388,42 +451,7 @@ export default class MessageComposer extends React.Component {
);
}
- let formatBar;
- if (this.state.showFormatting && this.state.inputState.isRichTextEnabled) {
- const {marks, blockType} = this.state.inputState;
- const formatButtons = formatButtonList.map((name) => {
- // special-case to match the md serializer and the special-case in MessageComposerInput.js
- const markName = name === 'inline-code' ? 'code' : name;
- const active = marks.some(mark => mark.type === markName) || blockType === name;
- const suffix = active ? '-on' : '';
- const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
- const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
- return ;
- },
- );
-
- formatBar =
-
-
- { formatButtons }
-
-
-
-
-
;
- }
+ const showFormatBar = this.state.showFormatting && this.state.inputState.isRichTextEnabled;
const wrapperClasses = classNames({
mx_MessageComposer_wrapper: true,
@@ -436,7 +464,7 @@ export default class MessageComposer extends React.Component {
{ controls }
- { formatBar }
+ { showFormatBar ? this.renderFormatBar() : null }
);
}