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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + 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} +
+
+ {_t("Cancel")} +
+
; + } +} 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}