From a7e67ccfd969c6ae6f43490363d99b8c3c5bed7e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 4 Mar 2015 11:46:56 -0500 Subject: [PATCH] refactor Conflicts ui into its own component --- index.html | 1 + js/id/modes/save.js | 288 ++++++------------------------------------ js/id/ui/conflicts.js | 237 ++++++++++++++++++++++++++++++++++ test/index.html | 1 + 4 files changed, 277 insertions(+), 250 deletions(-) create mode 100644 js/id/ui/conflicts.js diff --git a/index.html b/index.html index 2cf949503..3e71faba6 100644 --- a/index.html +++ b/index.html @@ -82,6 +82,7 @@ + diff --git a/js/id/modes/save.js b/js/id/modes/save.js index 2c8ede84a..6e97c670a 100644 --- a/js/id/modes/save.js +++ b/js/id/modes/save.js @@ -62,6 +62,33 @@ iD.modes.Save = function(context) { function checkConflicts() { + function choice(id, text, action) { + return { id: id, text: text, action: function() { history.replace(action); } }; + } + function formatUser(d) { + return '' + d + ''; + } + function entityName(entity) { + return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id); + } + + function compareVersions(local, remote) { + if (local.version !== remote.version) return false; + + if (local.type === 'way') { + var children = _.union(local.nodes, remote.nodes); + + for (var i = 0; i < children.length; i++) { + var a = localGraph.hasEntity(children[i]), + b = remoteGraph.hasEntity(children[i]); + + if (!a || !b || a.version !== b.version) return false; + } + } + + return true; + } + _.each(toCheck, function(id) { var local = localGraph.entity(id), remote = remoteGraph.entity(id); @@ -95,24 +122,6 @@ iD.modes.Save = function(context) { } - function compareVersions(local, remote) { - if (local.version !== remote.version) return false; - - if (local.type === 'way') { - var children = _.union(local.nodes, remote.nodes); - - for (var i = 0; i < children.length; i++) { - var a = localGraph.hasEntity(children[i]), - b = remoteGraph.hasEntity(children[i]); - - if (!a || !b || a.version !== b.version) return false; - } - } - - return true; - } - - function finalize() { if (conflicts.length) { conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); }); @@ -149,214 +158,22 @@ iD.modes.Save = function(context) { loading.close(); - var header = selection - .append('div') - .attr('class', 'header fillL'); - - header - .append('button') - .attr('class', 'fr') - .on('click', function() { - history.pop(); - selection.remove(); - }) - .append('span') - .attr('class', 'icon close'); - - header - .append('h3') - .text(t('save.conflict.header')); - - var body = selection - .append('div') - .attr('class', 'body fillL'); - - body - .append('div') - .attr('class', 'conflicts-help') - .text(t('save.conflict.help')) - .append('a') - .attr('class', 'conflicts-download') - .text(t('save.conflict.download_changes')) - .on('click.download', function() { + selection.call(iD.ui.Conflicts(context) + .list(conflicts) + .on('download', function() { var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)), win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank'); win.focus(); - }); - - body - .append('div') - .attr('class', 'conflict-container fillL3') - .call(showConflict, 0); - - body - .append('div') - .attr('class', 'conflicts-done') - .attr('opacity', 0) - .style('display', 'none') - .text(t('save.conflict.done')); - - var buttons = body - .append('div') - .attr('class','buttons col12 joined conflicts-buttons'); - - buttons - .append('button') - .attr('disabled', conflicts.length > 1) - .attr('class', 'action conflicts-button col6') - .text(t('save.title')) - .on('click.try_again', function() { - selection.remove(); - save(e, true); - }); - - buttons - .append('button') - .attr('class', 'secondary-action conflicts-button col6') - .text(t('confirm.cancel')) - .on('click.cancel', function() { + }) + .on('cancel', function() { history.pop(); selection.remove(); - }); - } - - - 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') - .data([conflicts[index]]); - - var enter = item.enter() - .append('div') - .attr('class', 'conflict'); - - enter - .append('h4') - .attr('class', 'conflict-count') - .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.name; }) - .on('click', function(d) { - zoomToEntity(d.id); - d3.event.preventDefault(); - }); - - var details = enter - .append('div') - .attr('class', 'conflict-detail-container'); - - details - .append('ul') - .attr('class', 'conflict-detail-list') - .selectAll('li') - .data(function(d) { return d.details || []; }) - .enter() - .append('li') - .attr('class', 'conflict-detail-item') - .html(function(d) { return d; }); - - details - .append('div') - .attr('class', 'conflict-choices') - .call(addChoices); - - details - .append('div') - .attr('class', 'conflict-nav-buttons joined cf') - .selectAll('button') - .data(['previous', 'next']) - .enter() - .append('button') - .text(function(d) { return t('save.conflict.' + d); }) - .attr('class', 'conflict-nav-button action col6') - .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'), - sign = (i === 0 ? -1 : 1); - - container - .selectAll('.conflict') - .remove(); - - container - .call(showConflict, index + sign); - - d3.event.preventDefault(); - }); - - item.exit() - .remove(); - - } - - function addChoices(selection) { - var choices = selection - .append('ul') - .attr('class', 'layer-list') - .selectAll('li') - .data(function(d) { return d.choices || []; }); - - var enter = choices.enter() - .append('li') - .attr('class', 'layer'); - - var label = enter - .append('label'); - - label - .append('input') - .attr('type', 'radio') - .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); - }); - - label - .append('span') - .text(function(d) { return d.text; }); - - choices - .each(function(d, i) { - var ul = this.parentElement; - if (ul.__data__.chosen === i) choose(ul, d); - }); - } - - function choose(ul, datum) { - if (d3.event) d3.event.preventDefault(); - - d3.select(ul) - .selectAll('li') - .classed('active', function(d) { return d === datum; }) - .selectAll('input') - .property('checked', function(d) { return d === datum; }); - - datum.action(); - zoomToEntity(datum.id); + .on('save', function() { + selection.remove(); + save(e, true); + }) + ); } @@ -424,38 +241,9 @@ iD.modes.Save = function(context) { .remove(); } - - function formatUser(d) { - return '' + d + ''; - } - - function entityName(entity) { - return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id); - } - - function choice(id, text, action) { - return { - id: id, - text: text, - action: function() { history.replace(action); } - }; - } - - function zoomToEntity(id) { - context.surface().selectAll('.hover') - .classed('hover', false); - - var entity = context.graph().hasEntity(id); - if (entity) { - context.map().zoomTo(entity); - context.surface().selectAll( - iD.util.entityOrMemberSelector([entity.id], context.graph())) - .classed('hover', true); - } - } - } + function success(e, changeset_id) { context.enter(iD.modes.Browse(context) .sidebar(iD.ui.Success(context) diff --git a/js/id/ui/conflicts.js b/js/id/ui/conflicts.js new file mode 100644 index 000000000..321dd9a0b --- /dev/null +++ b/js/id/ui/conflicts.js @@ -0,0 +1,237 @@ +iD.ui.Conflicts = function(context) { + var dispatch = d3.dispatch('download', 'cancel', 'save'), + list; + + function conflicts(selection) { + var header = selection + .append('div') + .attr('class', 'header fillL'); + + header + .append('button') + .attr('class', 'fr') + .on('click', function() { dispatch.cancel(); }) + .append('span') + .attr('class', 'icon close'); + + header + .append('h3') + .text(t('save.conflict.header')); + + var body = selection + .append('div') + .attr('class', 'body fillL'); + + body + .append('div') + .attr('class', 'conflicts-help') + .text(t('save.conflict.help')) + .append('a') + .attr('class', 'conflicts-download') + .text(t('save.conflict.download_changes')) + .on('click.download', function() { dispatch.download(); }); + + body + .append('div') + .attr('class', 'conflict-container fillL3') + .call(showConflict, 0); + + body + .append('div') + .attr('class', 'conflicts-done') + .attr('opacity', 0) + .style('display', 'none') + .text(t('save.conflict.done')); + + var buttons = body + .append('div') + .attr('class','buttons col12 joined conflicts-buttons'); + + buttons + .append('button') + .attr('disabled', list.length > 1) + .attr('class', 'action conflicts-button col6') + .text(t('save.title')) + .on('click.try_again', function() { dispatch.save(); }); + + buttons + .append('button') + .attr('class', 'secondary-action conflicts-button col6') + .text(t('confirm.cancel')) + .on('click.cancel', function() { dispatch.cancel(); }); + } + + + function showConflict(selection, index) { + if (index < 0 || index >= list.length) return; + + var parent = d3.select(selection.node().parentElement); + + // enable save button if this is the last conflict being reviewed.. + if (index === list.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') + .data([list[index]]); + + var enter = item.enter() + .append('div') + .attr('class', 'conflict'); + + enter + .append('h4') + .attr('class', 'conflict-count') + .text(t('save.conflict.count', { num: index + 1, total: list.length })); + + enter + .append('a') + .attr('class', 'conflict-description') + .attr('href', '#') + .text(function(d) { return d.name; }) + .on('click', function(d) { + zoomToEntity(d.id); + d3.event.preventDefault(); + }); + + var details = enter + .append('div') + .attr('class', 'conflict-detail-container'); + + details + .append('ul') + .attr('class', 'conflict-detail-list') + .selectAll('li') + .data(function(d) { return d.details || []; }) + .enter() + .append('li') + .attr('class', 'conflict-detail-item') + .html(function(d) { return d; }); + + details + .append('div') + .attr('class', 'conflict-choices') + .call(addChoices); + + details + .append('div') + .attr('class', 'conflict-nav-buttons joined cf') + .selectAll('button') + .data(['previous', 'next']) + .enter() + .append('button') + .text(function(d) { return t('save.conflict.' + d); }) + .attr('class', 'conflict-nav-button action col6') + .attr('disabled', function(d, i) { + return (i === 0 && index === 0) || + (i === 1 && index === list.length - 1) || null; + }) + .on('click', function(d, i) { + var container = parent.select('.conflict-container'), + sign = (i === 0 ? -1 : 1); + + container + .selectAll('.conflict') + .remove(); + + container + .call(showConflict, index + sign); + + d3.event.preventDefault(); + }); + + item.exit() + .remove(); + + } + + function addChoices(selection) { + var choices = selection + .append('ul') + .attr('class', 'layer-list') + .selectAll('li') + .data(function(d) { return d.choices || []; }); + + var enter = choices.enter() + .append('li') + .attr('class', 'layer'); + + var label = enter + .append('label'); + + label + .append('input') + .attr('type', 'radio') + .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); + }); + + label + .append('span') + .text(function(d) { return d.text; }); + + choices + .each(function(d, i) { + var ul = this.parentElement; + if (ul.__data__.chosen === i) choose(ul, d); + }); + } + + function choose(ul, datum) { + if (d3.event) d3.event.preventDefault(); + + d3.select(ul) + .selectAll('li') + .classed('active', function(d) { return d === datum; }) + .selectAll('input') + .property('checked', function(d) { return d === datum; }); + + datum.action(); + zoomToEntity(datum.id); + } + + function zoomToEntity(id) { + context.surface().selectAll('.hover') + .classed('hover', false); + + var entity = context.graph().hasEntity(id); + if (entity) { + context.map().zoomTo(entity); + context.surface().selectAll( + iD.util.entityOrMemberSelector([entity.id], context.graph())) + .classed('hover', true); + } + } + + + // The conflict list should be an array of objects like: + // { + // id: id, + // name: entityName(local), + // details: merge.conflicts(), + // chosen: 1, + // choices: [ + // choice(id, keepMine, forceLocal), + // choice(id, keepTheirs, forceRemote) + // ] + // } + conflicts.list = function(_) { + if (!arguments.length) return list; + list = _; + return conflicts; + }; + + return d3.rebind(conflicts, dispatch, 'on'); +}; diff --git a/test/index.html b/test/index.html index 7643c1bc5..4f4f6510c 100644 --- a/test/index.html +++ b/test/index.html @@ -77,6 +77,7 @@ +