More bugfixes and style updates:

* working Previous/Next buttons
* remove behaviors from save mode
  (users should not be moving nodes around or selecting at this point)
* clear hover before hovering next object
* enable save button and finished message after reviewing last conflict
* store users choice in __data__.chosen..
* default choices to `keep remote version`
* better message for delete conflicts
* fix undelete action to check localGraph (which will have the entity)
  instead of context.graph() (which may not)
This commit is contained in:
Bryan Housel
2015-02-27 15:45:43 -05:00
parent 0ed12da6fa
commit 80f5f65f63
4 changed files with 86 additions and 136 deletions
-9
View File
@@ -2504,15 +2504,6 @@ img.wiki-image {
padding: 20px 20px 0 20px;
}
.conflict-container:not(.expanded) .conflict-description:hover {
background: #ececec;
}
.conflict-container.expanded {
padding: 10px 0;
background: #f6f6f6;
}
.conflict-detail-container {
padding: 10px 20px;
}
+3 -2
View File
@@ -314,16 +314,17 @@ en:
no_changes: No changes to save.
error: Errors occurred while trying to save
status_code: "Server returned status code {code}"
status_gone: '{name} has already been deleted.'
unknown_error_details: "Please ensure you are connected to the internet."
uploading: Uploading changes to OpenStreetMap.
unsaved_changes: You have unsaved changes
conflict:
header: Resolve conflicting edits
count: 'Conflict {num} of {total}'
message: '{name}'
previous: '< Previous'
next: 'Next >'
keep_local: Keep mine
keep_remote: Use theirs
deleted: 'This object has been deleted.'
restore: Restore
delete: Leave Deleted
download_changes: Download your changes.
+3 -2
View File
@@ -388,16 +388,17 @@
"no_changes": "No changes to save.",
"error": "Errors occurred while trying to save",
"status_code": "Server returned status code {code}",
"status_gone": "{name} has already been deleted.",
"unknown_error_details": "Please ensure you are connected to the internet.",
"uploading": "Uploading changes to OpenStreetMap.",
"unsaved_changes": "You have unsaved changes",
"conflict": {
"header": "Resolve conflicting edits",
"count": "Conflict {num} of {total}",
"message": "{name}",
"previous": "< Previous",
"next": "Next >",
"keep_local": "Keep mine",
"keep_remote": "Use theirs",
"deleted": "This object has been deleted.",
"restore": "Restore",
"delete": "Leave Deleted",
"download_changes": "Download your changes.",
+80 -123
View File
@@ -36,7 +36,7 @@ iD.modes.Save = function(context) {
if (err) {
if (err.status === 410) { // Status: Gone (contains no responseText)
addDeleteConflict(id, err);
addDeleteConflict(id);
} else {
errors.push({
id: id,
@@ -57,35 +57,34 @@ iD.modes.Save = function(context) {
}
function addDeleteConflict(id, err) {
function addDeleteConflict(id) {
if (deletedIds.indexOf(id) !== -1) return;
else deletedIds.push(id);
function undelete(id) {
return function(graph) {
var entity = context.entity(id),
target = iD.Entity(entity, { version: +entity.version + 1 });
return graph.replace(target);
};
}
var local = context.graph().entity(id);
var local = localGraph.entity(id);
conflicts.push({
id: id,
msg: t('save.status_gone', { name: entityName(local) }),
details: [ t('save.status_code', { code: err.status }) ],
name: entityName(local),
details: [ t('save.conflict.deleted') ],
chosen: 1,
choices: [
choice(id, t('save.conflict.restore'), undelete(id)),
choice(id, t('save.conflict.restore'), undelete(local)),
choice(id, t('save.conflict.delete'), iD.actions.DeleteMultiple([id]))
]
],
});
function undelete(entity) {
return function(graph) {
var target = iD.Entity(entity, { version: +entity.version + 1 });
return graph.replace(target);
};
}
}
function checkConflicts(id) {
var graph = context.graph(),
local = graph.entity(id),
var local = localGraph.entity(id),
remote = remoteGraph.entity(id);
if (local.version !== remote.version) {
@@ -100,8 +99,9 @@ iD.modes.Save = function(context) {
conflicts.push({
id: id,
msg: t('save.conflict.message', { name: entityName(local) }),
name: entityName(local),
details: merge.conflicts(),
chosen: 1,
choices: [
choice(id, t('save.conflict.keep_local'), forceLocal),
choice(id, t('save.conflict.keep_remote'), forceRemote)
@@ -113,6 +113,7 @@ iD.modes.Save = function(context) {
function finalize() {
if (conflicts.length) {
conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
showConflicts();
} else if (errors.length) {
showErrors();
@@ -125,7 +126,7 @@ iD.modes.Save = function(context) {
if (err) {
errors.push({
msg: err.responseText,
details: [ t('save.status_code', {code: err.status}) ]
details: [ t('save.status_code', { code: err.status }) ]
});
showErrors();
} else {
@@ -183,10 +184,15 @@ iD.modes.Save = function(context) {
body
.append('div')
.attr('class', 'message-text conflicts-message-text');
.attr('class', 'conflict-container fillL3')
.call(showConflict, 0);
body
.call(showConflict, conflicts, 0);
.append('div')
.attr('class', 'conflicts-done')
.attr('opacity', 0)
.style('display', 'none')
.text(t('save.conflict.done'));
var buttons = body
.append('div')
@@ -194,7 +200,7 @@ iD.modes.Save = function(context) {
buttons
.append('button')
.attr('disabled', true)
.attr('disabled', conflicts.length > 1)
.attr('class', 'action conflicts-button col6')
.text(t('save.title'))
.on('click.try_again', function() {
@@ -213,38 +219,48 @@ iD.modes.Save = function(context) {
}
function showConflict(selection, data, index) {
function showConflict(selection, index) {
var parent = d3.select(selection.node().parentElement);
// enable save button if this is the last conflict being reviewed..
if (index === conflicts.length - 1) {
window.setTimeout(function() {
parent.select('.conflicts-button')
.attr('disabled', null);
parent.select('.conflicts-done')
.transition()
.attr('opacity', 1)
.style('display', 'block');
}, 250);
}
var item = selection
.selectAll('.conflict-container')
.data([data[index]]);
.selectAll('.conflict')
.data([conflicts[index]]);
var enter = item.enter()
.append('div')
.attr('class', 'conflict-container');
// .classed('expanded', function(d, i) { return i === 0; })
// .each(function(d, i) { if (i === 0) zoomToEntity(d); });
.attr('class', 'conflict');
enter
.append('h4')
.attr('class', 'conflict-count')
// .style('display', function(d, i) { return (i === 0) ? 'block' : 'none'; })
.text(t('save.conflict.count', { num: index + 1, total: data.length }));
.text(t('save.conflict.count', { num: index + 1, total: conflicts.length }));
enter
.append('a')
.attr('class', 'conflict-description')
.attr('href', '#')
.text(function(d) { return d.msg || t('save.unknown_error_details'); })
.text(function(d) { return d.name; })
.on('click', function(d) {
zoomToEntity(d.id);
// toggleExpanded(this.parentElement, d);
d3.event.preventDefault();
});
var details = enter
.append('div')
.attr('class', 'conflict-detail-container');
// .style('display', function(d, i) { return i === 0 ? 'block' : 'none'; });
details
.append('ul')
@@ -259,92 +275,44 @@ iD.modes.Save = function(context) {
details
.append('div')
.attr('class', 'conflict-choices')
.each(addChoices);
.call(addChoices);
details
.append('div')
.attr('class', 'conflict-nav-buttons joined cf')
.selectAll('button')
.data(['prev', 'next'])
.data(['previous', 'next'])
.enter()
.append('button')
.text(function(d) { return t('save.conflict.' + d); })
.attr('class', 'conflict-nav-button action col6')
.text(function(d) { return d; })
.on('click', function(d) {
.attr('disabled', function(d, i) {
return (i === 0 && index === 0) ||
(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),
sign = (i === 0 ? -1 : 1);
container
.selectAll('.conflict')
.remove();
container
.call(showConflict, index + sign);
d3.event.preventDefault();
});
// details
// .append('div')
// .attr('class', 'modal-section buttons cf')
// .append('button')
// .attr('class', 'action col4')
// .text(t('confirm.okay'))
// .on('click', function(d) {
// var container = this.parentElement.parentElement.parentElement;
// var next = container.parentElement.firstElementChild.classList.contains('expanded') ? container.nextElementSibling : container.parentElement.firstElementChild;
// window.setTimeout(function() {
// if (next) {
// toggleExpanded(next, d);
// } else {
// d3.select(container.parentElement).append('div')
// .attr('class','conflicts-done')
// .text(t('save.conflict.done'));
// d3.select('.conflicts-button')
// .attr('disabled', null);
// }
// }, 250);
// d3.select(container)
// .transition()
// .style('opacity', 0)
// .remove();
// d3.event.preventDefault();
// });
item.exit()
.remove();
function toggleExpanded(el, d) {
var error = d3.select(el),
detail = d3.select(el.getElementsByTagName('div')[0]),
count = d3.select(el.getElementsByTagName('h4')[0]),
exp = error.classed('expanded');
// Clear old expanded
enter.classed('expanded', false);
details.style('display', 'none');
// Set new
detail
.style('opacity', exp ? 1 : 0)
.transition()
.style('opacity', exp ? 0 : 1)
.style('display', exp ? 'none' : 'block');
count
.style('opacity', exp ? 1 : 0)
.transition()
.style('opacity', exp ? 0 : 1)
.style('display', exp ? 'none' : 'block');
zoomToEntity(d);
error.classed('expanded', !exp);
}
}
function addChoices(datum) {
var selection = d3.select(this)
.append('ul')
.attr('class', 'layer-list');
function addChoices(selection) {
var choices = selection
.append('ul')
.attr('class', 'layer-list')
.selectAll('li')
.data(function(d) { return d.choices || []; });
@@ -358,9 +326,10 @@ iD.modes.Save = function(context) {
label
.append('input')
.attr('type', 'radio')
.attr('name', datum.id)
.on('change', function(d) {
.attr('name', function(d) { return d.id; })
.on('change', function(d, i) {
var ul = this.parentElement.parentElement.parentElement;
ul.__data__.chosen = i;
choose(ul, d);
});
@@ -368,17 +337,17 @@ iD.modes.Save = function(context) {
.append('span')
.text(function(d) { return d.text; });
// choose first choice by default..
choices
.each(function(d, i) {
if (i === 0) choose(this.parentElement, d);
var ul = this.parentElement;
if (ul.__data__.chosen === i) choose(ul, d);
});
}
function choose(el, datum) {
function choose(ul, datum) {
if (d3.event) d3.event.preventDefault();
d3.select(el)
d3.select(ul)
.selectAll('li')
.classed('active', function(d) { return d === datum; })
.selectAll('input')
@@ -471,8 +440,10 @@ iD.modes.Save = function(context) {
}
function zoomToEntity(id) {
var entity = context.graph().hasEntity(id);
context.surface().selectAll('.hover')
.classed('hover', false);
var entity = context.graph().hasEntity(id);
if (entity) {
context.map().zoomTo(entity);
context.surface().selectAll(
@@ -499,27 +470,13 @@ iD.modes.Save = function(context) {
id: 'save'
};
var behaviors = [
iD.behavior.Hover(context),
// iD.behavior.Select(context),
iD.behavior.Lasso(context),
iD.modes.DragNode(context).behavior];
mode.enter = function() {
behaviors.forEach(function(behavior) {
context.install(behavior);
});
context.connection().authenticate(function() {
context.ui().sidebar.show(ui);
});
};
mode.exit = function() {
behaviors.forEach(function(behavior) {
context.uninstall(behavior);
});
context.ui().sidebar.hide(ui);
};