mirror of
https://github.com/element-hq/element-web.git
synced 2024-12-16 10:01:32 +03:00
182 lines
6.2 KiB
JavaScript
182 lines
6.2 KiB
JavaScript
/*
|
|
Copyright 2019 New Vector Ltd
|
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
|
|
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.
|
|
*/
|
|
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import dis from '../../../dispatcher';
|
|
import EditorModel from '../../../editor/model';
|
|
import {getCaretOffsetAndText} from '../../../editor/dom';
|
|
import {htmlSerializeIfNeeded, textSerialize} from '../../../editor/serialize';
|
|
import {PartCreator} from '../../../editor/parts';
|
|
import {MatrixClient} from 'matrix-js-sdk';
|
|
import BasicMessageComposer from "./BasicMessageComposer";
|
|
import ReplyPreview from "./ReplyPreview";
|
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
|
import ReplyThread from "../elements/ReplyThread";
|
|
import {parseEvent} from '../../../editor/deserialize';
|
|
|
|
function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
|
|
const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent);
|
|
Object.assign(content, replyContent);
|
|
|
|
// Part of Replies fallback support - prepend the text we're sending
|
|
// with the text we're replying to
|
|
const nestedReply = ReplyThread.getNestedReplyText(repliedToEvent, permalinkCreator);
|
|
if (nestedReply) {
|
|
if (content.formatted_body) {
|
|
content.formatted_body = nestedReply.html + content.formatted_body;
|
|
}
|
|
content.body = nestedReply.body + content.body;
|
|
}
|
|
}
|
|
|
|
function createMessageContent(model, permalinkCreator) {
|
|
const repliedToEvent = RoomViewStore.getQuotingEvent();
|
|
|
|
const body = textSerialize(model);
|
|
const content = {
|
|
msgtype: "m.text",
|
|
body: body,
|
|
};
|
|
const formattedBody = htmlSerializeIfNeeded(model, {forceHTML: !!repliedToEvent});
|
|
if (formattedBody) {
|
|
content.format = "org.matrix.custom.html";
|
|
content.formatted_body = formattedBody;
|
|
}
|
|
|
|
if (repliedToEvent) {
|
|
addReplyToMessageContent(content, repliedToEvent, permalinkCreator);
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
export default class SendMessageComposer extends React.Component {
|
|
static propTypes = {
|
|
room: PropTypes.object.isRequired,
|
|
placeholder: PropTypes.string,
|
|
permalinkCreator: PropTypes.object.isRequired,
|
|
};
|
|
|
|
static contextTypes = {
|
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
|
};
|
|
|
|
constructor(props, context) {
|
|
super(props, context);
|
|
this.model = null;
|
|
this._editorRef = null;
|
|
}
|
|
|
|
_setEditorRef = ref => {
|
|
this._editorRef = ref;
|
|
};
|
|
|
|
_onKeyDown = (event) => {
|
|
if (event.metaKey || event.altKey || event.shiftKey) {
|
|
return;
|
|
}
|
|
if (event.key === "Enter") {
|
|
this._sendMessage();
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
_sendMessage() {
|
|
const isReply = !!RoomViewStore.getQuotingEvent();
|
|
const {roomId} = this.props.room;
|
|
this.context.matrixClient.sendMessage(roomId, createMessageContent(this.model, this.props.permalinkCreator));
|
|
this.model.reset([]);
|
|
this._editorRef.clearUndoHistory();
|
|
|
|
if (isReply) {
|
|
// Clear reply_to_event as we put the message into the queue
|
|
// if the send fails, retry will handle resending.
|
|
dis.dispatch({
|
|
action: 'reply_to_event',
|
|
event: null,
|
|
});
|
|
}
|
|
dis.dispatch({action: 'focus_composer'});
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
const sel = document.getSelection();
|
|
const {caret} = getCaretOffsetAndText(this._editorRef, sel);
|
|
const parts = this.model.serializeParts();
|
|
this.props.editState.setEditorState(caret, parts);
|
|
dis.unregister(this.dispatcherRef);
|
|
}
|
|
|
|
componentWillMount() {
|
|
const partCreator = new PartCreator(this.props.room, this.context.matrixClient);
|
|
this.model = new EditorModel([], partCreator);
|
|
this.dispatcherRef = dis.register(this.onAction);
|
|
}
|
|
|
|
onAction = (payload) => {
|
|
switch (payload.action) {
|
|
case 'reply_to_event':
|
|
case 'focus_composer':
|
|
this._editorRef.focus();
|
|
break;
|
|
case 'insert_mention':
|
|
this._insertMention(payload.user_id);
|
|
break;
|
|
case 'quote':
|
|
this._insertQuotedMessage(payload.event);
|
|
break;
|
|
}
|
|
};
|
|
|
|
_insertMention(userId) {
|
|
const member = this.props.room.getMember(userId);
|
|
const displayName = member ?
|
|
member.rawDisplayName : userId;
|
|
const userPillPart = this.model.partCreator.userPill(displayName, userId);
|
|
this.model.insertPartsAt([userPillPart], this._editorRef.getCaret());
|
|
// refocus on composer, as we just clicked "Mention"
|
|
this._editorRef.focus();
|
|
}
|
|
|
|
_insertQuotedMessage(event) {
|
|
const {partCreator} = this.model;
|
|
const quoteParts = parseEvent(event, partCreator, { isQuotedMessage: true });
|
|
// add two newlines
|
|
quoteParts.push(partCreator.newline());
|
|
quoteParts.push(partCreator.newline());
|
|
this.model.insertPartsAt(quoteParts, {offset: 0});
|
|
// refocus on composer, as we just clicked "Quote"
|
|
this._editorRef.focus();
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div className="mx_SendMessageComposer" onClick={this.focusComposer} onKeyDown={this._onKeyDown}>
|
|
<div className="mx_SendMessageComposer_overlayWrapper">
|
|
<ReplyPreview permalinkCreator={this.props.permalinkCreator} />
|
|
</div>
|
|
<BasicMessageComposer
|
|
ref={this._setEditorRef}
|
|
model={this.model}
|
|
room={this.props.room}
|
|
label={this.props.placeholder}
|
|
placeholder={this.props.placeholder}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
}
|