From 9d2792836f1260cf039f89a7c4e74ee41ec28621 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Mon, 21 Jan 2019 16:24:53 +0000 Subject: [PATCH] Add ImproveOSM error resolution --- modules/services/improveOSM.js | 87 ++++++++++++++++++++++++- modules/ui/improveOSM_editor.js | 108 +++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 4 deletions(-) diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index 5f2b408ea..126c6aa34 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -10,6 +10,7 @@ import { request as d3_request } from 'd3-request'; import { geoExtent } from '../geo'; import { impOsmError } from '../osm'; +import { services } from './index'; import { t } from '../util/locale'; import { utilRebind, utilTiler, utilQsString } from '../util'; @@ -189,8 +190,14 @@ export default { comments: null, error_subtype: '', error_type: k, + identifier: { // this is used to post changes to the error + wayId: feature.wayId, + fromNodeId: feature.fromNodeId, + toNodeId: feature.toNodeId + }, object_id: feature.wayId, - object_type: 'way' + object_type: 'way', + status: feature.status }); //TODO include road type in description? @@ -217,7 +224,12 @@ export default { loc: pointAverage(feature.points), comments: null, error_subtype: geoType, - error_type: k + error_type: k, + identifier: { + x: feature.x, + y: feature.y + }, + status: feature.status }); d.replacements = { @@ -251,8 +263,10 @@ export default { comments: null, error_subtype: '', error_type: k, + identifier: feature.id, object_id: via_node, - object_type: 'node' + object_type: 'node', + status: feature.status }); // Variables used in the description @@ -278,6 +292,73 @@ export default { }) }, + postUpdate: function(d, callback) { + if (!services.osm.authenticated()) { // Username required in payload + return callback({ message: 'Not Authenticated', status: -3}, d); + } + if (_erCache.inflight[d.id]) { + return callback({ message: 'Error update already inflight', status: -2 }, d); + } + + var username = services.osm.userDetails(function(err, user) { + if (err) return ''; + + return user.display_name; + }); + + var that = this; + var type = d.error_type; + var payload = {}; + + payload.username = username; + + // Each error type has different data for identification + if (type === 'ow') { + payload.roadSegments = [ d.identifier ]; + } else if (type === 'mr') { + payload.tiles = [ d.identifier ]; + } else if (type === 'tr') { + payload.targetIds = [ d.identifier ]; + } + + // Separate requests required to comment and change status + var url = _impOsmUrls[type] + '/comment'; + + // Comments don't currently work + // if (d.newComment !== undefined) { + // payload.text = d.newComment; + + // _krCache.inflight[d.id] = d3_request(url) + // .header('Content-Type', 'application/json') + // .post(payload, function(back) { + // console.log(back); + // }); + // } + + if (d.newStatus !== d.status) { + payload.status = d.newStatus; + payload.text = 'status changed'; + + _erCache.inflight[d.id] = [d3_request(url) + .header('Content-Type', 'application/json') + .post(JSON.stringify(payload), function(err) { + delete _erCache.inflight[d.id]; + + if (d.newStatus === 'INVALID') { + that.removeError(d); + } else if (d.newStatus === 'SOLVED') { + that.removeError(d); + + //TODO the identifiers are ugly and can't be used frontend, use error position instead? + // or perhaps don't track this at all? + //_erCache.closed[d.error_type + ':' + d.identifier] = true; + } + + return callback(err, d); + })]; + } + }, + // get all cached errors covering the viewport getErrors: function(projection) { var viewport = projection.clipExtent(); diff --git a/modules/ui/improveOSM_editor.js b/modules/ui/improveOSM_editor.js index ed1d5065c..d235b2101 100644 --- a/modules/ui/improveOSM_editor.js +++ b/modules/ui/improveOSM_editor.js @@ -76,7 +76,113 @@ export function uiImproveOsmEditor(context) { .merge(editor) .call(errorHeader.error(_error)) .call(quickLinks.choices(choices)) - .call(errorDetails.error(_error)); + .call(errorDetails.error(_error)) + .call(improveOsmSaveSection); + } + + function improveOsmSaveSection(selection) { + var isSelected = (_error && _error.id === context.selectedErrorID()); + var isShown = (_error && (isSelected || _error.newComment || _error.comment)); + var saveSection = selection.selectAll('.error-save') + .data( + (isShown ? [_error] : []), + function(d) { return d.id + '-' + (d.status || 0); } + ); + + // exit + saveSection.exit() + .remove(); + + // enter + var saveSectionEnter = saveSection.enter() + .append('div') + .attr('class', 'keepRight-save save-section cf'); + + // update + saveSection = saveSectionEnter + .merge(saveSection) + .call(errorSaveButtons); + } + + function errorSaveButtons(selection) { + var isSelected = (_error && _error.id === context.selectedErrorID()); + var buttonSection = selection.selectAll('.buttons') + .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); + + // exit + buttonSection.exit() + .remove(); + + // enter + var buttonEnter = buttonSection.enter() + .append('div') + .attr('class', 'buttons'); + + // Comments don't currently work + // buttonEnter + // .append('button') + // .attr('class', 'button comment-button action') + // .text(t('QA.keepRight.save_comment')); + + buttonEnter + .append('button') + .attr('class', 'button close-button action'); + + buttonEnter + .append('button') + .attr('class', 'button ignore-button action'); + + + // update + buttonSection = buttonSection + .merge(buttonEnter); + + // Comments don't currently work + // buttonSection.select('.comment-button') + // .attr('disabled', function(d) { + // return d.newComment === undefined ? true : null; + // }) + // .on('click.comment', function(d) { + // this.blur(); // avoid keeping focus on the button - #4641 + // var errorService = services.improveOSM; + // if (errorService) { + // errorService.postUpdate(d, function(err, error) { + // dispatch.call('change', error); + // }); + // } + // }); + + buttonSection.select('.close-button') + .text(function(d) { + var andComment = (d.newComment !== undefined ? '_comment' : ''); + return t('QA.keepRight.close' + andComment); + }) + .on('click.close', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var errorService = services.improveOSM; + if (errorService) { + d.newStatus = 'SOLVED'; + errorService.postUpdate(d, function(err, error) { + dispatch.call('change', error); + }); + } + }); + + buttonSection.select('.ignore-button') + .text(function(d) { + var andComment = (d.newComment !== undefined ? '_comment' : ''); + return t('QA.keepRight.ignore' + andComment); + }) + .on('click.ignore', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var errorService = services.improveOSM; + if (errorService) { + d.newStatus = 'INVALID'; + errorService.postUpdate(d, function(err, error) { + dispatch.call('change', error); + }); + } + }); } improveOsmEditor.error = function(val) {