From f305d8ac085a09e980992ba0668ea8a10794e2bd Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 19 Jul 2019 16:09:23 +0200 Subject: [PATCH 1/3] Basic diff visualisation for plain text edits --- package.json | 1 + .../dialogs/_MessageEditHistoryDialog.scss | 14 +++++++ .../views/dialogs/MessageEditHistoryDialog.js | 3 +- .../views/messages/EditHistoryMessage.js | 37 +++++++++++++++++-- yarn.lock | 5 +++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index dba8def040..af7bbb7d5b 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "classnames": "^2.1.2", "commonmark": "^0.28.1", "counterpart": "^0.18.0", + "diff-match-patch": "^1.0.4", "emojibase-data": "^4.0.0", "emojibase-regex": "^3.0.0", "file-saver": "^1.3.3", diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss index 38188974ac..3f54cc2e82 100644 --- a/res/css/views/dialogs/_MessageEditHistoryDialog.scss +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -39,6 +39,20 @@ limitations under the License. padding: 0; color: $primary-fg-color; + span.mx_EditHistoryMessage_deletion, span.mx_EditHistoryMessage_insertion { + padding: 0px 2px; + } + + span.mx_EditHistoryMessage_deletion { + color: rgb(255, 76, 85); + background-color: rgba(255, 76, 85, 0.1); + } + + span.mx_EditHistoryMessage_insertion { + color: rgb(26, 169, 123); + background-color: rgba(26, 169, 123, 0.1); + } + .mx_EventTile_line, .mx_EventTile_content { margin-right: 0px; } diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.js b/src/components/views/dialogs/MessageEditHistoryDialog.js index 56e208e464..6014cb941c 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.js +++ b/src/components/views/dialogs/MessageEditHistoryDialog.js @@ -108,7 +108,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent { allEvents = allEvents.concat(this.state.originalEvent); } const baseEventId = this.props.mxEvent.getId(); - allEvents.forEach(e => { + allEvents.forEach((e, i) => { if (!lastEvent || wantsDateSeparator(lastEvent.getDate(), e.getDate())) { nodes.push(
  • ); } @@ -116,6 +116,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent { nodes.push(( { + // not using del and ins tags here as del is used for strikethrough + if (modifier < 0) { + return ({text}); + } else if (modifier > 0) { + return ({text}); + } else { + return text; + } + }); + } + render() { const {mxEvent} = this.props; - const originalContent = mxEvent.getOriginalContent(); - const content = originalContent["m.new_content"] || originalContent; + const content = getReplacedContent(mxEvent); let contentContainer; if (mxEvent.isRedacted()) { const UnknownBody = sdk.getComponent('messages.UnknownBody'); contentContainer = ; } else { - const contentElements = HtmlUtils.bodyToHtml(content, null, {stripReplyFallback: true}); + let contentElements; + if (isPlainMessage(mxEvent) && this.props.previousEdit && isPlainMessage(this.props.previousEdit)) { + contentElements = this._diffIt(getReplacedContent(this.props.previousEdit).body, content.body); + } else { + contentElements = HtmlUtils.bodyToHtml(content, null, {stripReplyFallback: true}); + } if (mxEvent.getContent().msgtype === "m.emote") { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); contentContainer = ( diff --git a/yarn.lock b/yarn.lock index 2ee2604b28..8f6f24d5e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2464,6 +2464,11 @@ di@^0.0.1: resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= +diff-match-patch@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1" + integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg== + diff-sequences@^24.3.0: version "24.3.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975" From de1c4e0cfe9f6b8aa7af7a965b109acd5491c3f7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 19 Jul 2019 16:34:39 +0200 Subject: [PATCH 2/3] better naming --- 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 64eac0ae9f..89d6e14a5c 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -126,7 +126,7 @@ export default class EditHistoryMessage extends React.PureComponent { ); } - _diffIt(oldBody, newBody) { + _renderBodyDiff(oldBody, newBody) { const dpm = new DiffMatchPatch(); const diff = dpm.diff_main(oldBody, newBody); dpm.diff_cleanupSemantic(diff); @@ -152,7 +152,7 @@ export default class EditHistoryMessage extends React.PureComponent { } else { let contentElements; if (isPlainMessage(mxEvent) && this.props.previousEdit && isPlainMessage(this.props.previousEdit)) { - contentElements = this._diffIt(getReplacedContent(this.props.previousEdit).body, content.body); + contentElements = this._renderBodyDiff(getReplacedContent(this.props.previousEdit).body, content.body); } else { contentElements = HtmlUtils.bodyToHtml(content, null, {stripReplyFallback: true}); } From c713172a37a0d633ddf22d1318be890941a89e09 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 19 Jul 2019 16:34:50 +0200 Subject: [PATCH 3/3] add proptypes --- src/components/views/messages/EditHistoryMessage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index 89d6e14a5c..8649cca1c4 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -41,6 +41,8 @@ export default class EditHistoryMessage extends React.PureComponent { static propTypes = { // the message event being edited mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, + previousEdit: PropTypes.instanceOf(MatrixEvent).isRequired, + isBaseEvent: PropTypes.bool, }; constructor(props) {