From ef71e6fd4fc2701d1fd462647f67b09262a8b19c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 29 May 2019 18:03:05 +0200 Subject: [PATCH 01/23] very basic & hackish edit history dialog --- res/css/_components.scss | 1 + .../dialogs/_MessageEditHistoryDialog.scss | 38 +++++++++ .../views/dialogs/MessageEditHistoryDialog.js | 80 +++++++++++++++++++ src/i18n/strings/en_EN.json | 1 + 4 files changed, 120 insertions(+) create mode 100644 res/css/views/dialogs/_MessageEditHistoryDialog.scss create mode 100644 src/components/views/dialogs/MessageEditHistoryDialog.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 4986ca837f..d30684993d 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -61,6 +61,7 @@ @import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_IncomingSasDialog.scss"; +@import "./views/dialogs/_MessageEditHistoryDialog.scss"; @import "./views/dialogs/_RestoreKeyBackupDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss new file mode 100644 index 0000000000..ad51f4237f --- /dev/null +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -0,0 +1,38 @@ +/* +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. +*/ + +.mx_MessageEditHistoryDialog ul { + list-style-type: none; + + &>li.edit { + display: flex; + + &>strong { + flex: 0 0 100px; + font-weight: bold; + } + + &>p { + + } + + } + + ul, ol { + list-style-type: circle; + } +} + diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js new file mode 100644 index 0000000000..958c7457e0 --- /dev/null +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -0,0 +1,80 @@ +/* +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 MatrixClientPeg from "../../../MatrixClientPeg"; +import { _t } from '../../../languageHandler'; +import sdk from "../../../index"; +import * as HtmlUtils from '../../../HtmlUtils'; +import {wantsDateSeparator, formatTime} from '../../../DateUtils'; + +export default class MessageEditHistoryDialog extends React.Component { + static propTypes = { + mxEvent: PropTypes.object.isRequired, + }; + + componentWillMount() { + this.setState({edits: [this.props.mxEvent], isLoading: true}); + } + + async componentDidMount() { + const roomId = this.props.mxEvent.getRoomId(); + const eventId = this.props.mxEvent.getId(); + let edits = await MatrixClientPeg.get(). + relations(roomId, eventId, "m.replace", "m.room.message"); + edits = edits.slice().reverse(); + edits.unshift(this.props.mxEvent); + this.setState({edits, isLoading: false}); + } + + _renderEdit(event) { + const timestamp = formatTime(new Date(event.getTs()), true); + const content = event.event.content["m.new_content"] || event.event.content; + return
  • {timestamp}

    {HtmlUtils.bodyToHtml(content)}

  • ; + } + + _renderEdits() { + const DateSeparator = sdk.getComponent('messages.DateSeparator'); + const nodes = []; + let lastEvent; + this.state.edits.forEach(e => { + if (!lastEvent || wantsDateSeparator(lastEvent.getDate(), e.getDate())) { + nodes.push(
  • ); + } + nodes.push(this._renderEdit(e)); + lastEvent = e; + }); + return nodes; + } + + render() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + let spinner; + const edits = this._renderEdits(); + if (this.state.isLoading) { + const Spinner = sdk.getComponent("elements.Spinner"); + spinner = ; + } + return ( + +
      {edits}
    + {spinner} +
    + ); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 010ad29da0..01d84324ce 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1199,6 +1199,7 @@ "Manually export keys": "Manually export keys", "You'll lose access to your encrypted messages": "You'll lose access to your encrypted messages", "Are you sure you want to sign out?": "Are you sure you want to sign out?", + "Message edits": "Message edits", "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.", "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.", "Report bugs & give feedback": "Report bugs & give feedback", From 8b5f07e63d20cc72f780eadd08eec0ee954253fe Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 29 May 2019 18:23:18 +0200 Subject: [PATCH 02/23] open edit dialog on clicking (edited) --- src/components/views/messages/TextualBody.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index d76956d193..d95f0d88df 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -454,6 +454,11 @@ module.exports = React.createClass({ this.setState({editedMarkerHovered: false}); }, + _openHistoryDialog: async function() { + const MessageEditHistoryDialog = sdk.getComponent("views.dialogs.MessageEditHistoryDialog"); + Modal.createDialog(MessageEditHistoryDialog, {mxEvent: this.props.mxEvent}); + }, + _renderEditedMarker: function() { let editedTooltip; if (this.state.editedMarkerHovered) { @@ -468,6 +473,7 @@ module.exports = React.createClass({ return (
    {editedTooltip}{`(${_t("edited")})`}
    From e54881aa2486f688a91461277648f9ca7b8bd095 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 31 May 2019 16:52:27 +0200 Subject: [PATCH 03/23] WIP --- src/components/views/messages/TextualBody.js | 103 +------------------ src/utils/pillify.js | 103 +++++++++++++++++++ 2 files changed, 105 insertions(+), 101 deletions(-) create mode 100644 src/utils/pillify.js diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index d95f0d88df..db5d178c96 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -30,12 +30,11 @@ import Modal from '../../../Modal'; import SdkConfig from '../../../SdkConfig'; import dis from '../../../dispatcher'; import { _t } from '../../../languageHandler'; -import MatrixClientPeg from '../../../MatrixClientPeg'; import * as ContextualMenu from '../../structures/ContextualMenu'; import SettingsStore from "../../../settings/SettingsStore"; -import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; import ReplyThread from "../elements/ReplyThread"; import {host as matrixtoHost} from '../../../matrix-to'; +import {pillifyLinks} from '../../../utils/pillify'; module.exports = React.createClass({ displayName: 'TextualBody', @@ -99,7 +98,7 @@ module.exports = React.createClass({ // pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer // are still sent as plaintext URLs. If these are ever pillified in the composer, // we should be pillify them here by doing the linkifying BEFORE the pillifying. - this.pillifyLinks(this.refs.content.children); + pillifyLinks(this.refs.content.children, this.props.mxEvent); HtmlUtils.linkifyElement(this.refs.content); this.calculateUrlPreview(); @@ -184,104 +183,6 @@ module.exports = React.createClass({ } }, - pillifyLinks: function(nodes) { - const shouldShowPillAvatar = SettingsStore.getValue("Pill.shouldShowPillAvatar"); - let node = nodes[0]; - while (node) { - let pillified = false; - - if (node.tagName === "A" && node.getAttribute("href")) { - const href = node.getAttribute("href"); - - // If the link is a (localised) matrix.to link, replace it with a pill - const Pill = sdk.getComponent('elements.Pill'); - if (Pill.isMessagePillUrl(href)) { - const pillContainer = document.createElement('span'); - - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); - const pill = ; - - ReactDOM.render(pill, pillContainer); - node.parentNode.replaceChild(pillContainer, node); - // Pills within pills aren't going to go well, so move on - pillified = true; - - // update the current node with one that's now taken its place - node = pillContainer; - } - } else if ( - node.nodeType === Node.TEXT_NODE && - // as applying pills happens outside of react, make sure we're not doubly - // applying @room pills here, as a rerender with the same content won't touch the DOM - // to clear the pills from the last run of pillifyLinks - !node.parentElement.classList.contains("mx_AtRoomPill") - ) { - const Pill = sdk.getComponent('elements.Pill'); - - let currentTextNode = node; - const roomNotifTextNodes = []; - - // Take a textNode and break it up to make all the instances of @room their - // own textNode, adding those nodes to roomNotifTextNodes - while (currentTextNode !== null) { - const roomNotifPos = Pill.roomNotifPos(currentTextNode.textContent); - let nextTextNode = null; - if (roomNotifPos > -1) { - let roomTextNode = currentTextNode; - - if (roomNotifPos > 0) roomTextNode = roomTextNode.splitText(roomNotifPos); - if (roomTextNode.textContent.length > Pill.roomNotifLen()) { - nextTextNode = roomTextNode.splitText(Pill.roomNotifLen()); - } - roomNotifTextNodes.push(roomTextNode); - } - currentTextNode = nextTextNode; - } - - if (roomNotifTextNodes.length > 0) { - const pushProcessor = new PushProcessor(MatrixClientPeg.get()); - const atRoomRule = pushProcessor.getPushRuleById(".m.rule.roomnotif"); - if (atRoomRule && pushProcessor.ruleMatchesEvent(atRoomRule, this.props.mxEvent)) { - // Now replace all those nodes with Pills - for (const roomNotifTextNode of roomNotifTextNodes) { - // Set the next node to be processed to the one after the node - // we're adding now, since we've just inserted nodes into the structure - // we're iterating over. - // Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once - node = roomNotifTextNode.nextSibling; - - const pillContainer = document.createElement('span'); - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); - const pill = ; - - ReactDOM.render(pill, pillContainer); - roomNotifTextNode.parentNode.replaceChild(pillContainer, roomNotifTextNode); - } - // Nothing else to do for a text node (and we don't need to advance - // the loop pointer because we did it above) - continue; - } - } - } - - if (node.childNodes && node.childNodes.length && !pillified) { - this.pillifyLinks(node.childNodes); - } - - node = node.nextSibling; - } - }, - findLinks: function(nodes) { let links = []; diff --git a/src/utils/pillify.js b/src/utils/pillify.js new file mode 100644 index 0000000000..7744b6c15d --- /dev/null +++ b/src/utils/pillify.js @@ -0,0 +1,103 @@ +import ReactDOM from 'react-dom'; +import MatrixClientPeg from '../MatrixClientPeg'; +import SettingsStore from "../settings/SettingsStore"; +import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; +import sdk from '../index'; + + +export function pillifyLinks(nodes, mxEvent) { + const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); + const shouldShowPillAvatar = SettingsStore.getValue("Pill.shouldShowPillAvatar"); + let node = nodes[0]; + while (node) { + let pillified = false; + + if (node.tagName === "A" && node.getAttribute("href")) { + const href = node.getAttribute("href"); + + // If the link is a (localised) matrix.to link, replace it with a pill + const Pill = sdk.getComponent('elements.Pill'); + if (Pill.isMessagePillUrl(href)) { + const pillContainer = document.createElement('span'); + + const pill = ; + + ReactDOM.render(pill, pillContainer); + node.parentNode.replaceChild(pillContainer, node); + // Pills within pills aren't going to go well, so move on + pillified = true; + + // update the current node with one that's now taken its place + node = pillContainer; + } + } else if ( + node.nodeType === Node.TEXT_NODE && + // as applying pills happens outside of react, make sure we're not doubly + // applying @room pills here, as a rerender with the same content won't touch the DOM + // to clear the pills from the last run of pillifyLinks + !node.parentElement.classList.contains("mx_AtRoomPill") + ) { + const Pill = sdk.getComponent('elements.Pill'); + + let currentTextNode = node; + const roomNotifTextNodes = []; + + // Take a textNode and break it up to make all the instances of @room their + // own textNode, adding those nodes to roomNotifTextNodes + while (currentTextNode !== null) { + const roomNotifPos = Pill.roomNotifPos(currentTextNode.textContent); + let nextTextNode = null; + if (roomNotifPos > -1) { + let roomTextNode = currentTextNode; + + if (roomNotifPos > 0) roomTextNode = roomTextNode.splitText(roomNotifPos); + if (roomTextNode.textContent.length > Pill.roomNotifLen()) { + nextTextNode = roomTextNode.splitText(Pill.roomNotifLen()); + } + roomNotifTextNodes.push(roomTextNode); + } + currentTextNode = nextTextNode; + } + + if (roomNotifTextNodes.length > 0) { + const pushProcessor = new PushProcessor(MatrixClientPeg.get()); + const atRoomRule = pushProcessor.getPushRuleById(".m.rule.roomnotif"); + if (atRoomRule && pushProcessor.ruleMatchesEvent(atRoomRule, mxEvent)) { + // Now replace all those nodes with Pills + for (const roomNotifTextNode of roomNotifTextNodes) { + // Set the next node to be processed to the one after the node + // we're adding now, since we've just inserted nodes into the structure + // we're iterating over. + // Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once + node = roomNotifTextNode.nextSibling; + + const pillContainer = document.createElement('span'); + const pill = ; + + ReactDOM.render(pill, pillContainer); + roomNotifTextNode.parentNode.replaceChild(pillContainer, roomNotifTextNode); + } + // Nothing else to do for a text node (and we don't need to advance + // the loop pointer because we did it above) + continue; + } + } + } + + if (node.childNodes && node.childNodes.length && !pillified) { + pillifyLinks(node.childNodes); + } + + node = node.nextSibling; + } +} From 19b4699bc2259f185031af1ddbdcbeacebace0f9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 3 Jun 2019 14:31:09 +0200 Subject: [PATCH 04/23] WIP for showing pills in edit history --- .../views/elements/EditHistoryMessage.js | 51 +++++++++++++++++++ src/utils/pillify.js | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/components/views/elements/EditHistoryMessage.js diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js new file mode 100644 index 0000000000..2b8ecb87aa --- /dev/null +++ b/src/components/views/elements/EditHistoryMessage.js @@ -0,0 +1,51 @@ +/* +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 * as HtmlUtils from '../../../HtmlUtils'; +import {formatTime} from '../../../DateUtils'; +import {MatrixEvent} from 'matrix-js-sdk'; +import {pillifyLinks} from '../../../utils/pillify'; + +export default class EditHistoryMessage extends React.Component { + static propTypes = { + // the message event being edited + mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, + }; + + constructor(props, context) { + super(props, context); + } + + componentDidMount() { + pillifyLinks(this.refs.content.children, this.props.mxEvent); + } + + componentDidUpdate() { + pillifyLinks(this.refs.content.children, this.props.mxEvent); + } + + render() { + const event = this.props.mxEvent; + const timestamp = formatTime(new Date(event.getTs()), true); + const content = event.event.content["m.new_content"] || event.event.content; + return
  • + {timestamp} +

    {HtmlUtils.bodyToHtml(content)}

    +
  • ; + } +} diff --git a/src/utils/pillify.js b/src/utils/pillify.js index 7744b6c15d..74eac560d3 100644 --- a/src/utils/pillify.js +++ b/src/utils/pillify.js @@ -95,7 +95,7 @@ export function pillifyLinks(nodes, mxEvent) { } if (node.childNodes && node.childNodes.length && !pillified) { - pillifyLinks(node.childNodes); + pillifyLinks(node.childNodes, mxEvent); } node = node.nextSibling; From c9aa7efe54be3bb1bcfc22236e8b85d26bab0d81 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 24 Jun 2019 17:45:29 +0200 Subject: [PATCH 05/23] don't require EventTile for default timestamp style --- res/css/views/messages/_MessageTimestamp.scss | 2 ++ res/css/views/rooms/_EventTile.scss | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/messages/_MessageTimestamp.scss b/res/css/views/messages/_MessageTimestamp.scss index e21189c59e..e5c228aa68 100644 --- a/res/css/views/messages/_MessageTimestamp.scss +++ b/res/css/views/messages/_MessageTimestamp.scss @@ -15,4 +15,6 @@ limitations under the License. */ .mx_MessageTimestamp { + color: $event-timestamp-color; + font-size: 10px; } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 62632eab27..1bd62e393e 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -93,8 +93,6 @@ limitations under the License. display: block; visibility: hidden; white-space: nowrap; - color: $event-timestamp-color; - font-size: 10px; left: 0px; width: 46px; /* 8 + 30 (avatar) + 8 */ text-align: center; From e7fc84d5dab3320a68e50446d913f577ad116b58 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 24 Jun 2019 17:45:57 +0200 Subject: [PATCH 06/23] render history items in own component including: - respect 12/24 hour setting - pillify --- .../views/dialogs/MessageEditHistoryDialog.js | 19 +++++++++---------- .../views/elements/EditHistoryMessage.js | 6 +++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 958c7457e0..d6198abea4 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -19,8 +19,8 @@ import PropTypes from 'prop-types'; import MatrixClientPeg from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; import sdk from "../../../index"; -import * as HtmlUtils from '../../../HtmlUtils'; -import {wantsDateSeparator, formatTime} from '../../../DateUtils'; +import {wantsDateSeparator} from '../../../DateUtils'; +import SettingsStore from '../../../settings/SettingsStore'; export default class MessageEditHistoryDialog extends React.Component { static propTypes = { @@ -28,7 +28,11 @@ export default class MessageEditHistoryDialog extends React.Component { }; componentWillMount() { - this.setState({edits: [this.props.mxEvent], isLoading: true}); + this.setState({ + edits: [this.props.mxEvent], + isLoading: true, + isTwelveHour: SettingsStore.getValue("showTwelveHourTimestamps"), + }); } async componentDidMount() { @@ -41,13 +45,8 @@ export default class MessageEditHistoryDialog extends React.Component { this.setState({edits, isLoading: false}); } - _renderEdit(event) { - const timestamp = formatTime(new Date(event.getTs()), true); - const content = event.event.content["m.new_content"] || event.event.content; - return
  • {timestamp}

    {HtmlUtils.bodyToHtml(content)}

  • ; - } - _renderEdits() { + const EditHistoryMessage = sdk.getComponent('elements.EditHistoryMessage'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); const nodes = []; let lastEvent; @@ -55,7 +54,7 @@ export default class MessageEditHistoryDialog extends React.Component { if (!lastEvent || wantsDateSeparator(lastEvent.getDate(), e.getDate())) { nodes.push(
  • ); } - nodes.push(this._renderEdit(e)); + nodes.push(); lastEvent = e; }); return nodes; diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js index 2b8ecb87aa..1f18072fd8 100644 --- a/src/components/views/elements/EditHistoryMessage.js +++ b/src/components/views/elements/EditHistoryMessage.js @@ -41,10 +41,10 @@ export default class EditHistoryMessage extends React.Component { render() { const event = this.props.mxEvent; - const timestamp = formatTime(new Date(event.getTs()), true); + const timestamp = formatTime(new Date(event.getTs()), this.props.isTwelveHour); const content = event.event.content["m.new_content"] || event.event.content; - return
  • - {timestamp} + return
  • + {timestamp}

    {HtmlUtils.bodyToHtml(content)}

  • ; } From beb003b2d65f1b8e3a11953a0e69e9c5a27a6a95 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 24 Jun 2019 17:46:30 +0200 Subject: [PATCH 07/23] some preliminary styling --- res/css/views/dialogs/_MessageEditHistoryDialog.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss index ad51f4237f..b917fe87fd 100644 --- a/res/css/views/dialogs/_MessageEditHistoryDialog.scss +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -16,17 +16,18 @@ limitations under the License. .mx_MessageEditHistoryDialog ul { list-style-type: none; + font-size: 14px; &>li.edit { + margin: 10px 0; display: flex; - &>strong { - flex: 0 0 100px; - font-weight: bold; + &>.mx_MessageTimestamp { + flex: 0 0 50px; } &>p { - + margin: 0; } } From 0fe28cba4363125b52ec6351743b5f7248a2e9e6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 25 Jun 2019 10:18:27 +0200 Subject: [PATCH 08/23] support emotes in edit history --- .../views/elements/EditHistoryMessage.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js index 1f18072fd8..749f478ba4 100644 --- a/src/components/views/elements/EditHistoryMessage.js +++ b/src/components/views/elements/EditHistoryMessage.js @@ -40,12 +40,23 @@ export default class EditHistoryMessage extends React.Component { } render() { - const event = this.props.mxEvent; - const timestamp = formatTime(new Date(event.getTs()), this.props.isTwelveHour); - const content = event.event.content["m.new_content"] || event.event.content; + const {mxEvent} = this.props; + const content = mxEvent.event.content["m.new_content"] || mxEvent.event.content; + const contentElements = HtmlUtils.bodyToHtml(content); + let contentContainer; + if (mxEvent.getContent().msgtype === "m.emote") { + const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); + contentContainer = (

    *  + { name } +  {contentElements} +

    ); + } else { + contentContainer = (

    {contentElements}

    ); + } + const timestamp = formatTime(new Date(mxEvent.getTs()), this.props.isTwelveHour); return
  • {timestamp} -

    {HtmlUtils.bodyToHtml(content)}

    + { contentContainer }
  • ; } } From 8c9a6ddf96e66b6239a9e672cc8c6acfd1eb8335 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 25 Jun 2019 14:50:56 +0200 Subject: [PATCH 09/23] support edits pagination in a ScrollPanel --- .../dialogs/_MessageEditHistoryDialog.scss | 11 ++++ .../views/dialogs/MessageEditHistoryDialog.js | 60 ++++++++++++++----- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss index b917fe87fd..bc4c2680ed 100644 --- a/res/css/views/dialogs/_MessageEditHistoryDialog.scss +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -15,6 +15,17 @@ limitations under the License. */ .mx_MessageEditHistoryDialog ul { +.mx_MessageEditHistoryDialog { + display: flex; + flex-direction: column; + max-height: 60vh; +} + +.mx_MessageEditHistoryDialog_scrollPanel { + flex: 1 1 auto; +} + +.mx_MessageEditHistoryDialog_edits { list-style-type: none; font-size: 14px; diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index d6198abea4..37f49026ac 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -28,21 +28,41 @@ export default class MessageEditHistoryDialog extends React.Component { }; componentWillMount() { + this.loadMoreEdits = this.loadMoreEdits.bind(this); this.setState({ - edits: [this.props.mxEvent], + events: [], + nextBatch: null, isLoading: true, isTwelveHour: SettingsStore.getValue("showTwelveHourTimestamps"), }); } - async componentDidMount() { + async loadMoreEdits(backwards) { + if (backwards || (!this.state.nextBatch && !this.state.isLoading)) { + // bail out on backwards as we only paginate in one direction + return false; + } + const opts = {token: this.state.nextBatch}; const roomId = this.props.mxEvent.getRoomId(); const eventId = this.props.mxEvent.getId(); - let edits = await MatrixClientPeg.get(). - relations(roomId, eventId, "m.replace", "m.room.message"); - edits = edits.slice().reverse(); - edits.unshift(this.props.mxEvent); - this.setState({edits, isLoading: false}); + const result = await MatrixClientPeg.get().relations( + roomId, eventId, "m.replace", "m.room.message", opts); + //console.log(`loadMoreEdits: got ${result.}`) + let resolve; + const promise = new Promise(r => resolve = r); + this.setState({ + events: this.state.events.concat(result.events), + nextBatch: result.nextBatch, + isLoading: false, + }, () => { + const hasMoreResults = !!this.state.nextBatch; + resolve(hasMoreResults); + }); + return promise; + } + + componentDidMount() { + this.loadMoreEdits(); } _renderEdits() { @@ -50,7 +70,7 @@ export default class MessageEditHistoryDialog extends React.Component { const DateSeparator = sdk.getComponent('messages.DateSeparator'); const nodes = []; let lastEvent; - this.state.edits.forEach(e => { + this.state.events.forEach(e => { if (!lastEvent || wantsDateSeparator(lastEvent.getDate(), e.getDate())) { nodes.push(
  • ); } @@ -61,18 +81,28 @@ export default class MessageEditHistoryDialog extends React.Component { } render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - let spinner; - const edits = this._renderEdits(); - if (this.state.isLoading) { + let content; + if (this.state.error) { + content = this.state.error; + } else if (this.state.isLoading) { const Spinner = sdk.getComponent("elements.Spinner"); - spinner = ; + content = ; + } else { + const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); + content = ( +
      {this._renderEdits()}
    +
    ); } + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); return ( -
      {edits}
    - {spinner} + {content}
    ); } From ee03a0f31dd5c2f5f390e23cbe6a54a646dc139d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 11:56:58 +0200 Subject: [PATCH 10/23] recycle EventTile css to make history items look mostly similar --- .../dialogs/_MessageEditHistoryDialog.scss | 20 ++++--------------- .../views/elements/EditHistoryMessage.js | 14 +++++++------ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss index bc4c2680ed..2da72a042e 100644 --- a/res/css/views/dialogs/_MessageEditHistoryDialog.scss +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -28,23 +28,11 @@ limitations under the License. .mx_MessageEditHistoryDialog_edits { list-style-type: none; font-size: 14px; + padding: 0; + color: $primary-fg-color; - &>li.edit { - margin: 10px 0; - display: flex; - - &>.mx_MessageTimestamp { - flex: 0 0 50px; - } - - &>p { - margin: 0; - } - - } - - ul, ol { - list-style-type: circle; + .mx_EventTile_line, .mx_EventTile_content { + margin-right: 0px; } } diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js index 749f478ba4..a6cbfbdfd4 100644 --- a/src/components/views/elements/EditHistoryMessage.js +++ b/src/components/views/elements/EditHistoryMessage.js @@ -46,17 +46,19 @@ export default class EditHistoryMessage extends React.Component { let contentContainer; if (mxEvent.getContent().msgtype === "m.emote") { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); - contentContainer = (

    *  + contentContainer = (

    { name }  {contentElements} -

    ); +
    ); } else { - contentContainer = (

    {contentElements}

    ); + contentContainer = (
    {contentElements}
    ); } const timestamp = formatTime(new Date(mxEvent.getTs()), this.props.isTwelveHour); - return
  • - {timestamp} - { contentContainer } + return
  • +
    + {timestamp} + { contentContainer } +
  • ; } } From fffdfde8baa91e5bb6368427dd25a73370b3cd12 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 11:57:29 +0200 Subject: [PATCH 11/23] center dialog title --- res/css/views/dialogs/_MessageEditHistoryDialog.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss index 2da72a042e..b80742bd24 100644 --- a/res/css/views/dialogs/_MessageEditHistoryDialog.scss +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -14,7 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MessageEditHistoryDialog ul { +.mx_MessageEditHistoryDialog .mx_Dialog_header > .mx_Dialog_title { + text-align: center; +} + .mx_MessageEditHistoryDialog { display: flex; flex-direction: column; From fe3be39fe772a1d5b6f80b633d23a5739a826e17 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 11:57:49 +0200 Subject: [PATCH 12/23] don't hide timestamps --- src/components/views/dialogs/MessageEditHistoryDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 37f49026ac..1f5408e94c 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -95,7 +95,7 @@ export default class MessageEditHistoryDialog extends React.Component { stickyBottom={false} startAtBottom={false} > -
      {this._renderEdits()}
    +
      {this._renderEdits()}
    ); } const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); From ddae4de7bd11c541c25190ce62010ded43f7ce9d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 11:58:03 +0200 Subject: [PATCH 13/23] cleanup: RoomDirectory doesn't use gemini anymore --- res/css/structures/_RoomDirectory.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/res/css/structures/_RoomDirectory.scss b/res/css/structures/_RoomDirectory.scss index bcfe3aefd6..1df0a61a2b 100644 --- a/res/css/structures/_RoomDirectory.scss +++ b/res/css/structures/_RoomDirectory.scss @@ -35,13 +35,6 @@ limitations under the License. flex: 1; } -.mx_RoomDirectory .gm-scroll-view { - // little hack because gemini doesn't seem to detect - // the scrollbar width well in this instance - // when using css scrollbars - scrollbar-width: thin; -} - .mx_RoomDirectory_createRoom { background-color: $button-bg-color; border-radius: 4px; From 498db2597dd87d52784f6d35ccf4a3f1934fcc1b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 12:15:13 +0200 Subject: [PATCH 14/23] show hand on hovering (edited) marker --- res/css/views/rooms/_EventTile.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 1bd62e393e..1f75373be8 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -401,6 +401,7 @@ limitations under the License. color: $roomtopic-color; display: inline-block; margin-left: 9px; + cursor: pointer; } /* Various markdown overrides */ From fa0319f14b005fc72d5d9368bf590b5afa69f848 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 15:49:24 +0200 Subject: [PATCH 15/23] apply renamed (token -> from) option --- src/components/views/dialogs/MessageEditHistoryDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 1f5408e94c..dd6ef0d866 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -42,7 +42,7 @@ export default class MessageEditHistoryDialog extends React.Component { // bail out on backwards as we only paginate in one direction return false; } - const opts = {token: this.state.nextBatch}; + const opts = {from: this.state.nextBatch}; const roomId = this.props.mxEvent.getRoomId(); const eventId = this.props.mxEvent.getId(); const result = await MatrixClientPeg.get().relations( From f4b86ca2658f70017c34037f183f8f6b63c5bd5f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 15:51:28 +0200 Subject: [PATCH 16/23] don't bind --- src/components/views/dialogs/MessageEditHistoryDialog.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index dd6ef0d866..45d46b49f2 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -28,7 +28,6 @@ export default class MessageEditHistoryDialog extends React.Component { }; componentWillMount() { - this.loadMoreEdits = this.loadMoreEdits.bind(this); this.setState({ events: [], nextBatch: null, @@ -37,7 +36,7 @@ export default class MessageEditHistoryDialog extends React.Component { }); } - async loadMoreEdits(backwards) { + loadMoreEdits = async (backwards) => { if (backwards || (!this.state.nextBatch && !this.state.isLoading)) { // bail out on backwards as we only paginate in one direction return false; From 39c96b15d8bfb98723c0164230a6a38ac78859d4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 15:51:46 +0200 Subject: [PATCH 17/23] set state in ctor --- src/components/views/dialogs/MessageEditHistoryDialog.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 45d46b49f2..9b11fadf83 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -27,13 +27,14 @@ export default class MessageEditHistoryDialog extends React.Component { mxEvent: PropTypes.object.isRequired, }; - componentWillMount() { - this.setState({ + constructor(props) { + super(props); + this.state = { events: [], nextBatch: null, isLoading: true, isTwelveHour: SettingsStore.getValue("showTwelveHourTimestamps"), - }); + }; } loadMoreEdits = async (backwards) => { From 929020a13964f956c423d53d50f61acf1fa39741 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 15:51:55 +0200 Subject: [PATCH 18/23] remove leftover logging --- src/components/views/dialogs/MessageEditHistoryDialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 9b11fadf83..81a3732fe6 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -47,7 +47,6 @@ export default class MessageEditHistoryDialog extends React.Component { const eventId = this.props.mxEvent.getId(); const result = await MatrixClientPeg.get().relations( roomId, eventId, "m.replace", "m.room.message", opts); - //console.log(`loadMoreEdits: got ${result.}`) let resolve; const promise = new Promise(r => resolve = r); this.setState({ From d606c966ea7194854af5d22bd91e77bf76315de4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 16:12:44 +0200 Subject: [PATCH 19/23] use PureComponent --- src/components/views/dialogs/MessageEditHistoryDialog.js | 2 +- src/components/views/elements/EditHistoryMessage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 81a3732fe6..786f1f43a0 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -22,7 +22,7 @@ import sdk from "../../../index"; import {wantsDateSeparator} from '../../../DateUtils'; import SettingsStore from '../../../settings/SettingsStore'; -export default class MessageEditHistoryDialog extends React.Component { +export default class MessageEditHistoryDialog extends React.PureComponent { static propTypes = { mxEvent: PropTypes.object.isRequired, }; diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js index a6cbfbdfd4..b4b87bd246 100644 --- a/src/components/views/elements/EditHistoryMessage.js +++ b/src/components/views/elements/EditHistoryMessage.js @@ -21,7 +21,7 @@ import {formatTime} from '../../../DateUtils'; import {MatrixEvent} from 'matrix-js-sdk'; import {pillifyLinks} from '../../../utils/pillify'; -export default class EditHistoryMessage extends React.Component { +export default class EditHistoryMessage extends React.PureComponent { static propTypes = { // the message event being edited mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, From c987f4e8d8ac71289820d1636d6721ef32320b54 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 16:12:57 +0200 Subject: [PATCH 20/23] remove passthrough ctor --- src/components/views/elements/EditHistoryMessage.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js index b4b87bd246..2807bedeb8 100644 --- a/src/components/views/elements/EditHistoryMessage.js +++ b/src/components/views/elements/EditHistoryMessage.js @@ -27,10 +27,6 @@ export default class EditHistoryMessage extends React.PureComponent { mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, }; - constructor(props, context) { - super(props, context); - } - componentDidMount() { pillifyLinks(this.refs.content.children, this.props.mxEvent); } From a1548285b55688097a743a3dd2132308664dbd0a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 16:13:15 +0200 Subject: [PATCH 21/23] fix copyright header and whitespace --- .../views/elements/EditHistoryMessage.js | 2 +- src/utils/pillify.js | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/elements/EditHistoryMessage.js index 2807bedeb8..85a704641a 100644 --- a/src/components/views/elements/EditHistoryMessage.js +++ b/src/components/views/elements/EditHistoryMessage.js @@ -1,5 +1,4 @@ /* -Copyright 2019 New Vector Ltd Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +13,7 @@ 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 * as HtmlUtils from '../../../HtmlUtils'; diff --git a/src/utils/pillify.js b/src/utils/pillify.js index 74eac560d3..e943cfe657 100644 --- a/src/utils/pillify.js +++ b/src/utils/pillify.js @@ -1,10 +1,25 @@ +/* +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 ReactDOM from 'react-dom'; import MatrixClientPeg from '../MatrixClientPeg'; import SettingsStore from "../settings/SettingsStore"; import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; import sdk from '../index'; - export function pillifyLinks(nodes, mxEvent) { const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); const shouldShowPillAvatar = SettingsStore.getValue("Pill.shouldShowPillAvatar"); From 54de0b298b36bdfc68d48b628373b21ed2779e75 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 16:13:32 +0200 Subject: [PATCH 22/23] add "Click to see edits." to tooltip --- src/components/views/messages/TextualBody.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index db5d178c96..25316844df 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -368,7 +368,7 @@ module.exports = React.createClass({ const date = editEvent && formatDate(editEvent.getDate()); editedTooltip = ; } return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 01d84324ce..15dbd95d8c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -954,7 +954,7 @@ "Failed to copy": "Failed to copy", "Add an Integration": "Add an Integration", "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", - "Edited at %(date)s": "Edited at %(date)s", + "Edited at %(date)s. Click to view edits.": "Edited at %(date)s. Click to view edits.", "edited": "edited", "Removed or unknown message type": "Removed or unknown message type", "Message removed by %(userId)s": "Message removed by %(userId)s", From c9c84016cbc0a438e3a2c02b7d01987c0b77da7d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 26 Jun 2019 16:17:25 +0200 Subject: [PATCH 23/23] move EditHistoryMessage to messages directory --- src/components/views/dialogs/MessageEditHistoryDialog.js | 2 +- .../views/{elements => messages}/EditHistoryMessage.js | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/components/views/{elements => messages}/EditHistoryMessage.js (100%) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 786f1f43a0..9d533eab56 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -65,7 +65,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent { } _renderEdits() { - const EditHistoryMessage = sdk.getComponent('elements.EditHistoryMessage'); + const EditHistoryMessage = sdk.getComponent('messages.EditHistoryMessage'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); const nodes = []; let lastEvent; diff --git a/src/components/views/elements/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js similarity index 100% rename from src/components/views/elements/EditHistoryMessage.js rename to src/components/views/messages/EditHistoryMessage.js