(core) simplify document comparison code, and flesh out diff with local changes

Summary:
With recent changes to action history, we can now remove the temporary
`finalRowContent` field from change details, since all the information
we need is now in the ActionSummary.

We also now have more information about the state of the common ancestor,
which previously we could not get either from ActionSummary or from
`finalRowContent`. We take advantage of that to flesh out rendering
differences where there are some changes locally and some changes
remotely.

There's still a lot more to do, this is just one step.

I have added a link to the UI for viewing the comparison. I wouldn't
want to advertise that link until diffs are robust to name changes.

Test Plan: added test, updated tests

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2658
This commit is contained in:
Paul Fitzpatrick
2020-11-11 15:25:37 -05:00
parent 2a592d8b4d
commit c67966775b
8 changed files with 100 additions and 123 deletions

View File

@@ -24,6 +24,7 @@ const {urlState} = require('app/client/models/gristUrlState');
const {SectionFilter} = require('app/client/models/SectionFilter');
const {copyToClipboard} = require('app/client/lib/copyToClipboard');
const {setTestState} = require('app/client/lib/testState');
const {ExtraRows} = require('app/client/models/DataTableModelWithDiff');
const {createFilterMenu} = require('app/client/ui/ColumnFilterMenu');
/**
@@ -47,18 +48,10 @@ function BaseView(gristDoc, viewSectionModel, options) {
// Check if we are making a comparison with another document.
this.comparison = this.gristDoc.comparison;
if (this.comparison) {
const tableId = this.schemaModel.tableId();
// TODO: make robust to name changes.
this.leftTableDelta = this.comparison.details.leftChanges.tableDeltas[tableId];
this.rightTableDelta = this.comparison.details.rightChanges.tableDeltas[tableId];
} else {
this.rightTableDelta = null;
this.leftTableDelta = null;
}
// TODO: but accessing by tableId identifier may be problematic when the table is renamed.
this.tableModel = this.gristDoc.getTableModelMaybeWithDiff(this.schemaModel.tableId());
this.extraRows = new ExtraRows(this.schemaModel.tableId(), this.comparison && this.comparison.details);
// We use a DynamicQuerySet as the underlying RowSource, with ColumnFilters applies on top of
// it. It filters based on section linking, re-querying as needed in case of onDemand tables.
@@ -76,13 +69,9 @@ function BaseView(gristDoc, viewSectionModel, options) {
}
if (this.comparison) {
// Assign extra row ids for any rows added in the remote (right) table.
// We flip their sign to make them as belonging to the remote table only.
// TODO: if we wanted to show rows removed in the local (left) table, we'd need to
// add those too, and come up with ids to give them. Without this, there's no
// way to render an update that was made remotely to a row that was removed locally.
const extraRowIds = (this.rightTableDelta && this.rightTableDelta.addRows || [])
.map(rowId => -rowId);
// Assign extra row ids for any rows added in the remote (right) table or removed in the
// local (left) table.
const extraRowIds = this.extraRows.getExtraRows();
this._mainRowSource = rowset.ExtendedRowSource.create(this, this._mainRowSource, extraRowIds);
}

View File

@@ -704,10 +704,6 @@ GridView.prototype.buildDom = function() {
let vVerticalGridlines = v.optionsObj.prop('verticalGridlines');
let vZebraStripes = v.optionsObj.prop('zebraStripes');
const rightAddRows = new Set(this.rightTableDelta && this.rightTableDelta.addRows.map(id => -id));
const rightRemoveRows = new Set(this.rightTableDelta && this.rightTableDelta.removeRows);
const leftAddRows = new Set(this.leftTableDelta && this.leftTableDelta.addRows);
var renameCommands = {
nextField: function() {
editIndex(editIndex() + 1);
@@ -908,11 +904,8 @@ GridView.prototype.buildDom = function() {
kd.toggleClass('record-even', () => (row._index()+1) % 2 === 0 ),
self.comparison ? kd.cssClass(() => {
var rowId = row.id();
if (rightAddRows.has(rowId)) { return 'diff-remote'; }
else if (rightRemoveRows.has(rowId)) { return 'diff-parent'; }
else if (leftAddRows.has(rowId)) { return 'diff-local'; }
return '';
const rowType = self.extraRows.getRowType(row.id());
return rowType && `diff-${rowType}` || '';
}) : null,
kd.foreach(v.viewFields(), function(field) {

View File

@@ -152,26 +152,21 @@
white-space: pre-wrap;
}
.diff-change .diff-parent, .diff-change .diff-remote {
display: inline-block;
width: 50%;
}
.diff-conflict .diff-parent, .diff-conflict .diff-local, .diff-conflict .diff-remote {
display: inline-block;
width: 33.33%;
}
.diff-local {
.diff-local, .diff-local-add {
background-color: #dfdfff;
}
.diff-parent {
.diff-parent, .diff-remote-remove {
background-color: #ffdfdf;
text-decoration: line-through;
}
.diff-remote {
.diff-local-remove {
background-color: #dfdfdf;
text-decoration: line-through;
}
.diff-remote, .diff-remote-add {
background-color: #afffaf;
}