diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index 3478ce30a8..3662e34ba4 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -23,6 +23,7 @@ import EditorModel from '../../../editor/model'; import {setCaretPosition} from '../../../editor/caret'; import {getCaretOffsetAndText} from '../../../editor/dom'; import {htmlSerializeIfNeeded, textSerialize} from '../../../editor/serialize'; +import {findPreviousEditableEvent, findNextEditableEvent} from '../../../utils/EventUtils'; import {parseEvent} from '../../../editor/deserialize'; import Autocomplete from '../rooms/Autocomplete'; import {PartCreator} from '../../../editor/parts'; @@ -42,7 +43,7 @@ export default class MessageEditor extends React.Component { constructor(props, context) { super(props, context); - const room = this.context.matrixClient.getRoom(this.props.event.getRoomId()); + const room = this._getRoom(); const partCreator = new PartCreator( () => this._autocompleteRef, query => this.setState({query}), @@ -61,6 +62,10 @@ export default class MessageEditor extends React.Component { this._autocompleteRef = null; } + _getRoom() { + return this.context.matrixClient.getRoom(this.props.event.getRoomId()); + } + _updateEditorState = (caret) => { renderModel(this._editorRef, this.model); if (caret) { @@ -79,6 +84,16 @@ export default class MessageEditor extends React.Component { this.model.update(text, event.inputType, caret); } + _isCaretAtStart() { + const {caret} = getCaretOffsetAndText(this._editorRef, document.getSelection()); + return caret.offset === 0; + } + + _isCaretAtEnd() { + const {caret, text} = getCaretOffsetAndText(this._editorRef, document.getSelection()); + return caret.offset === text.length; + } + _onKeyDown = (event) => { // insert newline on Shift+Enter if (event.shiftKey && event.key === "Enter") { @@ -112,6 +127,27 @@ export default class MessageEditor extends React.Component { event.preventDefault(); } else if (event.key === "Escape") { this._cancelEdit(); + } else if (event.key === "ArrowUp") { + if (!this._isCaretAtStart()) { + return; + } + const previousEvent = findPreviousEditableEvent(this._getRoom(), this.props.event.getId()); + if (previousEvent) { + dis.dispatch({action: 'edit_event', event: previousEvent}); + event.preventDefault(); + } + } else if (event.key === "ArrowDown") { + if (!this._isCaretAtEnd()) { + return; + } + const nextEvent = findNextEditableEvent(this._getRoom(), this.props.event.getId()); + if (nextEvent) { + dis.dispatch({action: 'edit_event', event: nextEvent}); + } else { + dis.dispatch({action: 'edit_event', event: null}); + dis.dispatch({action: 'focus_composer'}); + } + event.preventDefault(); } } diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index df16310bda..729e13f13c 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -60,6 +60,7 @@ import RoomViewStore from '../../../stores/RoomViewStore'; import ReplyThread from "../elements/ReplyThread"; import {ContentHelpers} from 'matrix-js-sdk'; import AccessibleButton from '../elements/AccessibleButton'; +import { findPreviousEditableEvent } from '../../../utils/EventUtils'; const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); @@ -1188,14 +1189,16 @@ export default class MessageComposerInput extends React.Component { // and we must be at the edge of the document (up=start, down=end) if (up) { if (!selection.anchor.isAtStartOfNode(document)) return; - } else { - if (!selection.anchor.isAtEndOfNode(document)) return; - } - const selected = this.selectHistory(up); - if (selected) { - // We're selecting history, so prevent the key event from doing anything else - e.preventDefault(); + const editEvent = findPreviousEditableEvent(this.props.room); + if (editEvent) { + // We're selecting history, so prevent the key event from doing anything else + e.preventDefault(); + dis.dispatch({ + action: 'edit_event', + event: editEvent, + }); + } } } else { this.moveAutocompleteSelection(up);