diff --git a/data/core.yaml b/data/core.yaml index fbca6f1ab..8a1b75f3c 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -320,6 +320,7 @@ en: unsaved_changes: You have unsaved changes conflict: header: Resolve conflicting edits + count: '{num} of {total}' message: '{name}' keep_local: Keep mine keep_remote: Use theirs @@ -339,10 +340,10 @@ en: your changes or the other user's changes. merge_remote_changes: conflict: - location: Location was changed both locally and remotely. - nodelist: Nodes were changed both locally and remotely. - memberlist: Relation members were changed both locally and remotely. - tags: 'Tag "{tag}" was changed to "{local}" locally and "{remote}" remotely.' + location: 'This object was moved by both you and {user}.' + nodelist: 'Nodes were changed by both you and {user}.' + memberlist: 'Relation members were changed by both you and {user}.' + tags: 'You changed the {tag} tag to "{local}" and {user} changed it to "{remote}".' success: edited_osm: "Edited OSM!" just_edited: "You just edited OpenStreetMap!" diff --git a/dist/locales/en.json b/dist/locales/en.json index eb4313811..53c851af1 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -394,6 +394,7 @@ "unsaved_changes": "You have unsaved changes", "conflict": { "header": "Resolve conflicting edits", + "count": "{num} of {total}", "message": "{name}", "keep_local": "Keep mine", "keep_remote": "Use theirs", @@ -413,10 +414,10 @@ }, "merge_remote_changes": { "conflict": { - "location": "Location was changed both locally and remotely.", - "nodelist": "Nodes were changed both locally and remotely.", - "memberlist": "Relation members were changed both locally and remotely.", - "tags": "Tag \"{tag}\" was changed to \"{local}\" locally and \"{remote}\" remotely." + "location": "This object was moved by both you and {user}.", + "nodelist": "Nodes were changed by both you and {user}.", + "memberlist": "Relation members were changed by both you and {user}.", + "tags": "You changed the {tag} tag to \"{local}\" and {user} changed it to \"{remote}\"." } }, "success": { diff --git a/js/id/actions/merge_remote_changes.js b/js/id/actions/merge_remote_changes.js index c81a69ad6..0592ed04e 100644 --- a/js/id/actions/merge_remote_changes.js +++ b/js/id/actions/merge_remote_changes.js @@ -1,10 +1,13 @@ -iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph) { +iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) { var base = localGraph.base().entities[id], local = localGraph.entity(id), remote = remoteGraph.entity(id), option = 'safe', // 'safe', 'force_local', 'force_remote' conflicts = []; + function user(d) { + return _.isFunction(formatUser) ? formatUser(d) : d; + } function mergeLocation(target) { if (!target) return; @@ -21,7 +24,7 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph) { return target.update({loc: remote.loc}); } - conflicts.push(t('merge_remote_changes.conflict.location')); + conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) })); return; // fail merge } @@ -54,7 +57,7 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph) { } else if (_.isEqual(c.o, c.b)) { // only changed locally nodes.push.apply(nodes, c.a); } else { // changed both locally and remotely - conflicts.push(t('merge_remote_changes.conflict.nodelist')); + conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) })); return; // fail merge.. } } @@ -73,7 +76,7 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph) { return target.update({members: remote.members}); } - conflicts.push(t('merge_remote_changes.conflict.memberlist')); + conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) })); return; // fail merge } @@ -101,7 +104,7 @@ iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph) { if (remote.tags[k] !== base.tags[k]) { // tag modified remotely.. if (target.tags[k] && target.tags[k] !== remote.tags[k]) { conflicts.push(t('merge_remote_changes.conflict.tags', - { tag: k, local: target.tags[k], remote: remote.tags[k] })); + { tag: k, local: target.tags[k], remote: remote.tags[k], user: user(remote.user) })); fail = true; } else { tags[k] = remote.tags[k]; diff --git a/js/id/core/connection.js b/js/id/core/connection.js index e98627e55..12923ab69 100644 --- a/js/id/core/connection.js +++ b/js/id/core/connection.js @@ -217,28 +217,29 @@ iD.Connection = function() { }; connection.putChangeset = function(changes, comment, imageryUsed, callback) { - oauth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/create', - options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed))) - }, function(err, changeset_id) { - if (err) return callback(err); - oauth.xhr({ - method: 'POST', - path: '/api/0.6/changeset/' + changeset_id + '/upload', - options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes)) - }, function(err) { - if (err) return callback(err); - oauth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/' + changeset_id + '/close' - }, function(err) { - callback(err, changeset_id); - }); - }); - }); + callback({ responseText: 'save disabled', status: 0 }); + // oauth.xhr({ + // method: 'PUT', + // path: '/api/0.6/changeset/create', + // options: { header: { 'Content-Type': 'text/xml' } }, + // content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed))) + // }, function(err, changeset_id) { + // if (err) return callback(err); + // oauth.xhr({ + // method: 'POST', + // path: '/api/0.6/changeset/' + changeset_id + '/upload', + // options: { header: { 'Content-Type': 'text/xml' } }, + // content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes)) + // }, function(err) { + // if (err) return callback(err); + // oauth.xhr({ + // method: 'PUT', + // path: '/api/0.6/changeset/' + changeset_id + '/close' + // }, function(err) { + // callback(err, changeset_id); + // }); + // }); + // }); }; var userDetails; diff --git a/js/id/modes/save.js b/js/id/modes/save.js index fe4672fa3..949213bda 100644 --- a/js/id/modes/save.js +++ b/js/id/modes/save.js @@ -25,6 +25,10 @@ iD.modes.Save = function(context) { context.enter(iD.modes.Browse(context)); } + function formatUser(d) { + return '' + d + ''; + } + function save(e) { var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true), history = context.history(), @@ -87,16 +91,16 @@ iD.modes.Save = function(context) { var remote = altGraph.entity(id); if (local.version !== remote.version) { - var merge = iD.actions.MergeRemoteChanges, - safe = merge(id, graph, altGraph), - diff = context.perform(safe), - details = safe.conflicts(); + var action = iD.actions.MergeRemoteChanges, + merge = action(id, graph, altGraph, formatUser), + diff = context.perform(merge), + details = merge.conflicts(); if (diff.length()) { didMerge = true; } else { - var forceLocal = merge(id, graph, altGraph).withOption('force_local'), - forceRemote = merge(id, graph, altGraph).withOption('force_remote'); + var forceLocal = action(id, graph, altGraph, formatUser).withOption('force_local'), + forceRemote = action(id, graph, altGraph, formatUser).withOption('force_remote'); conflicts.push({ id: id, @@ -249,6 +253,15 @@ iD.modes.Save = function(context) { if (i === 0) zoomToEntity(d); }); + enter + .append('h4') + .style('display', function(d, i) { + return (i === 0) ? 'block': 'none'; + }) + .text(function(d, i) { + return t('save.conflict.count', { num: i+1, total: data.length }); + }); + enter .append('a') .attr('class', 'conflict-description') @@ -274,7 +287,7 @@ iD.modes.Save = function(context) { .enter() .append('li') .attr('class', 'conflict-detail-item') - .text(function(d) { return d; }); + .html(function(d) { return d; }); details .append('div') @@ -314,9 +327,9 @@ iD.modes.Save = function(context) { .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 @@ -330,6 +343,12 @@ iD.modes.Save = function(context) { .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);