diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js index 85e170373c..9ff0244564 100644 --- a/src/components/views/elements/MessageEditor.js +++ b/src/components/views/elements/MessageEditor.js @@ -23,7 +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 {findEditableEvent} from '../../../utils/EventUtils'; import {parseEvent} from '../../../editor/deserialize'; import Autocomplete from '../rooms/Autocomplete'; import {PartCreator} from '../../../editor/parts'; @@ -133,7 +133,7 @@ export default class MessageEditor extends React.Component { if (this._hasModifications || !this._isCaretAtStart()) { return; } - const previousEvent = findPreviousEditableEvent(this._getRoom(), this.props.event.getId()); + const previousEvent = findEditableEvent(this._getRoom(), false, this.props.event.getId()); if (previousEvent) { dis.dispatch({action: 'edit_event', event: previousEvent}); event.preventDefault(); @@ -142,7 +142,7 @@ export default class MessageEditor extends React.Component { if (this._hasModifications || !this._isCaretAtEnd()) { return; } - const nextEvent = findNextEditableEvent(this._getRoom(), this.props.event.getId()); + const nextEvent = findEditableEvent(this._getRoom(), true, this.props.event.getId()); if (nextEvent) { dis.dispatch({action: 'edit_event', event: nextEvent}); } else { diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index ba24df6d08..30d7aa3237 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -59,7 +59,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'; +import {findEditableEvent} from '../../../utils/EventUtils'; const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); @@ -1181,7 +1181,7 @@ export default class MessageComposerInput extends React.Component { if (up) { if (!selection.anchor.isAtStartOfNode(document)) return; - const editEvent = findPreviousEditableEvent(this.props.room); + const editEvent = findEditableEvent(this.props.room, false); if (editEvent) { // We're selecting history, so prevent the key event from doing anything else e.preventDefault(); diff --git a/src/utils/EventUtils.js b/src/utils/EventUtils.js index d7c2285e7b..f8a4dd708c 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.js @@ -52,34 +52,29 @@ export function canEditContent(mxEvent) { mxEvent.getSender() === MatrixClientPeg.get().getUserId(); } -export function findPreviousEditableEvent(room, fromEventId = undefined) { +export function findEditableEvent(room, isForward, fromEventId = undefined) { const liveTimeline = room.getLiveTimeline(); const events = liveTimeline.getEvents(); - let startFromIdx = events.length - 1; - if (fromEventId) { - const fromEventIdx = findLastIndex(events, e => e.getId() === fromEventId); - if (fromEventIdx !== -1) { - startFromIdx = fromEventIdx - 1; - } + const maxIdx = events.length - 1; + const inc = isForward ? 1 : -1; + const beginIdx = isForward ? 0 : maxIdx; + let endIdx = isForward ? maxIdx : 0; + if (!fromEventId) { + endIdx = Math.min(Math.max(0, beginIdx + (inc * 100)), maxIdx); } - const nextEventIdx = findLastIndex(events, e => !shouldHideEvent(e) && canEditContent(e), startFromIdx); - if (nextEventIdx !== -1) { - return events[nextEventIdx]; + let foundFromEventId = !fromEventId; + for (let i = beginIdx; i !== (endIdx + inc); i += inc) { + const e = events[i]; + // find start event first + if (!foundFromEventId && e.getId() === fromEventId) { + foundFromEventId = true; + // don't look further than 100 events from `fromEventId` + // to not iterate potentially 1000nds of events on key up/down + endIdx = Math.min(Math.max(0, i + (inc * 100)), maxIdx); + } else if (foundFromEventId && !shouldHideEvent(e) && canEditContent(e)) { + // otherwise look for editable event + return e; + } } } -export function findNextEditableEvent(room, fromEventId = undefined) { - const liveTimeline = room.getLiveTimeline(); - const events = liveTimeline.getEvents(); - let startFromIdx = 0; - if (fromEventId) { - const fromEventIdx = findIndex(events, e => e.getId() === fromEventId); - if (fromEventIdx !== -1) { - startFromIdx = fromEventIdx + 1; - } - } - const nextEventIdx = findIndex(events, e => !shouldHideEvent(e) && canEditContent(e), startFromIdx); - if (nextEventIdx !== -1) { - return events[nextEventIdx]; - } -}