From 448119324f4e0b8a6a9e865882d7f9c8ae793c03 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 16:51:51 +0000 Subject: [PATCH 01/10] Add ability to comment on ImproveOSM issues --- modules/services/improveOSM.js | 15 ++++--- modules/ui/improveOSM_editor.js | 76 ++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index 5623a09db..6f7f47e03 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -360,17 +360,16 @@ export default { payload.targetIds = [ d.identifier ]; } - // Comments don't currently work, if they ever do in future - // it looks as though they require a separate post - // if (d.newComment !== undefined) { - // payload.text = d.newComment; - // } - - if (d.newStatus !== d.status) { + if (d.newStatus !== undefined) { payload.status = d.newStatus; payload.text = 'status changed'; } + // Comment take place of default text + if (d.newComment !== undefined) { + payload.text = d.newComment; + } + _erCache.inflightPost[d.id] = d3_request(url) .header('Content-Type', 'application/json') .post(JSON.stringify(payload), function(err) { @@ -378,6 +377,8 @@ export default { // Unsuccessful response status, keep issue open if (err.status !== 200) { return callback(err, d); } + // Just a comment, error still open + if (d.newStatus === undefined) { return callback(err, d); } that.removeError(d); diff --git a/modules/ui/improveOSM_editor.js b/modules/ui/improveOSM_editor.js index c1c29c1b5..b37107fe6 100644 --- a/modules/ui/improveOSM_editor.js +++ b/modules/ui/improveOSM_editor.js @@ -1,4 +1,5 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { select as d3_select } from 'd3-selection'; import { t } from '../util/locale'; import { services } from '../services'; @@ -12,7 +13,7 @@ import { uiTooltipHtml } from './index'; -import { utilRebind } from '../util'; +import { utilNoAuto, utilRebind } from '../util'; export function uiImproveOsmEditor(context) { @@ -97,10 +98,45 @@ export function uiImproveOsmEditor(context) { .append('div') .attr('class', 'keepRight-save save-section cf'); + saveSectionEnter + .append('h4') + .attr('class', '.error-save-header') + .text(t('QA.keepRight.comment')); + + saveSectionEnter + .append('textarea') + .attr('class', 'new-comment-input') + .attr('placeholder', t('QA.keepRight.comment_placeholder')) + .attr('maxlength', 1000) + .property('value', function(d) { return d.newComment; }) + .call(utilNoAuto) + .on('input', changeInput) + .on('blur', changeInput); + // update saveSection = saveSectionEnter .merge(saveSection) .call(errorSaveButtons); + + function changeInput() { + var input = d3_select(this); + var val = input.property('value').trim(); + + if (val === '') { + val = undefined; + } + + // store the unsaved comment with the error itself + _error = _error.update({ newComment: val }); + + var errorService = services.improveOSM; + if (errorService) { + errorService.replaceError(_error); + } + + saveSection + .call(errorSaveButtons); + } } function errorSaveButtons(selection) { @@ -117,11 +153,10 @@ export function uiImproveOsmEditor(context) { .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 comment-button action') + .text(t('QA.keepRight.save_comment')); buttonEnter .append('button') @@ -136,20 +171,19 @@ export function uiImproveOsmEditor(context) { 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('.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) { @@ -192,4 +226,4 @@ export function uiImproveOsmEditor(context) { return utilRebind(improveOsmEditor, dispatch, 'on'); -} +} \ No newline at end of file From 28bb12b7aef0793b112db7ce309b54a8b2e69f17 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 19:31:30 +0000 Subject: [PATCH 02/10] Add function to retrieve ImproveOSM comments Just need to have the UI make this call and use the data next. --- modules/services/improveOSM.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index 6f7f47e03..b688a2f72 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -329,6 +329,29 @@ export default { }); }, + // Test case: + // http://missingroads.skobbler.net/missingGeoService/retrieveComments?tileX=137495&tileY=89379 + getComments: function(d, callback) { + var key = d.error_key; + var qParams = {}; + + if (key === 'ow') { + qParams = d.identifier; + } else if (key === 'mr') { + qParams.tileX = d.identifier.x; + qParams.tileY = d.identifier.y; + } else if (key === 'tr') { + qParams.targetId = d.identifier; + } + + var url = _impOsmUrls[key] + '/retrieveComments?' + utilQsString(qParams); + + d3_json(url, function(err, data) { + d.comments = data.comments; + return callback(err, d); + }); + }, + postUpdate: function(d, callback) { if (!services.osm.authenticated()) { // Username required in payload return callback({ message: 'Not Authenticated', status: -3}, d); From 2c7b689b472fdfee9f8970a4f1ae76f7dced44bf Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 19:49:57 +0000 Subject: [PATCH 03/10] Add comments display to ImproveOSM issues --- modules/services/improveOSM.js | 2 - modules/ui/improveOSM_comments.js | 92 +++++++++++++++++++++++++++++++ modules/ui/improveOSM_editor.js | 3 + modules/ui/index.js | 3 +- 4 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 modules/ui/improveOSM_comments.js diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index b688a2f72..762ef115d 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -329,8 +329,6 @@ export default { }); }, - // Test case: - // http://missingroads.skobbler.net/missingGeoService/retrieveComments?tileX=137495&tileY=89379 getComments: function(d, callback) { var key = d.error_key; var qParams = {}; diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js new file mode 100644 index 000000000..707869b46 --- /dev/null +++ b/modules/ui/improveOSM_comments.js @@ -0,0 +1,92 @@ +import { select as d3_select } from 'd3-selection'; + +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; +import { services } from '../services'; +import { utilDetect } from '../util/detect'; + +export function uiImproveOsmComments() { + var _error; + + + function errorComments(selection) { + // make the div immediately so it appears above the buttons + var comments = selection.selectAll('.comments-container') + .data([0]); + + comments = comments.enter() + .append('div') + .attr('class', 'comments-container') + .merge(comments); + + // must retrieve comments from API before they can be displayed + services.improveOSM.getComments(_error, function(err, d) { + var commentEnter = comments.selectAll('.comment') + .data(_error.comments) + .enter() + .append('div') + .attr('class', 'comment'); + + commentEnter + .append('div') + .attr('class', 'comment-avatar') + .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); + + var mainEnter = commentEnter + .append('div') + .attr('class', 'comment-main'); + + var metadataEnter = mainEnter + .append('div') + .attr('class', 'comment-metadata'); + + metadataEnter + .append('div') + .attr('class', 'comment-author') + .each(function(d) { + var selection = d3_select(this); + var osm = services.osm; + if (osm && d.user) { + selection = selection + .append('a') + .attr('class', 'comment-author-link') + .attr('href', osm.userURL(d.username)) + .attr('tabindex', -1) + .attr('target', '_blank'); + } + selection + .text(function(d) { return d.username }); + }); + + metadataEnter + .append('div') + .attr('class', 'comment-date') + .text(function(d) { + return t('note.status.commented', { when: localeDateString(d.timestamp) }); + }); + + mainEnter + .append('div') + .attr('class', 'comment-text') + .append('p') + .text(function(d) { return d.text; }); + }); + } + + function localeDateString(s) { + if (!s) return null; + var detected = utilDetect(); + var options = { day: 'numeric', month: 'short', year: 'numeric' }; + var d = new Date(s); + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(detected.locale, options); + } + + errorComments.error = function(val) { + if (!arguments.length) return _error; + _error = val; + return errorComments; + }; + + return errorComments; +} \ No newline at end of file diff --git a/modules/ui/improveOSM_editor.js b/modules/ui/improveOSM_editor.js index b37107fe6..cd43541b7 100644 --- a/modules/ui/improveOSM_editor.js +++ b/modules/ui/improveOSM_editor.js @@ -7,6 +7,7 @@ import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; import { + uiImproveOsmComments, uiImproveOsmDetails, uiImproveOsmHeader, uiQuickLinks, @@ -19,6 +20,7 @@ import { utilNoAuto, utilRebind } from '../util'; export function uiImproveOsmEditor(context) { var dispatch = d3_dispatch('change'); var errorDetails = uiImproveOsmDetails(context); + var errorComments = uiImproveOsmComments(context); var errorHeader = uiImproveOsmHeader(context); var quickLinks = uiQuickLinks(); @@ -77,6 +79,7 @@ export function uiImproveOsmEditor(context) { .call(errorHeader.error(_error)) .call(quickLinks.choices(choices)) .call(errorDetails.error(_error)) + .call(errorComments.error(_error)) .call(improveOsmSaveSection); } diff --git a/modules/ui/index.js b/modules/ui/index.js index c1361064e..25775c454 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -28,6 +28,7 @@ export { uiFormFields } from './form_fields'; export { uiFullScreen } from './full_screen'; export { uiGeolocate } from './geolocate'; export { uiHelp } from './help'; +export { uiImproveOsmComments } from './improveOSM_comments'; export { uiImproveOsmDetails } from './improveOSM_details'; export { uiImproveOsmEditor } from './improveOSM_editor'; export { uiImproveOsmHeader } from './improveOSM_header'; @@ -72,4 +73,4 @@ export { uiUndoRedo } from './undo_redo'; export { uiVersion } from './version'; export { uiViewOnOSM } from './view_on_osm'; export { uiViewOnKeepRight } from './view_on_keepRight'; -export { uiZoom } from './zoom'; +export { uiZoom } from './zoom'; \ No newline at end of file From fa87ed75ae450a422676a7fcbc106fd6f206d4b4 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 19:57:22 +0000 Subject: [PATCH 04/10] Display ImproveOSM comments oldest to newest --- modules/services/improveOSM.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index 762ef115d..9873e1e50 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -345,7 +345,7 @@ export default { var url = _impOsmUrls[key] + '/retrieveComments?' + utilQsString(qParams); d3_json(url, function(err, data) { - d.comments = data.comments; + d.comments = data.comments.reverse(); // comments served newest to oldest return callback(err, d); }); }, From f7bef29b92c1a084bd0bd292824b80fd049cf0d2 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 20:03:55 +0000 Subject: [PATCH 05/10] Fix date conversion on ImproveOSM comments --- modules/ui/improveOSM_comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js index 707869b46..a7b75d7cd 100644 --- a/modules/ui/improveOSM_comments.js +++ b/modules/ui/improveOSM_comments.js @@ -77,7 +77,7 @@ export function uiImproveOsmComments() { if (!s) return null; var detected = utilDetect(); var options = { day: 'numeric', month: 'short', year: 'numeric' }; - var d = new Date(s); + var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms if (isNaN(d.getTime())) return null; return d.toLocaleDateString(detected.locale, options); } From 5fc87fb0a4aa2047d6dced108b10ddba7d6bec13 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 20:19:54 +0000 Subject: [PATCH 06/10] Pacify eslint (I really need to start running npm test more often before pushing) --- modules/ui/improveOSM_comments.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js index a7b75d7cd..14caa30d7 100644 --- a/modules/ui/improveOSM_comments.js +++ b/modules/ui/improveOSM_comments.js @@ -22,7 +22,7 @@ export function uiImproveOsmComments() { // must retrieve comments from API before they can be displayed services.improveOSM.getComments(_error, function(err, d) { var commentEnter = comments.selectAll('.comment') - .data(_error.comments) + .data(d.comments) .enter() .append('div') .attr('class', 'comment'); @@ -55,7 +55,7 @@ export function uiImproveOsmComments() { .attr('target', '_blank'); } selection - .text(function(d) { return d.username }); + .text(function(d) { return d.username; }); }); metadataEnter From c095600635c7da644548baf9b99138093bc57cb4 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 20:44:04 +0000 Subject: [PATCH 07/10] Use more appropriate new comment header string --- modules/ui/improveOSM_editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/improveOSM_editor.js b/modules/ui/improveOSM_editor.js index cd43541b7..54699067f 100644 --- a/modules/ui/improveOSM_editor.js +++ b/modules/ui/improveOSM_editor.js @@ -104,7 +104,7 @@ export function uiImproveOsmEditor(context) { saveSectionEnter .append('h4') .attr('class', '.error-save-header') - .text(t('QA.keepRight.comment')); + .text(t('note.newComment')); saveSectionEnter .append('textarea') From 78111955ddf34b617bf415d049b42b7fab8dbe0c Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 21:06:19 +0000 Subject: [PATCH 08/10] Fix user URL on ImproveOSM comments --- modules/ui/improveOSM_comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js index 14caa30d7..490d7b5d9 100644 --- a/modules/ui/improveOSM_comments.js +++ b/modules/ui/improveOSM_comments.js @@ -46,7 +46,7 @@ export function uiImproveOsmComments() { .each(function(d) { var selection = d3_select(this); var osm = services.osm; - if (osm && d.user) { + if (osm && d.username) { selection = selection .append('a') .attr('class', 'comment-author-link') From 0346d8060f561f41c393a2f74abc47f96ce1571c Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 21:46:17 +0000 Subject: [PATCH 09/10] Retrieve ImproveOSM comments only once This additionally fixes the comment box not clearing on submission as I wasn't updating the error in the cache (to remove the newComment property). --- modules/services/improveOSM.js | 40 +++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index 9873e1e50..83572cefc 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -330,6 +330,9 @@ export default { }, getComments: function(d, callback) { + // If comments already retrieved no need to do so again + if (d.comments !== undefined) { return callback({}, d); } + var key = d.error_key; var qParams = {}; @@ -344,8 +347,14 @@ export default { var url = _impOsmUrls[key] + '/retrieveComments?' + utilQsString(qParams); + var that = this; d3_json(url, function(err, data) { - d.comments = data.comments.reverse(); // comments served newest to oldest + // comments are served newest to oldest + var comments = data.comments ? data.comments.reverse() : []; + + that.replaceError(d.update({ + comments: comments + })); return callback(err, d); }); }, @@ -398,15 +407,30 @@ export default { // Unsuccessful response status, keep issue open if (err.status !== 200) { return callback(err, d); } - // Just a comment, error still open - if (d.newStatus === undefined) { return callback(err, d); } - that.removeError(d); + // Just a comment, update error in cache + if (d.newStatus === undefined) { + var now = new Date(); + var comments = d.comments ? d.comments : []; - // No pretty identifier, so we just use coordinates - if (d.newStatus === 'SOLVED') { - var closedID = d.loc[1].toFixed(5) + '/' + d.loc[0].toFixed(5); - _erCache.closed[key + ':' + closedID] = true; + comments.push({ + username: payload.username, + text: payload.text, + timestamp: now.getTime() / 1000 + }); + + that.replaceError(d.update({ + comments: comments, + newComment: undefined + })); + } else { + that.removeError(d); + + if (d.newStatus === 'SOLVED') { + // No pretty identifier, so we just use coordinates + var closedID = d.loc[1].toFixed(5) + '/' + d.loc[0].toFixed(5); + _erCache.closed[key + ':' + closedID] = true; + } } return callback(err, d); From a566d58f81d8fa7e347921b29073c8174166a6c5 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Tue, 5 Feb 2019 21:48:06 +0000 Subject: [PATCH 10/10] Fix error on ImproveOSM issues with no comments --- modules/ui/improveOSM_comments.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js index 490d7b5d9..47608e0c5 100644 --- a/modules/ui/improveOSM_comments.js +++ b/modules/ui/improveOSM_comments.js @@ -21,6 +21,8 @@ export function uiImproveOsmComments() { // must retrieve comments from API before they can be displayed services.improveOSM.getComments(_error, function(err, d) { + if (!d.comments) { return; } // nothing to do here + var commentEnter = comments.selectAll('.comment') .data(d.comments) .enter()