Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 66 additions & 62 deletions runbot/static/src/js/fields/tracking_value.js
Original file line number Diff line number Diff line change
@@ -1,74 +1,78 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { Message } from "@mail/core/common/message";
import { CopyButton } from "@web/core/copy_button/copy_button";
import { memoize } from "@web/core/utils/functions";

const diffMatchPatch = new diff_match_patch();

function makeDiff(text1, text2) {
const { chars1, chars2, lineArray } = diffMatchPatch.diff_linesToChars_(text1, text2);
const diffs = diffMatchPatch.diff_main(chars1, chars2, false);
diffMatchPatch.diff_charsToLines_(diffs, lineArray);
diffMatchPatch.diff_cleanupSemantic(diffs);
return diffs;
}

function prepareForRendering(diffs) {
const lines = [];
let preLineCounter = 0;
let postLineCounter = 0;
for (const { 0: diffType, 1: data } of diffs) {
for (let line of data.split("\n")) {
line = line.replace(/&/g, "&");
line = line.replace(/</g, "&lt;");
line = line.replace(/>/g, "&gt;");
//text = text.replace(/\n/g, "<br>");
//text = text.replace(/ /g, "&nbsp&nbsp");
let type;
if (diffType === -1) {
type = "removed";
preLineCounter += 1;
} else if (diffType === 0) {
type = "kept";
preLineCounter += 1;
postLineCounter += 1;
} else if (diffType === 1) {
type = "added";
postLineCounter += 1;
}
lines.push({ type, preLineCounter, postLineCounter, line });
}
}
return lines;
}

function computeDiff({ oldValue, newValue }) {
const diff = makeDiff(oldValue, newValue);
return prepareForRendering(diff);
}

patch(Message, {
components: {
...Message.components,
CopyButton,
},
});

