2015-06-23 18:41:25 +03:00
|
|
|
/*
|
2016-01-07 07:06:39 +03:00
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2015-06-23 18:41:25 +03:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
2017-06-12 16:50:25 +03:00
|
|
|
import React from 'react';
|
2017-05-25 13:39:08 +03:00
|
|
|
import { _t } from '../../../languageHandler';
|
2017-06-12 16:50:25 +03:00
|
|
|
import CallHandler from '../../../CallHandler';
|
|
|
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
|
|
|
import Modal from '../../../Modal';
|
|
|
|
import sdk from '../../../index';
|
|
|
|
import dis from '../../../dispatcher';
|
2016-06-01 14:24:21 +03:00
|
|
|
import Autocomplete from './Autocomplete';
|
2016-06-13 21:40:43 +03:00
|
|
|
import UserSettingsStore from '../../../UserSettingsStore';
|
|
|
|
|
2015-06-15 20:35:28 +03:00
|
|
|
|
2016-06-20 11:22:55 +03:00
|
|
|
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);
|
2017-05-17 12:58:59 +03:00
|
|
|
this.onShowAppsClick = this.onShowAppsClick.bind(this);
|
|
|
|
this.onHideAppsClick = this.onHideAppsClick.bind(this);
|
2016-06-20 11:22:55 +03:00
|
|
|
this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
|
2017-05-17 03:41:42 +03:00
|
|
|
this.uploadFiles = this.uploadFiles.bind(this);
|
2016-06-20 11:22:55 +03:00
|
|
|
this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
|
|
|
|
this.onInputContentChanged = this.onInputContentChanged.bind(this);
|
2016-08-02 13:00:00 +03:00
|
|
|
this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
|
2016-09-05 15:08:53 +03:00
|
|
|
this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
|
|
|
|
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
|
|
|
|
this.onInputStateChanged = this.onInputStateChanged.bind(this);
|
2016-09-15 21:25:53 +03:00
|
|
|
this.onEvent = this.onEvent.bind(this);
|
2016-06-20 11:22:55 +03:00
|
|
|
|
|
|
|
this.state = {
|
2016-06-21 13:16:20 +03:00
|
|
|
autocompleteQuery: '',
|
2016-07-03 19:45:13 +03:00
|
|
|
selection: null,
|
2016-09-05 15:08:53 +03:00
|
|
|
inputState: {
|
2016-09-04 18:33:40 +03:00
|
|
|
style: [],
|
|
|
|
blockType: null,
|
2017-04-26 00:17:25 +03:00
|
|
|
isRichtextEnabled: UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', false),
|
2016-09-07 20:22:14 +03:00
|
|
|
wordCount: 0,
|
2016-09-04 18:33:40 +03:00
|
|
|
},
|
2016-09-07 20:22:14 +03:00
|
|
|
showFormatting: UserSettingsStore.getSyncedSetting('MessageComposer.showFormatting', false),
|
2016-06-01 14:24:21 +03:00
|
|
|
};
|
2016-06-20 11:22:55 +03:00
|
|
|
}
|
2016-06-01 14:24:21 +03:00
|
|
|
|
2016-09-15 21:25:53 +03:00
|
|
|
componentDidMount() {
|
|
|
|
// N.B. using 'event' rather than 'RoomEvents' otherwise the crypto handler
|
|
|
|
// for 'event' fires *after* 'RoomEvent', and our room won't have yet been
|
|
|
|
// marked as encrypted.
|
|
|
|
// XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
|
|
|
|
MatrixClientPeg.get().on("event", this.onEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
2016-09-29 18:57:10 +03:00
|
|
|
if (MatrixClientPeg.get()) {
|
|
|
|
MatrixClientPeg.get().removeListener("event", this.onEvent);
|
|
|
|
}
|
2016-09-15 21:25:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
onEvent(event) {
|
|
|
|
if (event.getType() !== 'm.room.encryption') return;
|
|
|
|
if (event.getRoomId() !== this.props.room.roomId) return;
|
|
|
|
this.forceUpdate();
|
|
|
|
}
|
|
|
|
|
2016-06-20 11:22:55 +03:00
|
|
|
onUploadClick(ev) {
|
2016-06-01 01:59:36 +03:00
|
|
|
if (MatrixClientPeg.get().isGuest()) {
|
2017-05-15 19:31:26 +03:00
|
|
|
dis.dispatch({action: 'view_set_mxid'});
|
2016-06-01 01:59:36 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-26 20:31:10 +03:00
|
|
|
this.refs.uploadInput.click();
|
2016-06-20 11:22:55 +03:00
|
|
|
}
|
2015-11-26 20:31:10 +03:00
|
|
|
|
2017-05-17 03:41:42 +03:00
|
|
|
onUploadFileSelected(files) {
|
|
|
|
this.uploadFiles(files.target.files);
|
|
|
|
}
|
2016-04-02 23:52:05 +03:00
|
|
|
|
2017-05-17 03:41:42 +03:00
|
|
|
uploadFiles(files) {
|
2016-07-03 19:45:13 +03:00
|
|
|
let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
|
|
|
let TintableSvg = sdk.getComponent("elements.TintableSvg");
|
2016-04-02 23:52:05 +03:00
|
|
|
|
2016-07-03 19:45:13 +03:00
|
|
|
let fileList = [];
|
|
|
|
for (let i=0; i<files.length; i++) {
|
2016-09-06 03:44:42 +03:00
|
|
|
fileList.push(<li key={i}>
|
2017-06-08 16:08:51 +03:00
|
|
|
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> {files[i].name || _t('Attachment')}
|
2016-04-02 23:52:05 +03:00
|
|
|
</li>);
|
2015-11-26 20:31:10 +03:00
|
|
|
}
|
2016-04-02 23:52:05 +03:00
|
|
|
|
|
|
|
Modal.createDialog(QuestionDialog, {
|
2017-05-23 17:16:31 +03:00
|
|
|
title: _t('Upload Files'),
|
2016-04-02 23:52:05 +03:00
|
|
|
description: (
|
|
|
|
<div>
|
2017-05-30 17:09:57 +03:00
|
|
|
<p>{ _t('Are you sure you want to upload the following files?') }</p>
|
2016-04-02 23:52:05 +03:00
|
|
|
<ul style={{listStyle: 'none', textAlign: 'left'}}>
|
|
|
|
{fileList}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
),
|
|
|
|
onFinished: (shouldUpload) => {
|
|
|
|
if(shouldUpload) {
|
|
|
|
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
|
|
|
|
if (files) {
|
2017-05-17 13:31:01 +03:00
|
|
|
for(let i=0; i<files.length; i++) {
|
2016-04-02 23:52:05 +03:00
|
|
|
this.props.uploadFile(files[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.refs.uploadInput.value = null;
|
2016-07-03 19:45:13 +03:00
|
|
|
},
|
2016-04-02 23:52:05 +03:00
|
|
|
});
|
2016-06-20 11:22:55 +03:00
|
|
|
}
|
2015-09-18 12:44:57 +03:00
|
|
|
|
2016-06-20 11:22:55 +03:00
|
|
|
onHangupClick() {
|
2017-05-17 13:31:01 +03:00
|
|
|
const call = CallHandler.getCallForRoom(this.props.room.roomId);
|
2015-12-17 05:49:09 +03:00
|
|
|
//var call = CallHandler.getAnyActiveCall();
|
2015-12-15 02:37:34 +03:00
|
|
|
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)
|
2016-07-03 19:45:13 +03:00
|
|
|
room_id: call.roomId,
|
2015-12-15 02:37:34 +03:00
|
|
|
});
|
2016-06-20 11:22:55 +03:00
|
|
|
}
|
2015-12-15 02:37:34 +03:00
|
|
|
|
2017-06-27 13:38:14 +03:00
|
|
|
// _startCallApp(isAudioConf) {
|
|
|
|
// dis.dispatch({
|
|
|
|
// action: 'appsDrawer',
|
|
|
|
// show: true,
|
|
|
|
// });
|
|
|
|
|
|
|
|
// const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', '');
|
|
|
|
// let appsStateEvent = {};
|
|
|
|
// if (appsStateEvents) {
|
|
|
|
// appsStateEvent = appsStateEvents.getContent();
|
|
|
|
// }
|
|
|
|
// if (!appsStateEvent.videoConf) {
|
|
|
|
// appsStateEvent.videoConf = {
|
|
|
|
// type: 'jitsi',
|
2017-06-27 13:39:20 +03:00
|
|
|
// // FIXME -- This should not be localhost
|
2017-06-27 13:38:14 +03:00
|
|
|
// url: 'http://localhost:8000/jitsi.html',
|
|
|
|
// data: {
|
|
|
|
// confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(),
|
|
|
|
// isAudioConf: isAudioConf,
|
|
|
|
// },
|
|
|
|
// };
|
|
|
|
// MatrixClientPeg.get().sendStateEvent(
|
|
|
|
// this.props.room.roomId,
|
|
|
|
// 'im.vector.modular.widgets',
|
|
|
|
// appsStateEvent,
|
|
|
|
// '',
|
|
|
|
// ).then(() => console.log('Sent state'), (e) => console.error(e));
|
|
|
|
// }
|
|
|
|
// }
|
2017-06-13 16:35:13 +03:00
|
|
|
|
|
|
|
onCallClick(ev) {
|
2017-06-27 13:38:14 +03:00
|
|
|
// NOTE -- Will be replaced by Jitsi code (currently commented)
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'place_call',
|
|
|
|
type: ev.shiftKey ? "screensharing" : "video",
|
|
|
|
room_id: this.props.room.roomId,
|
|
|
|
});
|
|
|
|
// this._startCallApp(false);
|
2016-06-20 11:22:55 +03:00
|
|
|
}
|
2015-11-26 20:31:10 +03:00
|
|
|
|
2016-06-20 11:22:55 +03:00
|
|
|
onVoiceCallClick(ev) {
|
2017-06-27 13:38:14 +03:00
|
|
|
// NOTE -- Will be replaced by Jitsi code (currently commented)
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'place_call',
|
|
|
|
type: "voice",
|
|
|
|
room_id: this.props.room.roomId,
|
|
|
|
});
|
|
|
|
// this._startCallApp(true);
|
2016-06-20 11:22:55 +03:00
|
|
|
}
|
2015-11-26 20:31:10 +03:00
|
|
|
|
2017-05-17 12:58:59 +03:00
|
|
|
onShowAppsClick(ev) {
|
2017-05-17 14:35:25 +03:00
|
|
|
dis.dispatch({
|
2017-05-17 23:15:57 +03:00
|
|
|
action: 'appsDrawer',
|
|
|
|
show: true,
|
2017-05-17 14:35:25 +03:00
|
|
|
});
|
2017-05-17 12:58:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
onHideAppsClick(ev) {
|
2017-05-17 14:35:25 +03:00
|
|
|
dis.dispatch({
|
2017-05-17 23:15:57 +03:00
|
|
|
action: 'appsDrawer',
|
|
|
|
show: false,
|
2017-05-17 14:35:25 +03:00
|
|
|
});
|
2017-05-17 12:58:59 +03:00
|
|
|
}
|
|
|
|
|
2016-06-21 13:16:20 +03:00
|
|
|
onInputContentChanged(content: string, selection: {start: number, end: number}) {
|
2016-06-01 14:24:21 +03:00
|
|
|
this.setState({
|
2016-06-21 13:16:20 +03:00
|
|
|
autocompleteQuery: content,
|
2016-07-03 19:45:13 +03:00
|
|
|
selection,
|
2016-06-20 11:22:55 +03:00
|
|
|
});
|
|
|
|
}
|
2016-06-01 14:24:21 +03:00
|
|
|
|
2016-09-05 15:08:53 +03:00
|
|
|
onInputStateChanged(inputState) {
|
|
|
|
this.setState({inputState});
|
|
|
|
}
|
|
|
|
|
2016-08-02 13:00:00 +03:00
|
|
|
_onAutocompleteConfirm(range, completion) {
|
|
|
|
if (this.messageComposerInput) {
|
2016-09-13 13:11:52 +03:00
|
|
|
this.messageComposerInput.setDisplayedCompletion(range, completion);
|
2016-08-02 13:00:00 +03:00
|
|
|
}
|
2016-06-21 16:03:39 +03:00
|
|
|
}
|
|
|
|
|
2016-09-07 20:22:14 +03:00
|
|
|
onFormatButtonClicked(name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", event) {
|
2016-09-05 15:08:53 +03:00
|
|
|
event.preventDefault();
|
2016-09-04 18:33:40 +03:00
|
|
|
this.messageComposerInput.onFormatButtonClicked(name, event);
|
|
|
|
}
|
|
|
|
|
2016-09-05 15:08:53 +03:00
|
|
|
onToggleFormattingClicked() {
|
2016-09-07 20:22:14 +03:00
|
|
|
UserSettingsStore.setSyncedSetting('MessageComposer.showFormatting', !this.state.showFormatting);
|
2016-09-05 15:08:53 +03:00
|
|
|
this.setState({showFormatting: !this.state.showFormatting});
|
|
|
|
}
|
|
|
|
|
2016-09-08 00:16:56 +03:00
|
|
|
onToggleMarkdownClicked(e) {
|
|
|
|
e.preventDefault(); // don't steal focus from the editor!
|
2016-09-05 15:08:53 +03:00
|
|
|
this.messageComposerInput.enableRichtext(!this.state.inputState.isRichtextEnabled);
|
|
|
|
}
|
|
|
|
|
2016-06-20 11:22:55 +03:00
|
|
|
render() {
|
2017-05-17 13:31:01 +03:00
|
|
|
const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
|
|
|
|
const uploadInputStyle = {display: 'none'};
|
|
|
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
|
|
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
2017-07-04 16:44:55 +03:00
|
|
|
const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
|
2015-12-15 02:37:34 +03:00
|
|
|
|
2017-05-17 13:31:01 +03:00
|
|
|
const controls = [];
|
2016-03-24 16:57:21 +03:00
|
|
|
|
|
|
|
controls.push(
|
2016-04-13 04:02:55 +03:00
|
|
|
<div key="controls_avatar" className="mx_MessageComposer_avatar">
|
2016-03-24 16:57:21 +03:00
|
|
|
<MemberAvatar member={me} width={24} height={24} />
|
2017-05-17 13:31:01 +03:00
|
|
|
</div>,
|
2016-03-24 16:57:21 +03:00
|
|
|
);
|
|
|
|
|
2017-01-21 00:00:22 +03:00
|
|
|
let e2eImg, e2eTitle, e2eClass;
|
2017-02-21 18:33:44 +03:00
|
|
|
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
|
|
|
if (roomIsEncrypted) {
|
2016-09-12 03:37:51 +03:00
|
|
|
// FIXME: show a /!\ if there are untrusted devices in the room...
|
2017-01-21 00:00:22 +03:00
|
|
|
e2eImg = 'img/e2e-verified.svg';
|
2017-05-23 17:16:31 +03:00
|
|
|
e2eTitle = _t('Encrypted room');
|
2017-01-21 00:00:22 +03:00
|
|
|
e2eClass = 'mx_MessageComposer_e2eIcon';
|
2016-11-15 15:31:17 +03:00
|
|
|
} else {
|
2017-01-21 00:00:22 +03:00
|
|
|
e2eImg = 'img/e2e-unencrypted.svg';
|
2017-05-23 17:16:31 +03:00
|
|
|
e2eTitle = _t('Unencrypted room');
|
2017-01-21 00:00:22 +03:00
|
|
|
e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
|
2016-09-12 03:37:51 +03:00
|
|
|
}
|
|
|
|
|
2016-11-15 15:31:17 +03:00
|
|
|
controls.push(
|
2017-01-21 00:00:22 +03:00
|
|
|
<img key="e2eIcon" className={e2eClass} src={e2eImg} width="12" height="12"
|
|
|
|
alt={e2eTitle} title={e2eTitle}
|
2017-05-17 13:31:01 +03:00
|
|
|
/>,
|
2016-11-15 15:31:17 +03:00
|
|
|
);
|
2017-05-17 13:31:01 +03:00
|
|
|
let callButton, videoCallButton, hangupButton, showAppsButton, hideAppsButton;
|
2015-12-15 02:37:34 +03:00
|
|
|
if (this.props.callState && this.props.callState !== 'ended') {
|
|
|
|
hangupButton =
|
2016-04-13 04:02:55 +03:00
|
|
|
<div key="controls_hangup" className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
|
2017-05-23 17:16:31 +03:00
|
|
|
<img src="img/hangup.svg" alt={ _t('Hangup') } title={ _t('Hangup') } width="25" height="26"/>
|
2015-12-15 02:37:34 +03:00
|
|
|
</div>;
|
2017-05-17 12:58:59 +03:00
|
|
|
} else {
|
2015-12-15 02:37:34 +03:00
|
|
|
callButton =
|
2017-05-23 17:16:31 +03:00
|
|
|
<div key="controls_call" className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title={ _t('Voice call') }>
|
2016-08-01 18:45:27 +03:00
|
|
|
<TintableSvg src="img/icon-call.svg" width="35" height="35"/>
|
2016-07-03 19:45:13 +03:00
|
|
|
</div>;
|
2015-12-15 02:37:34 +03:00
|
|
|
videoCallButton =
|
2017-05-23 17:16:31 +03:00
|
|
|
<div key="controls_videocall" className="mx_MessageComposer_videocall" onClick={this.onCallClick} title={ _t('Video call') }>
|
2016-08-01 18:45:27 +03:00
|
|
|
<TintableSvg src="img/icons-video.svg" width="35" height="35"/>
|
2016-07-03 19:45:13 +03:00
|
|
|
</div>;
|
2015-12-15 02:37:34 +03:00
|
|
|
}
|
|
|
|
|
2017-05-17 12:58:59 +03:00
|
|
|
// Apps
|
2017-06-27 19:40:28 +03:00
|
|
|
if (UserSettingsStore.isFeatureEnabled('matrix_apps')) {
|
|
|
|
if (this.props.showApps) {
|
|
|
|
hideAppsButton =
|
2017-06-28 15:55:18 +03:00
|
|
|
<div key="controls_hide_apps" className="mx_MessageComposer_apps" onClick={this.onHideAppsClick} title={_t("Hide Apps")}>
|
2017-06-27 19:40:28 +03:00
|
|
|
<TintableSvg src="img/icons-apps-active.svg" width="35" height="35"/>
|
|
|
|
</div>;
|
|
|
|
} else {
|
|
|
|
showAppsButton =
|
2017-06-28 15:55:18 +03:00
|
|
|
<div key="show_apps" className="mx_MessageComposer_apps" onClick={this.onShowAppsClick} title={_t("Show Apps")}>
|
2017-06-27 19:40:28 +03:00
|
|
|
<TintableSvg src="img/icons-apps.svg" width="35" height="35"/>
|
|
|
|
</div>;
|
|
|
|
}
|
2017-05-17 12:58:59 +03:00
|
|
|
}
|
|
|
|
|
2017-05-17 13:31:01 +03:00
|
|
|
const canSendMessages = this.props.room.currentState.maySendMessage(
|
2016-03-24 16:57:21 +03:00
|
|
|
MatrixClientPeg.get().credentials.userId);
|
|
|
|
|
|
|
|
if (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.
|
2017-05-17 13:31:01 +03:00
|
|
|
const uploadButton = (
|
2016-04-13 04:02:55 +03:00
|
|
|
<div key="controls_upload" className="mx_MessageComposer_upload"
|
2017-05-23 17:16:31 +03:00
|
|
|
onClick={this.onUploadClick} title={ _t('Upload file') }>
|
2016-07-13 19:46:56 +03:00
|
|
|
<TintableSvg src="img/icons-upload.svg" width="35" height="35"/>
|
2016-03-24 16:57:21 +03:00
|
|
|
<input ref="uploadInput" type="file"
|
|
|
|
style={uploadInputStyle}
|
2016-04-02 22:56:50 +03:00
|
|
|
multiple
|
2016-03-24 16:57:21 +03:00
|
|
|
onChange={this.onUploadFileSelected} />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
|
2016-09-04 18:33:40 +03:00
|
|
|
const formattingButton = (
|
2016-09-05 15:08:53 +03:00
|
|
|
<img className="mx_MessageComposer_formatting"
|
2017-06-08 16:08:51 +03:00
|
|
|
title={_t("Show Text Formatting Toolbar")}
|
2016-09-04 18:33:40 +03:00
|
|
|
src="img/button-text-formatting.svg"
|
2016-09-05 15:08:53 +03:00
|
|
|
onClick={this.onToggleFormattingClicked}
|
2017-07-04 16:44:55 +03:00
|
|
|
style={{visibility: this.state.showFormatting ? 'hidden' : 'visible'}}
|
2016-09-04 18:33:40 +03:00
|
|
|
key="controls_formatting" />
|
|
|
|
);
|
|
|
|
|
2017-02-21 18:33:44 +03:00
|
|
|
const placeholderText = roomIsEncrypted ?
|
2017-05-23 17:16:31 +03:00
|
|
|
_t('Send an encrypted message') + '…' : _t('Send a message (unencrypted)') + '…';
|
2017-02-21 18:33:44 +03:00
|
|
|
|
2016-03-24 16:57:21 +03:00
|
|
|
controls.push(
|
2016-06-21 16:03:39 +03:00
|
|
|
<MessageComposerInput
|
2017-05-17 13:31:01 +03:00
|
|
|
ref={(c) => this.messageComposerInput = c}
|
2016-06-21 16:03:39 +03:00
|
|
|
key="controls_input"
|
|
|
|
onResize={this.props.onResize}
|
|
|
|
room={this.props.room}
|
2017-02-21 18:33:44 +03:00
|
|
|
placeholder={placeholderText}
|
2017-05-17 03:41:42 +03:00
|
|
|
onFilesPasted={this.uploadFiles}
|
2016-09-05 15:08:53 +03:00
|
|
|
onContentChanged={this.onInputContentChanged}
|
|
|
|
onInputStateChanged={this.onInputStateChanged} />,
|
2016-09-04 18:33:40 +03:00
|
|
|
formattingButton,
|
2016-03-24 16:57:21 +03:00
|
|
|
uploadButton,
|
|
|
|
hangupButton,
|
|
|
|
callButton,
|
2017-05-17 12:58:59 +03:00
|
|
|
videoCallButton,
|
|
|
|
showAppsButton,
|
|
|
|
hideAppsButton,
|
2016-03-24 16:57:21 +03:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
controls.push(
|
2016-04-13 04:02:55 +03:00
|
|
|
<div key="controls_error" className="mx_MessageComposer_noperm_error">
|
2017-05-23 17:16:31 +03:00
|
|
|
{ _t('You do not have permission to post to this room') }
|
2017-06-12 16:52:41 +03:00
|
|
|
</div>,
|
2016-03-24 16:57:21 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-09-05 15:08:53 +03:00
|
|
|
const {style, blockType} = this.state.inputState;
|
2016-09-07 20:22:14 +03:00
|
|
|
const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map(
|
2017-05-17 13:31:01 +03:00
|
|
|
(name) => {
|
2016-09-04 18:33:40 +03:00
|
|
|
const active = style.includes(name) || blockType === name;
|
|
|
|
const suffix = active ? '-o-n' : '';
|
|
|
|
const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
|
2017-07-06 15:49:13 +03:00
|
|
|
const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
|
2016-09-07 20:22:14 +03:00
|
|
|
return <img className={className}
|
2017-05-23 17:16:31 +03:00
|
|
|
title={ _t(name) }
|
2017-07-06 15:49:13 +03:00
|
|
|
onMouseDown={onFormatButtonClicked}
|
2016-09-07 20:22:14 +03:00
|
|
|
key={name}
|
|
|
|
src={`img/button-text-${name}${suffix}.svg`}
|
|
|
|
height="17" />;
|
2016-09-04 18:33:40 +03:00
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2015-11-26 20:31:10 +03:00
|
|
|
return (
|
2016-04-12 19:18:32 +03:00
|
|
|
<div className="mx_MessageComposer mx_fadable" style={{ opacity: this.props.opacity }}>
|
2016-03-24 16:57:21 +03:00
|
|
|
<div className="mx_MessageComposer_wrapper">
|
|
|
|
<div className="mx_MessageComposer_row">
|
|
|
|
{controls}
|
2015-11-26 20:31:10 +03:00
|
|
|
</div>
|
|
|
|
</div>
|
2017-07-04 16:44:55 +03:00
|
|
|
<div className="mx_MessageComposer_formatbar_wrapper">
|
|
|
|
<div className="mx_MessageComposer_formatbar" style={this.state.showFormatting ? {} : {display: 'none'}}>
|
|
|
|
{formatButtons}
|
|
|
|
<div style={{flex: 1}}></div>
|
|
|
|
<img title={ this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off") }
|
|
|
|
onMouseDown={this.onToggleMarkdownClicked}
|
|
|
|
className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
|
|
|
|
src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />
|
|
|
|
<img title={ _t("Hide Text Formatting Toolbar") }
|
|
|
|
onClick={this.onToggleFormattingClicked}
|
|
|
|
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
|
|
|
|
src="img/icon-text-cancel.svg" />
|
|
|
|
</div>
|
|
|
|
</div>
|
2015-11-26 20:31:10 +03:00
|
|
|
</div>
|
|
|
|
);
|
2015-09-18 12:44:57 +03:00
|
|
|
}
|
2017-01-20 17:22:27 +03:00
|
|
|
}
|
2016-06-20 11:22:55 +03:00
|
|
|
|
|
|
|
MessageComposer.propTypes = {
|
|
|
|
// 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,
|
2015-06-15 20:35:28 +03:00
|
|
|
|
2016-06-20 11:22:55 +03:00
|
|
|
// opacity for dynamic UI fading effects
|
2017-05-17 13:31:01 +03:00
|
|
|
opacity: React.PropTypes.number,
|
|
|
|
|
|
|
|
// string representing the current room app drawer state
|
2017-05-17 23:15:57 +03:00
|
|
|
showApps: React.PropTypes.bool,
|
2016-06-20 11:22:55 +03:00
|
|
|
};
|