diff --git a/res/css/_components.scss b/res/css/_components.scss
index 6e681894e3..2e0c91bd8c 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -89,6 +89,7 @@
@import "./views/elements/_InlineSpinner.scss";
@import "./views/elements/_ManageIntegsButton.scss";
@import "./views/elements/_MemberEventListSummary.scss";
+@import "./views/elements/_MessageEditor.scss";
@import "./views/elements/_PowerSelector.scss";
@import "./views/elements/_ProgressBar.scss";
@import "./views/elements/_ReplyThread.scss";
diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss
new file mode 100644
index 0000000000..57ae79da8c
--- /dev/null
+++ b/res/css/views/elements/_MessageEditor.scss
@@ -0,0 +1,42 @@
+/*
+Copyright 2019 Vector Creations Ltd
+
+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_MessageEditor {
+ border-radius: 4px;
+ background-color: #f3f8fd;
+ padding: 10px;
+
+ .editor {
+ border-radius: 4px;
+ border: solid 1px #e9edf1;
+ background-color: #ffffff;
+ }
+
+ .buttons {
+ display: flex;
+ flex-direction: column;
+ align-items: end;
+ padding: 5px 0;
+
+ .mx_AccessibleButton {
+ background-color: $button-bg-color;
+ border-radius: 4px;
+ padding: 5px 40px;
+ color: $button-fg-color;
+ font-weight: 600;
+ }
+ }
+}
diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss
index 419542036e..a0240c8171 100644
--- a/res/css/views/messages/_MessageActionBar.scss
+++ b/res/css/views/messages/_MessageActionBar.scss
@@ -69,6 +69,10 @@ limitations under the License.
mask-image: url('$(res)/img/reply.svg');
}
+.mx_MessageActionBar_editButton::after {
+ mask-image: url('$(res)/img/edit.svg');
+}
+
.mx_MessageActionBar_optionsButton::after {
mask-image: url('$(res)/img/icon_context.svg');
}
diff --git a/res/img/edit.svg b/res/img/edit.svg
new file mode 100644
index 0000000000..15b5ef9563
--- /dev/null
+++ b/res/img/edit.svg
@@ -0,0 +1,97 @@
+
+
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index 2037217710..adc78d7032 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -450,9 +450,14 @@ module.exports = React.createClass({
_getTilesForEvent: function(prevEvent, mxEv, last) {
const EventTile = sdk.getComponent('rooms.EventTile');
+ const MessageEditor = sdk.getComponent('elements.MessageEditor');
const DateSeparator = sdk.getComponent('messages.DateSeparator');
const ret = [];
+ if (this.props.editEvent && this.props.editEvent.getId() === mxEv.getId()) {
+ return [];
+ }
+
// is this a continuation of the previous message?
let continuation = false;
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 17a062be98..6529e92256 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -402,6 +402,9 @@ const TimelinePanel = React.createClass({
if (payload.action === 'ignore_state_changed') {
this.forceUpdate();
}
+ if (payload.action === "edit_event") {
+ this.setState({editEvent: payload.event});
+ }
},
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
@@ -1244,6 +1247,7 @@ const TimelinePanel = React.createClass({
tileShape={this.props.tileShape}
resizeNotifier={this.props.resizeNotifier}
getRelationsForEvent={this.getRelationsForEvent}
+ editEvent={this.state.editEvent}
/>
);
},
diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js
new file mode 100644
index 0000000000..f57521dbe9
--- /dev/null
+++ b/src/components/views/elements/MessageEditor.js
@@ -0,0 +1,56 @@
+/*
+Copyright 2019 New Vector Ltd
+
+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';
+import PropTypes from 'prop-types';
+import dis from '../../../dispatcher';
+import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
+
+export default class MessageEditor extends React.Component {
+ static propTypes = {
+ // the latest event in this chain of replies
+ event: PropTypes.instanceOf(MatrixEvent).isRequired,
+ // called when the ReplyThread contents has changed, including EventTiles thereof
+ // onHeightChanged: PropTypes.func.isRequired,
+ };
+
+ static contextTypes = {
+ matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {};
+ this._onCancelClicked = this._onCancelClicked.bind(this);
+ }
+
+ _onCancelClicked() {
+ dis.dispatch({action: "edit_event", event: null});
+ }
+
+ render() {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ return
+
+ {this.props.event.getContent().body}
+
+
+
;
+ }
+}
diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js
index 52630d7b0e..c4b8c441bd 100644
--- a/src/components/views/messages/MessageActionBar.js
+++ b/src/components/views/messages/MessageActionBar.js
@@ -58,6 +58,13 @@ export default class MessageActionBar extends React.PureComponent {
});
}
+ onEditClick = (ev) => {
+ dis.dispatch({
+ action: 'edit_event',
+ event: this.props.mxEvent,
+ });
+ }
+
onOptionsClick = (ev) => {
const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
const buttonRect = ev.target.getBoundingClientRect();
@@ -128,6 +135,7 @@ export default class MessageActionBar extends React.PureComponent {
let agreeDimensionReactionButtons;
let likeDimensionReactionButtons;
let replyButton;
+ let editButton;
if (isContentActionable(this.props.mxEvent)) {
agreeDimensionReactionButtons = this.renderAgreeDimension();
@@ -136,12 +144,17 @@ export default class MessageActionBar extends React.PureComponent {
title={_t("Reply")}
onClick={this.onReplyClick}
/>;
+ editButton = ;
}
return
{agreeDimensionReactionButtons}
{likeDimensionReactionButtons}
{replyButton}
+ {editButton}