From 375976575bd0c94b03a6da60ab95785f43211fb6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 4 Jul 2019 11:07:22 +0200 Subject: [PATCH 1/8] add redact button --- .../views/dialogs/_MessageEditHistoryDialog.scss | 5 +++++ res/css/views/messages/_MessageActionBar.scss | 7 ++++++- src/components/views/messages/EditHistoryMessage.js | 13 +++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss index 951b863e6a..38188974ac 100644 --- a/res/css/views/dialogs/_MessageEditHistoryDialog.scss +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -42,5 +42,10 @@ limitations under the License. .mx_EventTile_line, .mx_EventTile_content { margin-right: 0px; } + + .mx_MessageActionBar .mx_AccessibleButton { + font-size: 10px; + padding: 0 8px; + } } diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 7ac0e95e81..b7ba2ef27d 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -30,9 +30,9 @@ limitations under the License. z-index: 1; > * { + white-space: nowrap; display: inline-block; position: relative; - width: 27px; border: 1px solid $message-action-bar-border-color; margin-left: -1px; @@ -55,6 +55,11 @@ limitations under the License. } } + +.mx_MessageActionBar_maskButton { + width: 27px; +} + .mx_MessageActionBar_maskButton::after { content: ''; position: absolute; diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index fef9c362c6..5b00257e87 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -20,6 +20,8 @@ import * as HtmlUtils from '../../../HtmlUtils'; import {formatTime} from '../../../DateUtils'; import {MatrixEvent} from 'matrix-js-sdk'; import {pillifyLinks} from '../../../utils/pillify'; +import { _t } from '../../../languageHandler'; +import sdk from '../../../index'; export default class EditHistoryMessage extends React.PureComponent { static propTypes = { @@ -27,6 +29,9 @@ export default class EditHistoryMessage extends React.PureComponent { mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, }; + _onRedactClick = async () => { + }; + componentDidMount() { pillifyLinks(this.refs.content.children, this.props.mxEvent); } @@ -35,6 +40,13 @@ export default class EditHistoryMessage extends React.PureComponent { pillifyLinks(this.refs.content.children, this.props.mxEvent); } + _renderActionBar() { + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + return (
+ {_t("Remove")} +
); + } + render() { const {mxEvent} = this.props; const originalContent = mxEvent.getOriginalContent(); @@ -55,6 +67,7 @@ export default class EditHistoryMessage extends React.PureComponent {
{timestamp} { contentContainer } + { this._renderActionBar() }
; } From 8468f7cdc39e5f970ae6978849d2e3e7d6afe70c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 4 Jul 2019 15:39:36 +0200 Subject: [PATCH 2/8] support redactions in edit history dialog --- .../dialogs/ConfirmAndWaitRedactDialog.js | 87 +++++++++++++++++++ .../views/messages/EditHistoryMessage.js | 50 ++++++++++- src/i18n/strings/en_EN.json | 3 +- 3 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 src/components/views/dialogs/ConfirmAndWaitRedactDialog.js diff --git a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js new file mode 100644 index 0000000000..5fd5129550 --- /dev/null +++ b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js @@ -0,0 +1,87 @@ +/* +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 sdk from '../../../index'; +import { _t } from '../../../languageHandler'; + +/* + * A dialog for confirming a redaction. + * Also shows a spinner (and possible error) while the redaction is ongoing, + * and only closes the dialog when the redaction is done or failed. + * + * This is done to prevent the edit history dialog racing with the redaction: + * if this dialog closes and the MessageEditHistoryDialog is shown again, + * it will fetch the relations again, which will race with the ongoing /redact request. + * which will cause the edit to appear unredacted. + * + * To avoid this, we keep the dialog open as long as /redact is in progress. + */ +export default class ConfirmAndWaitRedactDialog extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + isRedacting: false, + redactionErrorCode: null, + }; + } + + onParentFinished = async (proceed) => { + if (proceed) { + this.setState({isRedacting: true}); + try { + await this.props.redact(); + this.props.onFinished(true); + } catch (error) { + const code = error.errcode || error.statusCode; + if (typeof code !== "undefined") { + this.setState({redactionErrorCode: code}); + } else { + this.props.onFinished(true); + } + } + } else { + this.props.onFinished(false); + } + }; + + render() { + if (this.state.isRedacting) { + if (this.state.redactionErrorCode) { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const code = this.state.redactionErrorCode; + return ( + + ); + } else { + const BaseDialog = sdk.getComponent("dialogs.BaseDialog"); + const Spinner = sdk.getComponent('elements.Spinner'); + return ( + ); + } + } else { + const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); + return (); + } + } +} diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index 5b00257e87..67ab2f0e2c 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -22,6 +22,9 @@ import {MatrixEvent} from 'matrix-js-sdk'; import {pillifyLinks} from '../../../utils/pillify'; import { _t } from '../../../languageHandler'; import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import Modal from '../../../Modal'; +import classNames from 'classnames'; export default class EditHistoryMessage extends React.PureComponent { static propTypes = { @@ -29,19 +32,46 @@ export default class EditHistoryMessage extends React.PureComponent { mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, }; + constructor(props) { + super(props); + const cli = MatrixClientPeg.get(); + const {userId} = cli.credentials; + const event = this.props.mxEvent; + const room = cli.getRoom(event.getRoomId()); + const canRedact = room.currentState.maySendRedactionForEvent(event, userId); + this.state = {canRedact}; + } + _onRedactClick = async () => { + const event = this.props.mxEvent; + const cli = MatrixClientPeg.get(); + const ConfirmAndWaitRedactDialog = sdk.getComponent("dialogs.ConfirmAndWaitRedactDialog"); + + Modal.createTrackedDialog('Confirm Redact Dialog from edit history', '', ConfirmAndWaitRedactDialog, { + redact: () => cli.redactEvent(event.getRoomId(), event.getId()), + }, 'mx_Dialog_confirmredact'); }; + pillifyLinks() { + // not present for redacted events + if (this.refs.content) { + pillifyLinks(this.refs.content.children, this.props.mxEvent); + } + } + componentDidMount() { - pillifyLinks(this.refs.content.children, this.props.mxEvent); + this.pillifyLinks(); } componentDidUpdate() { - pillifyLinks(this.refs.content.children, this.props.mxEvent); + this.pillifyLinks(); } _renderActionBar() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + if (this.props.mxEvent.isRedacted()) { + return null; + } return (
{_t("Remove")}
); @@ -52,8 +82,12 @@ export default class EditHistoryMessage extends React.PureComponent { const originalContent = mxEvent.getOriginalContent(); const content = originalContent["m.new_content"] || originalContent; const contentElements = HtmlUtils.bodyToHtml(content); + const isRedacted = this.props.mxEvent.isRedacted(); let contentContainer; - if (mxEvent.getContent().msgtype === "m.emote") { + if (isRedacted) { + const UnknownBody = sdk.getComponent('messages.UnknownBody'); + contentContainer = (); + } else if (mxEvent.getContent().msgtype === "m.emote") { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); contentContainer = (
{ name } @@ -63,7 +97,15 @@ export default class EditHistoryMessage extends React.PureComponent { contentContainer = (
{contentElements}
); } const timestamp = formatTime(new Date(mxEvent.getTs()), this.props.isTwelveHour); - return
  • + const sendStatus = mxEvent.getAssociatedStatus(); + const isSending = (['sending', 'queued', 'encrypting'].indexOf(sendStatus) !== -1); + const classes = classNames({ + "mx_EventTile": true, + "mx_EventTile_redacted": isRedacted, + "mx_EventTile_sending": isSending, + "mx_EventTile_notSent": sendStatus === 'not_sent', + }); + return
  • {timestamp} { contentContainer } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0f1b6ebbc1..acabe191b4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -93,6 +93,7 @@ "Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:", "Unnamed Room": "Unnamed Room", "Error": "Error", + "You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)", "Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.", "Dismiss": "Dismiss", "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings", @@ -1125,6 +1126,7 @@ "Start chatting": "Start chatting", "Click on the button below to start chatting!": "Click on the button below to start chatting!", "Start Chatting": "Start Chatting", + "Removing…": "Removing…", "Confirm Removal": "Confirm Removal", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", "Community IDs cannot be empty.": "Community IDs cannot be empty.", @@ -1306,7 +1308,6 @@ "Reject invitation": "Reject invitation", "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", "Unable to reject invite": "Unable to reject invite", - "You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)", "Resend": "Resend", "Resend edit": "Resend edit", "Resend %(unsentCount)s reaction(s)": "Resend %(unsentCount)s reaction(s)", From 2182248d7c90907df23b74a6812aaad579b1ff41 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 5 Jul 2019 16:03:34 +0200 Subject: [PATCH 3/8] mark events from /relations as locally redacted if any pending redaction --- .../views/dialogs/MessageEditHistoryDialog.js | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index b30891488b..53dd6b2a1b 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -46,12 +46,13 @@ export default class MessageEditHistoryDialog extends React.PureComponent { const opts = {from: this.state.nextBatch}; const roomId = this.props.mxEvent.getRoomId(); const eventId = this.props.mxEvent.getId(); + const client = MatrixClientPeg.get(); let result; let resolve; let reject; const promise = new Promise((_resolve, _reject) => {resolve = _resolve; reject = _reject;}); try { - result = await MatrixClientPeg.get().relations( + result = await client.relations( roomId, eventId, "m.replace", "m.room.message", opts); } catch (error) { // log if the server returned an error @@ -61,8 +62,11 @@ export default class MessageEditHistoryDialog extends React.PureComponent { this.setState({error}, () => reject(error)); return promise; } + + const newEvents = result.events; + this._locallyRedactEventsIfNeeded(newEvents); this.setState({ - events: this.state.events.concat(result.events), + events: this.state.events.concat(newEvents), nextBatch: result.nextBatch, isLoading: false, }, () => { @@ -72,6 +76,21 @@ export default class MessageEditHistoryDialog extends React.PureComponent { return promise; } + _locallyRedactEventsIfNeeded(newEvents) { + const roomId = this.props.mxEvent.getRoomId(); + const client = MatrixClientPeg.get(); + const room = client.getRoom(roomId); + const pendingEvents = room.getPendingEvents(); + for (const e of newEvents) { + const pendingRedaction = pendingEvents.find(pe => { + return pe.getType() === "m.room.redaction" && pe.getAssociatedId() === e.getId(); + }); + if (pendingRedaction) { + e.markLocallyRedacted(pendingRedaction); + } + } + } + componentDidMount() { this.loadMoreEdits(); } From 0aa1252d014d20fb73b1e961428a93097c38894e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 5 Jul 2019 16:04:34 +0200 Subject: [PATCH 4/8] monitor associated send status for edit history events to update local echo if needed --- .../views/messages/EditHistoryMessage.js | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index 67ab2f0e2c..f2a721b52d 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -38,10 +38,17 @@ export default class EditHistoryMessage extends React.PureComponent { const {userId} = cli.credentials; const event = this.props.mxEvent; const room = cli.getRoom(event.getRoomId()); + if (event.localRedactionEvent()) { + event.localRedactionEvent().on("status", this._onAssociatedStatusChanged); + } const canRedact = room.currentState.maySendRedactionForEvent(event, userId); - this.state = {canRedact}; + this.state = {canRedact, sendStatus: event.getAssociatedStatus()}; } + _onAssociatedStatusChanged = () => { + this.setState({sendStatus: this.props.mxEvent.getAssociatedStatus()}); + }; + _onRedactClick = async () => { const event = this.props.mxEvent; const cli = MatrixClientPeg.get(); @@ -63,15 +70,24 @@ export default class EditHistoryMessage extends React.PureComponent { this.pillifyLinks(); } + componentWillUnmount() { + const event = this.props.mxEvent; + if (event.localRedactionEvent()) { + event.localRedactionEvent().off("status", this._onAssociatedStatusChanged); + } + } + componentDidUpdate() { this.pillifyLinks(); } _renderActionBar() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + // hide the button when already redacted if (this.props.mxEvent.isRedacted()) { return null; } + // disabled remove button when not allowed return (
    {_t("Remove")}
    ); @@ -82,9 +98,8 @@ export default class EditHistoryMessage extends React.PureComponent { const originalContent = mxEvent.getOriginalContent(); const content = originalContent["m.new_content"] || originalContent; const contentElements = HtmlUtils.bodyToHtml(content); - const isRedacted = this.props.mxEvent.isRedacted(); let contentContainer; - if (isRedacted) { + if (mxEvent.isRedacted()) { const UnknownBody = sdk.getComponent('messages.UnknownBody'); contentContainer = (); } else if (mxEvent.getContent().msgtype === "m.emote") { @@ -97,13 +112,12 @@ export default class EditHistoryMessage extends React.PureComponent { contentContainer = (
    {contentElements}
    ); } const timestamp = formatTime(new Date(mxEvent.getTs()), this.props.isTwelveHour); - const sendStatus = mxEvent.getAssociatedStatus(); - const isSending = (['sending', 'queued', 'encrypting'].indexOf(sendStatus) !== -1); + const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.state.sendStatus) !== -1); const classes = classNames({ "mx_EventTile": true, - "mx_EventTile_redacted": isRedacted, + "mx_EventTile_redacted": mxEvent.isRedacted(), "mx_EventTile_sending": isSending, - "mx_EventTile_notSent": sendStatus === 'not_sent', + "mx_EventTile_notSent": this.state.sendStatus === 'not_sent', }); return
  • From f4a8314c4f6f51fe3b1f56030e6664cfb50844d1 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 5 Jul 2019 16:05:40 +0200 Subject: [PATCH 5/8] make dom tree similar to eventtile so red lozenge style applies --- src/components/views/messages/EditHistoryMessage.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index f2a721b52d..8069a8e290 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -119,11 +119,13 @@ export default class EditHistoryMessage extends React.PureComponent { "mx_EventTile_sending": isSending, "mx_EventTile_notSent": this.state.sendStatus === 'not_sent', }); - return
  • -
    - {timestamp} - { contentContainer } - { this._renderActionBar() } + return
  • +
    +
    + {timestamp} + { contentContainer } + { this._renderActionBar() } +
  • ; } From 70334b69ec2cb7510d3a6433142578c0c9c5b688 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 5 Jul 2019 16:11:15 +0200 Subject: [PATCH 6/8] implement view source for edits --- .../views/messages/EditHistoryMessage.js | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index 8069a8e290..9d1c4bd784 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -59,6 +59,15 @@ export default class EditHistoryMessage extends React.PureComponent { }, 'mx_Dialog_confirmredact'); }; + _onViewSourceClick = () => { + const ViewSource = sdk.getComponent('structures.ViewSource'); + Modal.createTrackedDialog('View Event Source', '', ViewSource, { + roomId: this.props.mxEvent.getRoomId(), + eventId: this.props.mxEvent.getId(), + content: this.props.mxEvent.event, + }, 'mx_Dialog_viewsource'); + }; + pillifyLinks() { // not present for redacted events if (this.refs.content) { @@ -84,12 +93,19 @@ export default class EditHistoryMessage extends React.PureComponent { _renderActionBar() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); // hide the button when already redacted - if (this.props.mxEvent.isRedacted()) { - return null; + let redactButton; + if (!this.props.mxEvent.isRedacted()) { + redactButton = ( + {_t("Remove")} + ); } + const viewSourceButton = ( + {_t("View Source")} + ); // disabled remove button when not allowed return (
    - {_t("Remove")} + {redactButton} + {viewSourceButton}
    ); } From cf11a3758f0cb37695b50dcfc0f212d14cb00cbe Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 8 Jul 2019 10:12:04 +0200 Subject: [PATCH 7/8] set analyticsInfo for dialogs --- src/components/views/messages/EditHistoryMessage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index 9d1c4bd784..c873ebbcb2 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -54,14 +54,14 @@ export default class EditHistoryMessage extends React.PureComponent { const cli = MatrixClientPeg.get(); const ConfirmAndWaitRedactDialog = sdk.getComponent("dialogs.ConfirmAndWaitRedactDialog"); - Modal.createTrackedDialog('Confirm Redact Dialog from edit history', '', ConfirmAndWaitRedactDialog, { + Modal.createTrackedDialog('Confirm Redact Dialog', 'Edit history', ConfirmAndWaitRedactDialog, { redact: () => cli.redactEvent(event.getRoomId(), event.getId()), }, 'mx_Dialog_confirmredact'); }; _onViewSourceClick = () => { const ViewSource = sdk.getComponent('structures.ViewSource'); - Modal.createTrackedDialog('View Event Source', '', ViewSource, { + Modal.createTrackedDialog('View Event Source', 'Edit history', ViewSource, { roomId: this.props.mxEvent.getRoomId(), eventId: this.props.mxEvent.getId(), content: this.props.mxEvent.event, From 1af2ca6b36b32c2505a1ee429a134ac7c4145abf Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 8 Jul 2019 10:12:26 +0200 Subject: [PATCH 8/8] constistent jsx bracket and indent styling --- .../dialogs/ConfirmAndWaitRedactDialog.js | 13 +++-- .../views/messages/EditHistoryMessage.js | 56 +++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js index 5fd5129550..db00f445a8 100644 --- a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js +++ b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.js @@ -73,15 +73,18 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent { } else { const BaseDialog = sdk.getComponent("dialogs.BaseDialog"); const Spinner = sdk.getComponent('elements.Spinner'); - return ( + return ( + + + ); } } else { const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); - return (); + return ; } } } diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index c873ebbcb2..797e010aae 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -95,18 +95,24 @@ export default class EditHistoryMessage extends React.PureComponent { // hide the button when already redacted let redactButton; if (!this.props.mxEvent.isRedacted()) { - redactButton = ( - {_t("Remove")} - ); + redactButton = ( + + {_t("Remove")} + + ); } - const viewSourceButton = ( + const viewSourceButton = ( + {_t("View Source")} - ); + + ); // disabled remove button when not allowed - return (
    - {redactButton} - {viewSourceButton} -
    ); + return ( +
    + {redactButton} + {viewSourceButton} +
    + ); } render() { @@ -117,15 +123,17 @@ export default class EditHistoryMessage extends React.PureComponent { let contentContainer; if (mxEvent.isRedacted()) { const UnknownBody = sdk.getComponent('messages.UnknownBody'); - contentContainer = (); + contentContainer = ; } else if (mxEvent.getContent().msgtype === "m.emote") { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); - contentContainer = (
    *  - { name } -  {contentElements} -
    ); + contentContainer = ( +
    *  + { name } +  {contentElements} +
    + ); } else { - contentContainer = (
    {contentElements}
    ); + contentContainer =
    {contentElements}
    ; } const timestamp = formatTime(new Date(mxEvent.getTs()), this.props.isTwelveHour); const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.state.sendStatus) !== -1); @@ -135,14 +143,16 @@ export default class EditHistoryMessage extends React.PureComponent { "mx_EventTile_sending": isSending, "mx_EventTile_notSent": this.state.sendStatus === 'not_sent', }); - return
  • -
    -
    - {timestamp} - { contentContainer } - { this._renderActionBar() } + return ( +
  • +
    +
    + {timestamp} + { contentContainer } + { this._renderActionBar() } +
    -
  • - ; + + ); } }