Better support for delete/restore

This commit is contained in:
Bryan Housel
2015-02-28 23:03:44 -05:00
parent 98665fef91
commit 78ca4b11f4
2 changed files with 76 additions and 42 deletions

View File

@@ -63,31 +63,44 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser
}
function mergeChildNodes(target, children, updates, graph) {
function mergeChildren(target, children, updates, graph) {
function isUsed(node) {
return node.hasInterestingTags() ||
graph.parentWays(node).length > 0 ||
graph.parentRelations(node).length > 0;
}
var ccount = conflicts.length;
for (var i = 0; i < children.length; i++) {
var id = children[i],
node = graph.hasEntity(id);
// remove unwanted.
// remove unused childNodes.
if (target.nodes.indexOf(id) === -1) {
if (node && !node.hasInterestingTags()) updates.removeIds.push(id);
if (node && !isUsed(node)) {
updates.removeIds.push(id);
}
continue;
}
// restore used childNodes..
var localNode = localGraph.hasEntity(id),
remoteNode = remoteGraph.hasEntity(id);
remoteNode = remoteGraph.hasEntity(id),
targetNode;
// restore wanted..
if (option === 'force_remote') {
if (remoteNode) updates.replacements.push(remoteNode);
} else if (localNode && remoteNode) {
var targetNode = iD.Entity(localNode, { version: remoteNode.version });
if (option !== 'force_local') {
targetNode = mergeLocation(remoteNode, targetNode);
if (conflicts.length !== ccount) break;
}
if (remoteNode && option === 'force_remote') {
updates.replacements.push(remoteNode);
} else if (localNode && option === 'force_local') {
targetNode = iD.Entity(localNode,
{ version: (remoteNode ? remoteNode.version : localNode.version + 1) });
updates.replacements.push(targetNode);
} else if (localNode && remoteNode && option === 'safe') {
targetNode = iD.Entity(localNode, { version: remoteNode.version });
targetNode = mergeLocation(remoteNode, targetNode);
if (conflicts.length !== ccount) break;
updates.replacements.push(targetNode);
}
}
@@ -96,6 +109,17 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser
}
function updateChildren(updates, graph) {
for (var i = 0; i < updates.replacements.length; i++) {
graph = graph.replace(updates.replacements[i]);
}
if (updates.removeIds.length) {
graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
}
return graph;
}
function mergeMembers(remote, target) {
if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
return target;
@@ -157,19 +181,42 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser
// `graph.base()` --- ... --- `remoteGraph`
//
var action = function(graph) {
var base = graph.base().entities[id],
var updates = { replacements: [], removeIds: [] },
base = graph.base().entities[id],
local = localGraph.entity(id),
remote = remoteGraph.entity(id),
target = iD.Entity(local, { version: remote.version }),
updates = { replacements: [], removeIds: [] };
remote = remoteGraph.hasEntity(id),
target;
// delete/undelete
if (!remote) {
if (option === 'force_remote') {
return iD.actions.DeleteMultiple([id])(graph);
} else if (option === 'force_local') {
target = iD.Entity(local, { version: local.version + 1 });
if (target.type === 'way') {
target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
graph = updateChildren(updates, graph);
}
return graph.replace(target);
} else {
return graph; // do nothing
}
}
// merge
target = iD.Entity(local, { version: remote.version });
if (target.type === 'node') {
target = mergeLocation(remote, target);
} else if (target.type === 'way') {
// pull in any child nodes that may not be present locally..
graph.rebase(remoteGraph.childNodes(remote), [graph], false);
target = mergeNodes(base, remote, target);
target = mergeChildNodes(target, _.union(local.nodes, remote.nodes), updates, graph);
target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
} else if (target.type === 'relation') {
target = mergeMembers(remote, target);
}
@@ -177,13 +224,7 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser
target = mergeTags(base, remote, target);
if (!conflicts.length) {
graph = graph.replace(target);
for (var i = 0; i < updates.replacements.length; i++) {
graph = graph.replace(updates.replacements[i]);
}
if (updates.removeIds.length) {
graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
}
graph = updateChildren(updates, graph).replace(target);
}
return graph;

View File

@@ -15,7 +15,6 @@ iD.modes.Save = function(context) {
remoteGraph = iD.Graph(history.base(), true),
modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
deletedIds = [],
conflicts = [],
errors = [];
@@ -36,6 +35,7 @@ iD.modes.Save = function(context) {
if (err) {
if (err.status === 410) { // Status: Gone (contains no responseText)
remoteGraph.remove(remoteGraph.hasEntity(id));
addDeleteConflict(id);
} else {
errors.push({
@@ -58,10 +58,10 @@ iD.modes.Save = function(context) {
function addDeleteConflict(id) {
if (deletedIds.indexOf(id) !== -1) return;
else deletedIds.push(id);
var local = localGraph.entity(id);
var local = localGraph.entity(id),
action = iD.actions.MergeRemoteChanges,
forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote');
conflicts.push({
id: id,
@@ -69,17 +69,10 @@ iD.modes.Save = function(context) {
details: [ t('save.conflict.deleted') ],
chosen: 1,
choices: [
choice(id, t('save.conflict.restore'), undelete(local)),
choice(id, t('save.conflict.delete'), iD.actions.DeleteMultiple([id]))
choice(id, t('save.conflict.restore'), forceLocal),
choice(id, t('save.conflict.delete'), forceRemote)
],
});
function undelete(entity) {
return function(graph) {
var target = iD.Entity(entity, { version: +entity.version + 1 });
return graph.replace(target);
};
}
}
@@ -95,8 +88,8 @@ iD.modes.Save = function(context) {
if (diff.length()) return; // merged safely
var forceLocal = action(id, localGraph, remoteGraph, formatUser).withOption('force_local'),
forceRemote = action(id, localGraph, remoteGraph, formatUser).withOption('force_remote');
var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote');
conflicts.push({
id: id,
@@ -308,7 +301,7 @@ iD.modes.Save = function(context) {
(i === 1 && index === conflicts.length - 1) || null;
})
.on('click', function(d, i) {
var container = parent.select('.conflict-container'), //d3.select(this.parentElement.parentElement.parentElement.parentElement),
var container = parent.select('.conflict-container'),
sign = (i === 0 ? -1 : 1);
container