patch(Message.prototype, {
setup() {
super.setup(...arguments);
this.kept = false;
this.state.showKept = false;
},
isMultiline(trackingValue) {
const oldValue = trackingValue.oldValue;
const newValue = trackingValue.newValue;
return ((oldValue && typeof oldValue=== 'string' && oldValue.includes('\n')) && (newValue && typeof oldValue=== 'string' && newValue.includes('\n')))
},
formatTracking(trackingFieldInfo, trackingValue) {
return super.formatTracking(trackingFieldInfo, trackingValue)

lines: memoize(computeDiff),

isMultiline({ oldValue, newValue }) {
return (
typeof oldValue === "string" &&
oldValue.includes("\n") &&
typeof oldValue === "string" &&
newValue.includes("\n")
);
},

toggleKept() {
this.kept = !this.kept;
},
copyToClipboard(trackingValue) {
return function () {
navigator.clipboard.writeText(trackingValue);
};
this.state.showKept = !this.state.showKept;
},
lines(trackingValue) {
const oldValue = trackingValue.oldValue;
const newValue = trackingValue.newValue;
const diff = this.makeDiff(oldValue, newValue);
const lines = this.prepareForRendering(diff);
return lines;
},
makeDiff(text1, text2) {
var dmp = new diff_match_patch();
var a = dmp.diff_linesToChars_(text1, text2);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmp.diff_main(lineText1, lineText2, false);
dmp.diff_charsToLines_(diffs, lineArray);
dmp.diff_cleanupSemantic(diffs);
return diffs;
},
prepareForRendering(diffs) {
var lines = [];
var pre_line_counter = 0
var post_line_counter = 0
for (var x = 0; x < diffs.length; x++) {
var diff_type = diffs[x][0];
var data = diffs[x][1];
var data_lines = data.split('\n');
for (var line_index in data_lines) {
var line = data_lines[line_index];
line = line.replace(/&/g, '&amp;');
line = line.replace(/</g, '&lt;');
line = line.replace(/>/g, '&gt;');
//text = text.replace(/\n/g, '<br>');
//text = text.replace(/ /g, '&nbsp&nbsp');
if (diff_type == -1) {
lines.push({type:'removed', pre_line_counter: pre_line_counter, post_line_counter: '-', line: line})
pre_line_counter += 1
} else if (diff_type == 0) {
lines.push({type:'kept', pre_line_counter: '', post_line_counter: post_line_counter, line: line})
pre_line_counter += 1
post_line_counter +=1
} else if (diff_type == 1) {
lines.push({type:'added', pre_line_counter: '+', post_line_counter: post_line_counter, line: line})
post_line_counter +=1
}
}
}
return lines;
},
});
56 changes: 35 additions & 21 deletions runbot/static/src/js/fields/tracking_value.scss
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
.code_diff {
white-space: nowrap;
overflow-x: auto;
font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
.code-diff {
.table {
> tbody {
font-family: var(--font-monospace);
font-size: 0.75rem;
line-height: 1;
white-space: nowrap;
}

.col_number {
background-color: #EEE;
}
> :not(caption) > * {
border-width: 0 var(--border-width);

.added {
background:#a1f1a1;
}

.removed {
background:#f3a3a3;
&:first-child {
border-top-width: var(--border-width);
}

&:last-child {
border-bottom-width: var(--border-width);
}

> * {
border-width: 0;
padding: 0.3rem;

&.diff-line-sign {
border-left-width: var(--border-width);
}
}
}
}
.code {
white-space: pre-wrap;

.diff-line-number {
width: 4ch;
}
.diff_line {
background-color: #FFF;

.diff-line-sign {
width: 2ch;
}

.diff_line:hover {
filter: brightness(92%);
.diff-line-code {
white-space: pre-wrap;
}

}
37 changes: 23 additions & 14 deletions runbot/static/src/js/fields/tracking_value.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@
<xpath expr="//li[hasclass('o-mail-Message-tracking')]" position="replace">
<li class="o-mail-Message-tracking mb-1" role="group">
<t t-if="isMultiline(trackingValue)">
<div class="btn-group btn-group-toggle mb-1">
<button class="btn btn-sm btn-outline-primary" t-on-click="copyToClipboard(trackingValue.oldValue)">Copy old value to clipboard</button>
<button class="btn btn-sm btn-outline-primary" t-on-click="toggleKept">Toggle context</button>
<button class="btn btn-sm btn-outline-primary" t-on-click="copyToClipboard(trackingValue.newValue)">Copy new value to clipboard</button>
<div class="btn-group btn-group-sm">
<CopyButton copyText="'Copy old value'" content="trackingValue.oldValue" successText="'Old value copied'" className="'btn-outline-primary'"/>
<button class="btn btn-outline-primary" t-on-click="toggleKept">
<i class="fa fa-arrows-v mx-1"/>
Toggle context
</button>
<CopyButton copyText="'Copy new value'" content="trackingValue.newValue" successText="'New value copied'" className="'btn-outline-primary'"/>
</div>
<div class="o-mail-Message-trackingField ms-1 fst-italic text-muted">(<t t-out="trackingValue.fieldInfo.changedField"/>)</div>
<div class="code_diff">
<table>
<t t-foreach="lines(trackingValue)" t-as="line" t-key="line_index">
<tr t-if="kept or line.type!=='kept'">
<td class="col_number" t-out="line.pre_line_counter"/>
<td class="col_number" t-out="line.post_line_counter"/>
<td class="code" t-att-class="line.type" t-out="line.line"/>
</tr>
</t>
<div class="code-diff table-responsive">
<table class="table table-sm table-hover caption-top">
<caption class="o-mail-Message-trackingField fst-italic">(<t t-out="trackingValue.fieldInfo.changedField"/>)</caption>
<tbody>
<t t-foreach="lines(trackingValue)" t-as="line" t-key="line_index" t-if="state.showKept or line.type !== 'kept'">
<tr t-att-class="{'table-success': line.type === 'added', 'table-danger': line.type === 'removed'}">
<td class="diff-line-number text-muted"><t t-if="['removed','kept'].includes(line.type)" t-out="line.preLineCounter"/></td>
<td class="diff-line-number text-muted"><t t-if="['added','kept'].includes(line.type)" t-out="line.postLineCounter"/></td>
<td class="diff-line-sign text-muted">
<t t-if="line.type === 'removed'">-</t>
<t t-elif="line.type === 'added'">+</t>
</td>
<td class="diff-line-code" t-att-class="line.type" t-out="line.line"/>
</tr>
</t>
</tbody>
</table>
</div>
</t>
Expand Down