diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index c8fac0b667..698356a175 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -279,22 +279,33 @@ export default class SendMessageComposer extends React.Component { }; _insertMention(userId) { + const {model} = this; + const {partCreator} = model; 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()); + const userPillPart = partCreator.userPill(displayName, userId); + const caret = this._editorRef.getCaret(); + const position = model.positionForOffset(caret.offset, caret.atNodeEnd); + model.transform(() => { + const addedLen = model.insert([userPillPart], position); + return model.positionForOffset(caret.offset + addedLen, true); + }); // refocus on composer, as we just clicked "Mention" this._editorRef && this._editorRef.focus(); } _insertQuotedMessage(event) { - const {partCreator} = this.model; + const {model} = this; + const {partCreator} = 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}); + 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(); } diff --git a/src/editor/model.js b/src/editor/model.js index 4c657f3168..ca04d9fdd0 100644 --- a/src/editor/model.js +++ b/src/editor/model.js @@ -158,8 +158,14 @@ export default class EditorModel { this._updateCallback(caret, inputType); } - insertPartsAt(parts, caret) { - const position = this.positionForOffset(caret.offset, caret.atNodeEnd); + /** + * Inserts the given parts at the given position. + * Should be run inside a `model.transform()` callback. + * @param {Part[]} parts the parts to replace the range with + * @param {DocumentPosition} position the position to start inserting at + * @return {Number} the amount of characters added + */ + insert(parts, position) { const insertIndex = this._splitAt(position); let newTextLength = 0; for (let i = 0; i < parts.length; ++i) { @@ -167,10 +173,7 @@ export default class EditorModel { newTextLength += part.text.length; this._insertPart(insertIndex + i, part); } - // put caret after new part - const lastPartIndex = insertIndex + parts.length - 1; - const newPosition = new DocumentPosition(lastPartIndex, newTextLength); - this._updateCallback(newPosition); + return newTextLength; } update(newValue, inputType, caret) { @@ -403,7 +406,7 @@ export default class EditorModel { return new Range(this, position); } - // called from Range.replace + //mostly internal, called from Range.replace replaceRange(startPosition, endPosition, parts) { const newStartPartIndex = this._splitAt(startPosition); const idxDiff = newStartPartIndex - startPosition.index; diff --git a/src/editor/range.js b/src/editor/range.js index e2ecc5d12b..1aaf480733 100644 --- a/src/editor/range.js +++ b/src/editor/range.js @@ -41,6 +41,12 @@ export default class Range { return text; } + /** + * Splits the model at the range boundaries and replaces with the given parts. + * Should be run inside a `model.transform()` callback. + * @param {Part[]} parts the parts to replace the range with + * @return {Number} the net amount of characters added, can be negative. + */ replace(parts) { const newLength = parts.reduce((sum, part) => sum + part.text.length, 0); let oldLength = 0;