Merge pull request #3238 from matrix-org/bwindels/diffplainedits

Basic diff visualisation for plain text edits
This commit is contained in:
Bruno Windels 2019-07-19 14:41:53 +00:00 committed by GitHub
commit c335c04c75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 4 deletions

View file

@ -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(<li key={e.getTs() + "~"}><DateSeparator ts={e.getTs()} /></li>);
}
@ -116,6 +116,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
nodes.push((
<EditHistoryMessage
key={e.getId()}
previousEdit={!isBaseEvent && allEvents[i + 1]}
isBaseEvent={isBaseEvent}
mxEvent={e}
isTwelveHour={this.state.isTwelveHour}

View file

@ -25,11 +25,24 @@ import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import classNames from 'classnames';
import DiffMatchPatch from 'diff-match-patch';
function getReplacedContent(event) {
const originalContent = event.getOriginalContent();
return originalContent["m.new_content"] || originalContent;
}
function isPlainMessage(event) {
const content = getReplacedContent(event);
return content.msgtype === "m.text" && !content.format;
}
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) {
@ -115,16 +128,36 @@ export default class EditHistoryMessage extends React.PureComponent {
);
}
_renderBodyDiff(oldBody, newBody) {
const dpm = new DiffMatchPatch();
const diff = dpm.diff_main(oldBody, newBody);
dpm.diff_cleanupSemantic(diff);
return diff.map(([modifier, text], i) => {
// not using del and ins tags here as del is used for strikethrough
if (modifier < 0) {
return (<span className="mx_EditHistoryMessage_deletion" key={i}>{text}</span>);
} else if (modifier > 0) {
return (<span className="mx_EditHistoryMessage_insertion" key={i}>{text}</span>);
} 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 = <UnknownBody mxEvent={this.props.mxEvent} />;
} else {
const contentElements = HtmlUtils.bodyToHtml(content, null, {stripReplyFallback: true});
let contentElements;
if (isPlainMessage(mxEvent) && this.props.previousEdit && isPlainMessage(this.props.previousEdit)) {
contentElements = this._renderBodyDiff(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 = (