Apply the same to quoting & inserting of emoji then consolidate
This commit is contained in:
parent
ace3a62bac
commit
5f59e39958
12 changed files with 105 additions and 50 deletions
|
@ -467,16 +467,17 @@ class TimelinePanel extends React.Component {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "insert_mention": {
|
case "composer_insert": {
|
||||||
|
// re-dispatch to the correct composer
|
||||||
if (this.state.editState) {
|
if (this.state.editState) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
...payload,
|
...payload,
|
||||||
action: "insert_mention_edit_composer",
|
action: "edit_composer_insert",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
...payload,
|
...payload,
|
||||||
action: "insert_mention_send_composer",
|
action: "send_composer_insert",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -233,7 +233,7 @@ export default class MessageContextMenu extends React.Component {
|
||||||
|
|
||||||
onQuoteClick = () => {
|
onQuoteClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'quote',
|
action: "composer_insert",
|
||||||
event: this.props.mxEvent,
|
event: this.props.mxEvent,
|
||||||
});
|
});
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
|
|
|
@ -390,8 +390,8 @@ export default class TextualBody extends React.Component {
|
||||||
onEmoteSenderClick = event => {
|
onEmoteSenderClick = event => {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'insert_mention',
|
action: "composer_insert",
|
||||||
user_id: mxEvent.getSender(),
|
userId: mxEvent.getSender(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -360,8 +360,8 @@ const UserOptionsSection: React.FC<{
|
||||||
|
|
||||||
const onInsertPillButton = function() {
|
const onInsertPillButton = function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'insert_mention',
|
action: "composer_insert",
|
||||||
user_id: member.userId,
|
userId: member.userId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, {createRef, ClipboardEvent} from 'react';
|
import React, {createRef, ClipboardEvent} from 'react';
|
||||||
import {Room} from 'matrix-js-sdk/src/models/room';
|
import {Room} from 'matrix-js-sdk/src/models/room';
|
||||||
|
import {MatrixEvent} from 'matrix-js-sdk/src/models/event';
|
||||||
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
|
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
|
||||||
|
|
||||||
import EditorModel from '../../../editor/model';
|
import EditorModel from '../../../editor/model';
|
||||||
|
@ -32,7 +33,7 @@ import {
|
||||||
import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom';
|
import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom';
|
||||||
import Autocomplete, {generateCompletionDomId} from '../rooms/Autocomplete';
|
import Autocomplete, {generateCompletionDomId} from '../rooms/Autocomplete';
|
||||||
import {getAutoCompleteCreator} from '../../../editor/parts';
|
import {getAutoCompleteCreator} from '../../../editor/parts';
|
||||||
import {parsePlainTextMessage} from '../../../editor/deserialize';
|
import {parseEvent, parsePlainTextMessage} from '../../../editor/deserialize';
|
||||||
import {renderModel} from '../../../editor/render';
|
import {renderModel} from '../../../editor/render';
|
||||||
import TypingStore from "../../../stores/TypingStore";
|
import TypingStore from "../../../stores/TypingStore";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -732,4 +733,30 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
// refocus on composer, as we just clicked "Mention"
|
// refocus on composer, as we just clicked "Mention"
|
||||||
this.focus();
|
this.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public insertQuotedMessage(event: MatrixEvent) {
|
||||||
|
const {model} = this.props;
|
||||||
|
const {partCreator} = model;
|
||||||
|
const quoteParts = parseEvent(event, partCreator, {isQuotedMessage: true});
|
||||||
|
// add two newlines
|
||||||
|
quoteParts.push(partCreator.newline());
|
||||||
|
quoteParts.push(partCreator.newline());
|
||||||
|
model.transform(() => {
|
||||||
|
const addedLen = model.insert(quoteParts, model.positionForOffset(0));
|
||||||
|
return model.positionForOffset(addedLen, true);
|
||||||
|
});
|
||||||
|
// refocus on composer, as we just clicked "Quote"
|
||||||
|
this.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public insertPlaintext(text: string) {
|
||||||
|
const {model} = this.props;
|
||||||
|
const {partCreator} = model;
|
||||||
|
const caret = this.getCaret();
|
||||||
|
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
|
||||||
|
model.transform(() => {
|
||||||
|
const addedLen = model.insert([partCreator.plain(text)], position);
|
||||||
|
return model.positionForOffset(caret.offset + addedLen, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,8 +281,14 @@ export default class EditMessageComposer extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
onAction = payload => {
|
onAction = payload => {
|
||||||
if (payload.action === "insert_mention_edit_composer" && this._editorRef) {
|
if (payload.action === "edit_composer_insert" && this._editorRef) {
|
||||||
this._editorRef.insertMention(payload.user_id);
|
if (payload.user_id) {
|
||||||
|
this._editorRef.insertMention(payload.userId);
|
||||||
|
} else if (payload.event) {
|
||||||
|
this._editorRef.insertQuotedMessage(payload.event);
|
||||||
|
} else if (payload.text) {
|
||||||
|
this._editorRef.insertPlaintext(payload.text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -644,8 +644,8 @@ export default class EventTile extends React.Component {
|
||||||
onSenderProfileClick = event => {
|
onSenderProfileClick = event => {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'insert_mention',
|
action: "composer_insert",
|
||||||
user_id: mxEvent.getSender(),
|
userId: mxEvent.getSender(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -312,8 +312,8 @@ export default class MessageComposer extends React.Component {
|
||||||
|
|
||||||
addEmoji(emoji) {
|
addEmoji(emoji) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "insert_emoji",
|
action: "composer_insert",
|
||||||
emoji,
|
text: emoji,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -482,44 +482,18 @@ export default class SendMessageComposer extends React.Component {
|
||||||
case Action.FocusComposer:
|
case Action.FocusComposer:
|
||||||
this._editorRef && this._editorRef.focus();
|
this._editorRef && this._editorRef.focus();
|
||||||
break;
|
break;
|
||||||
case 'insert_mention_send_composer':
|
case "send_composer_insert":
|
||||||
this._editorRef && this._editorRef.insertMention(payload.user_id);
|
if (payload.userId) {
|
||||||
break;
|
this._editorRef && this._editorRef.insertMention(payload.userId);
|
||||||
case 'quote':
|
} else if (payload.event) {
|
||||||
this._insertQuotedMessage(payload.event);
|
this._editorRef && this._editorRef.insertQuotedMessage(payload.event);
|
||||||
break;
|
} else if (payload.text) {
|
||||||
case 'insert_emoji':
|
this._editorRef && this._editorRef.insertPlaintext(payload.emoji);
|
||||||
this._insertEmoji(payload.emoji);
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_insertQuotedMessage(event) {
|
|
||||||
const {model} = this;
|
|
||||||
const {partCreator} = model;
|
|
||||||
const quoteParts = parseEvent(event, partCreator, {isQuotedMessage: true});
|
|
||||||
// add two newlines
|
|
||||||
quoteParts.push(partCreator.newline());
|
|
||||||
quoteParts.push(partCreator.newline());
|
|
||||||
model.transform(() => {
|
|
||||||
const addedLen = model.insert(quoteParts, model.positionForOffset(0));
|
|
||||||
return model.positionForOffset(addedLen, true);
|
|
||||||
});
|
|
||||||
// refocus on composer, as we just clicked "Quote"
|
|
||||||
this._editorRef && this._editorRef.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
_insertEmoji = (emoji) => {
|
|
||||||
const {model} = this;
|
|
||||||
const {partCreator} = model;
|
|
||||||
const caret = this._editorRef.getCaret();
|
|
||||||
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
|
|
||||||
model.transform(() => {
|
|
||||||
const addedLen = model.insert([partCreator.plain(emoji)], position);
|
|
||||||
return model.positionForOffset(caret.offset + addedLen, true);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
_onPaste = (event) => {
|
_onPaste = (event) => {
|
||||||
const {clipboardData} = event;
|
const {clipboardData} = event;
|
||||||
// Prioritize text on the clipboard over files as Office on macOS puts a bitmap
|
// Prioritize text on the clipboard over files as Office on macOS puts a bitmap
|
||||||
|
|
|
@ -138,4 +138,9 @@ export enum Action {
|
||||||
* Fired when an upload is cancelled by the user. Should be used with UploadCanceledPayload.
|
* Fired when an upload is cancelled by the user. Should be used with UploadCanceledPayload.
|
||||||
*/
|
*/
|
||||||
UploadCanceled = "upload_canceled",
|
UploadCanceled = "upload_canceled",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts content into the active composer. Should be used with ComposerInsertPayload
|
||||||
|
*/
|
||||||
|
ComposerInsert = "composer_insert",
|
||||||
}
|
}
|
||||||
|
|
42
src/dispatcher/payloads/ComposerInsertPayload.ts
Normal file
42
src/dispatcher/payloads/ComposerInsertPayload.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 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 { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
|
import { ActionPayload } from "../payloads";
|
||||||
|
import { Action } from "../actions";
|
||||||
|
|
||||||
|
interface IBaseComposerInsertPayload extends ActionPayload {
|
||||||
|
action: Action.ComposerInsert,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IComposerInsertMentionPayload extends IBaseComposerInsertPayload {
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IComposerInsertQuotePayload extends IBaseComposerInsertPayload {
|
||||||
|
event: MatrixEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IComposerInsertPlaintextPayload extends IBaseComposerInsertPayload {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComposerInsertPayload =
|
||||||
|
IComposerInsertMentionPayload |
|
||||||
|
IComposerInsertQuotePayload |
|
||||||
|
IComposerInsertPlaintextPayload;
|
||||||
|
|
|
@ -390,7 +390,7 @@ export default class EditorModel {
|
||||||
return addLen;
|
return addLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
positionForOffset(totalOffset: number, atPartEnd: boolean) {
|
positionForOffset(totalOffset: number, atPartEnd = false) {
|
||||||
let currentOffset = 0;
|
let currentOffset = 0;
|
||||||
const index = this._parts.findIndex(part => {
|
const index = this._parts.findIndex(part => {
|
||||||
const partLen = part.text.length;
|
const partLen = part.text.length;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue