From b5ece1643251b87ecc78f902185925a7ff96bcc7 Mon Sep 17 00:00:00 2001 From: Kushan Joshi <0o3ko0@gmail.com> Date: Mon, 24 Jul 2017 16:18:47 +0530 Subject: [PATCH 01/31] add requesting review --- css/80_app.css | 4 ++++ data/core.yaml | 2 ++ dist/locales/en.json | 2 ++ modules/ui/commit.js | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index a4a6b652e5..7de353237c 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3293,6 +3293,10 @@ img.tile-removing { } .mode-save .commit-info { + margin-bottom: 2px; +} + +.mode-save .request-review { margin-bottom: 10px; } diff --git a/data/core.yaml b/data/core.yaml index 44767d2fa8..c49a0afd4f 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -258,6 +258,8 @@ en: message_label: Changeset Comment upload_explanation: "The changes you upload will be visible on all maps that use OpenStreetMap data." upload_explanation_with_user: "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data." + request_review: "I would like someone to review my edits." + request_review_link: "http://wiki.openstreetmap.org/wiki/Changeset" save: Upload cancel: Cancel changes: "{count} Changes" diff --git a/dist/locales/en.json b/dist/locales/en.json index d7e12c6f5d..2eb22882d3 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -332,6 +332,8 @@ "message_label": "Changeset Comment", "upload_explanation": "The changes you upload will be visible on all maps that use OpenStreetMap data.", "upload_explanation_with_user": "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.", + "request_review": "I would like someone to review my edits.", + "request_review_link": "http://wiki.openstreetmap.org/wiki/Changeset", "save": "Upload", "cancel": "Cancel", "changes": "{count} Changes", diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 4e1512b8ec..7dca44f517 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -173,6 +173,20 @@ export function uiCommit(context) { .attr('class', 'commit-info') .html(t('commit.upload_explanation')); + var requestReview = saveSection + .append('p') + .attr('class', 'request-review') + .html( t('commit.request_review')) + .append('a') + .attr('target', '_blank') + .attr('tabindex', -1) + .call(svgIcon('#icon-out-link', 'inline')) + .attr('href', t('commit.request_review_link')); + + requestReview + .append('input') + .attr('type', 'checkbox') + .on('change', toggleRequestReview()); context.connection().userDetails(function(err, user) { if (err) return; @@ -375,6 +389,31 @@ export function uiCommit(context) { }; } + function toggleRequestReview() { + var toggled = false; + return function() { + var changeset; + if (toggled) { + changeset = updateChangeset({ + review_requested: undefined + }); + } else { + changeset = updateChangeset({ + review_requested: 'yes' + }); + } + toggled = !toggled; + var expanded = !tagSection + .selectAll('a.hide-toggle.expanded') + .empty(); + + tagSection.call(rawTagEditor + .expanded(expanded) + .readOnlyTags(readOnlyTags) + .tags(_.clone(changeset.tags))); + }; + } + function changeTags(changed) { if (changed.hasOwnProperty('comment')) { From e52fcdb562efe79e0783bc10848cfbf93e1893f2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Aug 2017 15:12:29 -0400 Subject: [PATCH 02/31] Move the rest of the RTL rules to be next to the rules they override --- css/80_app.css | 402 ++++++++++++++++++++++--------------------------- 1 file changed, 182 insertions(+), 220 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index ac382dc1b6..1292234f01 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -225,6 +225,12 @@ input[type="radio"] { margin-right: 5px; margin-top: 3px; } +[dir='rtl'] input[type="checkbox"], +[dir='rtl'] input[type="radio"] { + float: right; + margin-left: 5px; + margin-right: 0; +} /* remove bottom border radius when combobox is open */ .combobox + * textarea:focus, @@ -453,23 +459,37 @@ button.minor:hover { .button-wrap:last-of-type { padding-right: 0; } +[dir='rtl'] .button-wrap:last-of-type { + padding-left: 0; + padding-right: 10px; +} .joined button { border-radius:0; border-right: 1px solid rgba(0,0,0,.5); } +[dir='rtl'] .joined button { + border-left: 1px solid rgba(0,0,0,.5); + border-right: none; +} .fillL .joined button { border-right: 1px solid white; } .joined button:first-child { - border-radius:4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +[dir='rtl'] .joined button:first-child { + border-radius: 0 4px 4px 0; } .joined button:last-child { border-right-width: 0; - border-radius:0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +[dir='rtl'] .joined button:last-child { + border-radius: 4px 0 0 4px; } button.action { @@ -516,6 +536,10 @@ button.save.has-count .count { margin: auto; margin-left: 9.3333%; } +[dir='rtl'] button.save.has-count .count { + margin-left: auto; + margin-right: 8%; +} button.save.has-count .count::before { content: ""; @@ -532,6 +556,12 @@ button.save.has-count .count::before { border-right-style: solid; border-right-color: inherit; } +[dir='rtl'] button.save.has-count .count::before { + border-left: 6px solid rgba(255,255,255,.5); + border-right: none; + left: auto; + right: -6px; +} /* Icons */ @@ -551,6 +581,10 @@ button.save.has-count .count::before { .icon.pre-text { margin-right: 5px; } +[dir='rtl'] .icon.pre-text { + margin-left: 5px; + margin-right: 0; +} .icon.light { color: #fff; @@ -583,14 +617,20 @@ button.save.has-count .count::before { #bar { position: fixed; padding: 10px 0; - left:0; - top:0; - right:0; - height:60px; + left: 0; + top: 0; + right: 0; + height: 60px; z-index: 9; min-width: 768px; } +[dir='rtl'] #bar .spacer, +[dir='rtl'] #bar .button-wrap, +[dir='rtl'] #bar .button-wrap button { + float: right; +} + /* Header for modals / panes ------------------------------------------------------- */ @@ -609,6 +649,10 @@ button.save.has-count .count::before { overflow: hidden; padding: 20px 20px 20px 40px; } +[dir='rtl'] .header h3 { + text-align: right; + padding: 20px 40px 20px 20px; +} .header button, .modal > button { @@ -629,12 +673,21 @@ button.save.has-count .count::before { right: 0; top: 0; } +[dir='rtl'] .entity-editor-pane .header button.preset-close, +[dir='rtl'] .preset-list-pane .header button.preset-choose { + left: 0; + right: auto; +} .entity-editor-pane .header button.preset-choose { position: absolute; left: 0; top: 0; } +[dir='rtl'] .entity-editor-pane .header button.preset-choose { + left: auto; + right: 0; +} .preset-choose { font-size: 16px; @@ -649,6 +702,10 @@ button.save.has-count .count::before { height: 60px; z-index: 50; } +[dir='rtl'] .modal > button { + left: 0; + right: unset; +} .footer { position: absolute; @@ -680,6 +737,9 @@ button.save.has-count .count::before { background: #f6f6f6; -ms-user-select: element; } +[dir='rtl'] #sidebar { + float: right; +} .sidebar-component { position: absolute; @@ -736,6 +796,10 @@ button.save.has-count .count::before { top: 80px; pointer-events: none; } +[dir='rtl'] #sidebar .search-header .icon { + left: auto; + right: 10px; +} #sidebar .search-header input { position: absolute; @@ -789,6 +853,9 @@ button.save.has-count .count::before { overflow: hidden; border-left: 1px solid rgba(0, 0, 0, .1); } +[dir='rtl'] .feature-list-item .label { + text-align: right; +} .feature-list-item .label .icon { opacity: .5; @@ -816,6 +883,11 @@ button.save.has-count .count::before { color: #666; padding-left: 10px; } +[dir='rtl'] .feature-list-item .entity-name { + padding-left: 0; + padding-right: 10px; +} + /* Presets ------------------------------------------------------- */ @@ -940,6 +1012,19 @@ button.save.has-count .count::before { height: 24px; } +[dir='rtl'] .preset-list-button-wrap .preset-icon { + left: auto; + right: auto; +} + +[dir='rtl'] .preset-list-button-wrap .preset-icon-28 { + right: 16px; +} + +[dir='rtl'] .preset-list-button-wrap .preset-icon-24 { + right: 18px; +} + .preset-list-button .label { background-color: #f6f6f6; text-align: left; @@ -955,7 +1040,15 @@ button.save.has-count .count::before { overflow: hidden; border-left: 1px solid rgba(0, 0, 0, .1); border-radius: 0 3px 3px 0; - } +} +[dir='rtl'] .preset-list-button .label { + text-align: right; + left: 0; + right: 60px; + border-left: none; + border-right: 1px solid rgba(0, 0, 0, .1); + border-radius: 3px 0 0 3px; +} .preset-list-button:hover .label { background-color: #ececec; @@ -971,6 +1064,11 @@ button.save.has-count .count::before { width: 32px; background: #fafafa; } +[dir='rtl'] .preset-list-item button.tag-reference-button { + left: 0; + right: auto; + border-radius: 3px 0 0 3px; +} .preset-list-item button.tag-reference-button:hover { background: #f1f1f1; @@ -1295,6 +1393,9 @@ button.save.has-count .count::before { padding: 5px 10px; } +[dir='rtl'] .preset-input-wrap .col6 { + float: right; +} /* preset form access */ @@ -1754,6 +1855,10 @@ div.combobox { margin-left: -30px; vertical-align: top; } +[dir='rtl'] .combobox-caret { + margin-left: 0; + margin-right: -30px; +} .combobox-caret::after { content:""; @@ -2057,6 +2162,10 @@ div.full-screen > button:hover { position: fixed; z-index: 100; } +[dir='rtl'] .map-controls { + left: 0; + right: auto; +} .map-control > button { width: 40px; @@ -2078,6 +2187,9 @@ div.full-screen > button:hover { .zoombuttons button.zoom-in { border-radius: 4px 0 0 0; } +[dir='rtl'] .zoombuttons button.zoom-in { + border-radius: 0 4px 0 0; +} /* Background / Map Data Settings */ @@ -2088,6 +2200,9 @@ div.full-screen > button:hover { .background-control button { border-radius: 4px 0 0 0; } +[dir='rtl'] .background-control button { + border-radius: 0 4px 0 0; +} .map-data-control, .background-control { @@ -2151,6 +2266,10 @@ div.full-screen > button:hover { float: right; } +[dir='rtl'] .list-item-gpx-browse svg { + transform: rotateY(180deg); +} + /* make sure tooltip fits in map-control panel */ /* if too wide, placement will be wrong the first time it displays */ .layer-list li.best .tooltip-inner { @@ -2188,9 +2307,13 @@ div.full-screen > button:hover { .hide-toggle { display: block; - padding-left:12px; + padding-left: 12px; position: relative; } +[dir='rtl'] .hide-toggle { + padding-left: 0; + padding-right: 12px; +} .hide-toggle:before { content: ''; @@ -2204,6 +2327,12 @@ div.full-screen > button:hover { border-bottom: 4px solid transparent; border-left: 8px solid #7092ff; } +[dir='rtl'] .hide-toggle:before { + left: auto; + right: 0; + border-left: none; + border-right: 8px solid #7092ff; +} .hide-toggle.expanded:before { border-top: 8px solid #7092ff; @@ -2211,6 +2340,11 @@ div.full-screen > button:hover { border-right: 4px solid transparent; border-left: 4px solid transparent; } +[dir='rtl'] .hide-toggle.expanded:before { + border-left: 4px solid transparent; + border-right: 4px solid transparent; +} + /* Adjust Alignment controls */ @@ -2341,13 +2475,17 @@ div.full-screen > button:hover { .opacity-options { background: url(img/background-pattern-opacity.png) 0 0 repeat; - height:20px; - width:82px; + height: 20px; + width: 82px; position: absolute; right: 50px; top: 20px; border: 1px solid #ccc; } +[dir='rtl'] .opacity-options { + left: 50px; + right: auto; +} .opacity-options li { height: 100%; @@ -2357,8 +2495,8 @@ div.full-screen > button:hover { .opacity-options li .select-box{ position: absolute; - width:20px; - height:18px; + width: 20px; + height: 18px; z-index: 9999; } @@ -2393,6 +2531,12 @@ div.full-screen > button:hover { border-left: 1px solid #CCC; border-radius: 0; } +[dir='rtl'] .map-data-control .layer-list button, +[dir='rtl'] .background-control .layer-list button { + float: left; + border-left: none; + border-right: 1px solid #CCC; +} .map-data-control .layer-list button .icon, .background-control .layer-list button .icon { @@ -2403,6 +2547,10 @@ div.full-screen > button:hover { .background-control .layer-list button:first-of-type { border-radius: 0 3px 3px 0; } +[dir='rtl'] .map-data-control .layer-list button:first-of-type, +[dir='rtl'] .background-control .layer-list button:first-of-type { + border-radius: 3px 0 0 3px; +} .map-data-control .map-overlay, .background-control .map-overlay, @@ -2410,7 +2558,6 @@ div.full-screen > button:hover { z-index: -1; } - /* Geolocator */ .geolocate-control { @@ -2420,6 +2567,9 @@ div.full-screen > button:hover { .geolocate-control button { border-radius: 0 0 0 4px; } +[dir='rtl'] .geolocate-control button { + border-radius: 0 0 4px 0; +} .map-overlay.content { position: fixed; @@ -2429,12 +2579,20 @@ div.full-screen > button:hover { right: 0; overflow: auto; } +[dir='rtl'] .map-overlay.content { + padding: 20px 20px 20px 50px; + left: 0; + right: auto !important; +} /* Help */ .help-control button { border-radius: 0 0 0 4px; } +[dir='rtl'] .help-control button { + border-radius: 0 0 4px 0; +} .help-wrap p { font-size: 15px; @@ -3356,11 +3514,8 @@ img.tile-removing { color:#fff; } +.mode-save .request-review, .mode-save .commit-info { - margin-bottom: 2px; -} - -.mode-save .request-review { margin-bottom: 10px; } @@ -3482,8 +3637,12 @@ img.tile-removing { } .notice .zoom-to .icon { - margin-top:10px; - margin-right:10px; + margin-top: 10px; + margin-right: 10px; +} +[dir='rtl'] .notice .zoom-to .icon { + margin-left: 10px; + margin-right: 0; } /* Tooltips @@ -3695,6 +3854,10 @@ img.tile-removing { .add-point .tooltip { left: 33.3333% !important; } +[dir='rtl'] .add-point .tooltip { + left: inherit !important; +} + .add-point .tooltip .tooltip-arrow { left: 60px; } @@ -4027,204 +4190,3 @@ li.hide + li.version .badge .tooltip .tooltip-arrow { color: #7092FF; } - -/* Right-to-left localization settings */ - -[dir='rtl'] #sidebar { - float: right; -} - -[dir='rtl'] #sidebar .search-header .icon { - left: auto; - right: 10px; -} - -/* header */ -[dir='rtl'] .header h3 { - text-align: right; - padding: 20px 40px 20px 20px; -} - -[dir='rtl'] .entity-editor-pane .header button.preset-choose { - left: auto; - right: 0; -} - -[dir='rtl'] .entity-editor-pane .header button.preset-close, [dir='rtl'] .preset-list-pane .header button.preset-choose { - left: 0; - right: auto; -} - -[dir='rtl'] .map-data-control .layer-list button, [dir='rtl'] .background-control .layer-list button { - float: left; - border-left: none; - border-right: 1px solid #CCC; -} - -[dir='rtl'] .map-data-control .layer-list button:first-of-type, [dir='rtl'] .background-control .layer-list button:first-of-type { - border-radius: 3px 0 0 3px; -} - -/* search */ -[dir='rtl'] .feature-list-item .label { - text-align: right; -} - -[dir='rtl'] .feature-list-item .entity-name { - padding-left: 0; - padding-right: 10px; -} - -/* preset form */ - -[dir='rtl'] .combobox-caret { - margin-left: 0; - margin-right: -30px; -} - -[dir='rtl'] .icon.pre-text { - margin-left: 5px; - margin-right: 0; -} - -[dir='rtl'] .notice .zoom-to .icon { - margin-left: 10px; - margin-right: 0; -} - -[dir='rtl'] .preset-list-button .label { - text-align: right; - left: 0; - right: 60px; - border-left: none; - border-right: 1px solid rgba(0, 0, 0, .1); - border-radius: 3px 0 0 3px; -} - -[dir='rtl'] .preset-list-item button.tag-reference-button { - left: 0; - right: auto; - border-radius: 3px 0 0 3px; -} - -[dir='rtl'] .preset-list-button-wrap .preset-icon { - left: auto; - right: auto; -} - -[dir='rtl'] .preset-list-button-wrap .preset-icon-28 { - right: 16px; -} - -[dir='rtl'] .preset-list-button-wrap .preset-icon-24 { - right: 18px; -} - -[dir='rtl'] input[type="checkbox"], [dir='rtl'] input[type="radio"] { - float: right; - margin-left: 5px; - margin-right: 0; -} - -[dir='rtl'] .preset-input-wrap .col6 { - float: right; -} - -/* map control buttons */ -[dir='rtl'] .map-controls { - left: 0; - right: auto; -} - -[dir='rtl'] .background-control button, -[dir='rtl'] .zoombuttons button.zoom-in { - border-radius: 0 4px 0 0; -} - -[dir='rtl'] .help-control button, -[dir='rtl'] .geolocate-control button { - border-radius: 0 0 4px 0; -} - -[dir='rtl'] .list-item-gpx-browse svg { - transform: rotateY(180deg); -} - -/* map control button overlays */ -[dir='rtl'] .map-overlay { - padding: 20px 20px 20px 50px; - left: 0; - right: auto !important; -} - -[dir='rtl'] .opacity-options { - left: 50px; - right: auto; -} - -[dir='rtl'] .hide-toggle { - padding-left: 0; - padding-right: 12px; -} - -[dir='rtl'] .hide-toggle:before { - left: auto; - right: 0; - border-left: none; - border-right: 8px solid #7092ff; -} - -[dir='rtl'] .hide-toggle.expanded:before { - border-left: 4px solid transparent; - border-right: 4px solid transparent; -} - -/* navbar */ -[dir='rtl'] #bar .spacer, -[dir='rtl'] #bar .button-wrap, -[dir='rtl'] #bar .button-wrap button { - float: right; -} - -[dir='rtl'] .add-point .tooltip { - left: inherit !important; -} - -[dir='rtl'] .button-wrap:last-of-type { - padding-left: 0; - padding-right: 10px; -} - -[dir='rtl'] button.save.has-count .count { - margin-left: auto; - margin-right: 8%; -} - -[dir='rtl'] button.save.has-count .count::before { - border-left: 6px solid rgba(255,255,255,.5); - border-right: none; - left: auto; - right: -6px; -} - -[dir='rtl'] .joined button { - border-left: 1px solid rgba(0,0,0,.5); - border-right: none; -} - -[dir='rtl'] .joined button:first-child { - border-radius: 0 4px 4px 0; -} - -[dir='rtl'] .joined button:last-child { - border-radius: 4px 0 0 4px; -} - -/* modal */ -[dir='rtl'] .modal > button { - position: absolute; - left: 0; - right: unset; - top: 0; -} - From 03c8e07cb1c2ed13d760569702f0a3033027394d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Aug 2017 15:13:11 -0400 Subject: [PATCH 03/31] Remove the link to the osm wiki changeset page (doesn't really add anything and not written for newbies) --- data/core.yaml | 1 - dist/locales/en.json | 1 - modules/ui/commit.js | 9 ++------- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index c49a0afd4f..daad1e9435 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -259,7 +259,6 @@ en: upload_explanation: "The changes you upload will be visible on all maps that use OpenStreetMap data." upload_explanation_with_user: "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data." request_review: "I would like someone to review my edits." - request_review_link: "http://wiki.openstreetmap.org/wiki/Changeset" save: Upload cancel: Cancel changes: "{count} Changes" diff --git a/dist/locales/en.json b/dist/locales/en.json index 2428c59bc0..f9fa2bdfe9 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -333,7 +333,6 @@ "upload_explanation": "The changes you upload will be visible on all maps that use OpenStreetMap data.", "upload_explanation_with_user": "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.", "request_review": "I would like someone to review my edits.", - "request_review_link": "http://wiki.openstreetmap.org/wiki/Changeset", "save": "Upload", "cancel": "Cancel", "changes": "{count} Changes", diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 20c20b862a..72e23306e8 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -174,17 +174,12 @@ export function uiCommit(context) { var prose = saveSection .append('p') .attr('class', 'commit-info') - .html(t('commit.upload_explanation')); + .text(t('commit.upload_explanation')); var requestReview = saveSection .append('p') .attr('class', 'request-review') - .html( t('commit.request_review')) - .append('a') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#icon-out-link', 'inline')) - .attr('href', t('commit.request_review_link')); + .text(t('commit.request_review')); requestReview .append('input') From 9bba0ed57ef30b1dd15d378be7b03fdf449f6a69 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Aug 2017 22:04:44 -0400 Subject: [PATCH 04/31] Move request review up next to comment --- modules/ui/commit.js | 57 +++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 72e23306e8..37e745e2a6 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -65,16 +65,18 @@ export function uiCommit(context) { .append('div') .attr('class', 'body'); - var commentSection = body + + // Fields + var fieldSection = body .append('div') .attr('class', 'modal-section form-field commit-form'); - commentSection + fieldSection .append('label') .attr('class', 'form-label') .text(t('commit.message_label')); - var commentField = commentSection + var commentField = fieldSection .append('textarea') .attr('class', 'commit-form-comment') .attr('placeholder', t('commit.description_placeholder')) @@ -108,10 +110,10 @@ export function uiCommit(context) { ); }); - var clippyArea = commentSection.append('div') + var clippyArea = fieldSection.append('div') .attr('class', 'clippy-area'); - var changeSetInfo = commentSection.append('div') + var changeSetInfo = fieldSection.append('div') .attr('class', 'changeset-info'); changeSetInfo.append('a') @@ -123,6 +125,17 @@ export function uiCommit(context) { .text(t('commit.about_changeset_comments')); + var requestReview = fieldSection + .append('p') + .attr('class', 'request-review') + .text(t('commit.request_review')); + + requestReview + .append('input') + .attr('type', 'checkbox') + .on('change', toggleRequestReview()); + + // Warnings var warnings = body.selectAll('div.warning-section') .data([context.history().validate(changes)]); @@ -176,16 +189,6 @@ export function uiCommit(context) { .attr('class', 'commit-info') .text(t('commit.upload_explanation')); - var requestReview = saveSection - .append('p') - .attr('class', 'request-review') - .text(t('commit.request_review')); - - requestReview - .append('input') - .attr('type', 'checkbox') - .on('change', toggleRequestReview()); - osm.userDetails(function(err, user) { if (err) return; @@ -387,28 +390,22 @@ export function uiCommit(context) { }; } + function toggleRequestReview() { var toggled = false; + return function() { - var changeset; - if (toggled) { - changeset = updateChangeset({ - review_requested: undefined - }); - } else { - changeset = updateChangeset({ - review_requested: 'yes' - }); - } + var changeset = updateChangeset({ review_requested: (toggled ? 'yes' : undefined) }); + var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); + toggled = !toggled; - var expanded = !tagSection - .selectAll('a.hide-toggle.expanded') - .empty(); - tagSection.call(rawTagEditor + tagSection + .call(rawTagEditor .expanded(expanded) .readOnlyTags(readOnlyTags) - .tags(_.clone(changeset.tags))); + .tags(_.clone(changeset.tags)) + ); }; } From 1a9614bb9d2862caf03858f640ccadeba4f22656 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Aug 2017 22:59:39 -0400 Subject: [PATCH 05/31] Adjust styles --- css/80_app.css | 41 +++++++++++++++++++++++++++++------------ modules/ui/commit.js | 7 +++---- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 1292234f01..ef585e645e 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3506,33 +3506,50 @@ img.tile-removing { float: left; height: 12px; min-width: 12px; - font-size:12px; + font-size: 12px; line-height: 12px; - border-radius:24px; - padding:5px; - background:#7092ff; - color:#fff; + border-radius: 24px; + padding: 5px; + background: #7092ff; + color: #fff; +} + +.mode-save .field-warning { + background: #ffb; + border: 1px solid #ccc; + border-radius: 4px; + padding: 10px; +} + +.mode-save .field-warning:empty { + display: none; } +.mode-save .field-warning, +.mode-save .changeset-info, .mode-save .request-review, .mode-save .commit-info { margin-bottom: 10px; } .mode-save .changeset-list { - border:1px solid #ccc; + border: 1px solid #ccc; border-radius: 4px; - background:#fff; + background: #fff; +} + +.mode-save .warning-section { + background: #ffb; } .mode-save .warning-section .changeset-list button { - border-left: 1px solid #CCC; + border-left: 1px solid #ccc; } .mode-save .changeset-list li { position: relative; - border-top:1px solid #ccc; - padding:5px 10px; + border-top: 1px solid #ccc; + padding: 5px 10px; cursor: pointer; } @@ -3545,8 +3562,8 @@ img.tile-removing { } .changeset-list li span.count { - font-size:10px; - color:#555; + font-size: 10px; + color: #555; } .mode-save .commit-section .changeset-list button { diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 37e745e2a6..71e8ef0a9e 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -110,8 +110,8 @@ export function uiCommit(context) { ); }); - var clippyArea = fieldSection.append('div') - .attr('class', 'clippy-area'); + var commentWarning = fieldSection.append('div') + .attr('class', 'field-warning comment-warning'); var changeSetInfo = fieldSection.append('div') .attr('class', 'changeset-info'); @@ -144,7 +144,6 @@ export function uiCommit(context) { .append('div') .attr('class', 'modal-section warning-section fillL2') .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; }) - .style('background', '#ffb') .merge(warnings); warnings @@ -350,7 +349,7 @@ export function uiCommit(context) { .attr('disabled', (comment.length ? null : true)); // Warn if comment mentions Google.. - var googleWarning = clippyArea + var googleWarning = commentWarning .html('') .selectAll('a') .data(comment.match(/google/i) ? [true] : []); From b172b4602bfa59b36c4c78229968ca1a49509db0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Aug 2017 01:12:42 -0400 Subject: [PATCH 06/31] Move review_requested back to the buttons.. Make it work on tag changes --- modules/ui/commit.js | 53 ++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 71e8ef0a9e..c8b55909f1 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -125,17 +125,6 @@ export function uiCommit(context) { .text(t('commit.about_changeset_comments')); - var requestReview = fieldSection - .append('p') - .attr('class', 'request-review') - .text(t('commit.request_review')); - - requestReview - .append('input') - .attr('type', 'checkbox') - .on('change', toggleRequestReview()); - - // Warnings var warnings = body.selectAll('div.warning-section') .data([context.history().validate(changes)]); @@ -213,6 +202,18 @@ export function uiCommit(context) { }); + var requestReview = saveSection + .append('p') + .attr('class', 'request-review') + .text(t('commit.request_review')); + + var requestReviewField = requestReview + .append('input') + .attr('type', 'checkbox') + .property('checked', isReviewRequested(changeset.tags)) + .on('change', toggleRequestReview()); + + // Buttons var buttonSection = saveSection .append('div') @@ -391,14 +392,11 @@ export function uiCommit(context) { function toggleRequestReview() { - var toggled = false; - return function() { - var changeset = updateChangeset({ review_requested: (toggled ? 'yes' : undefined) }); + var rr = requestReviewField.property('checked'); + var changeset = updateChangeset({ review_requested: (rr ? 'yes' : undefined) }); var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); - toggled = !toggled; - tagSection .call(rawTagEditor .expanded(expanded) @@ -415,13 +413,34 @@ export function uiCommit(context) { changed.comment = ''; } changed.comment = changed.comment.trim(); - commentField.property('value', changed.comment); + commentField + .property('value', changed.comment); + } + + if (changed.hasOwnProperty('review_requested')) { + if (changed.review_requested === undefined) { + requestReviewField + .property('checked', false); + } else { + changed.review_requested = changed.review_requested.trim(); + requestReviewField + .property('checked', isReviewRequested(changed)); + } } + updateChangeset(changed); utilTriggerEvent(commentField, 'input'); } + function isReviewRequested(tags) { + var rr = tags.review_requested; + if (rr === undefined) return false; + rr = rr.trim().toLowerCase(); + return !(rr == '' || rr === 'no') + } + + function updateChangeset(changed) { var tags = _.clone(changeset.tags); From 59e6642796f79bc2d6152b0575ec965339b4df31 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Aug 2017 15:07:42 -0400 Subject: [PATCH 07/31] Pacify eslint --- modules/ui/commit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index c8b55909f1..bfae8b19ad 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -437,7 +437,7 @@ export function uiCommit(context) { var rr = tags.review_requested; if (rr === undefined) return false; rr = rr.trim().toLowerCase(); - return !(rr == '' || rr === 'no') + return !(rr === '' || rr === 'no'); } From 439eed4b392e551c1e433a6fd63b811b9c8b1584 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Aug 2017 17:17:46 -0400 Subject: [PATCH 08/31] WIP: refactor uiCommit into sections, introduce uiChangesetEditor uiCommit is getting kind of big as we add more to the commit pane. I'm going to split it up and put the field rendering code into a separate module, similar to how uiEntityEditor embeds uiPresetEditor for the fields. This allows us to add a few more fields that users can set on their changesets (like hashtags, source), and even hide them under a "Add field" dropdown. --- modules/ui/changeset_editor.js | 165 +++++++++++++++++++++++++++++++++ modules/ui/commit.js | 14 ++- modules/ui/index.js | 1 + 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 modules/ui/changeset_editor.js diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js new file mode 100644 index 0000000000..e3c6a4103b --- /dev/null +++ b/modules/ui/changeset_editor.js @@ -0,0 +1,165 @@ +import * as d3 from 'd3'; +import { d3combobox } from '../lib/d3.combobox.js'; +import { t } from '../util/locale'; +import { uiField } from './field'; +import { + utilGetSetValue, + utilNoAuto, + utilRebind +} from '../util'; + + +export function uiChangesetEditor(context) { + var dispatch = d3.dispatch('change'), + fieldsArr, + changeset, + tags; + + + function changesetEditor(selection) { + + if (!fieldsArr) { + var presets = context.presets(); + + fieldsArr = []; + +// FIXME: for testing + if (presets.field('brand')) { + fieldsArr.push( + uiField(context, presets.field('brand'), changeset) + ); + } + +// FIXME: for testing + presets.universal().forEach(function(field) { + fieldsArr.push( + uiField(context, field, changeset, { show: false }) + ); + }); + + fieldsArr.forEach(function(field) { + field + .on('change', function(t, onInput) { + dispatch.call('change', field, t, onInput); + }); + }); + } + + fieldsArr.forEach(function(field) { + field + .tags(tags); + }); + + var shown = fieldsArr.filter(function(field) { return field.isShown(); }), + notShown = fieldsArr.filter(function(field) { return !field.isShown(); }); + + + var form = selection.selectAll('.preset-form') + .data([0]); + + form = form.enter() + .append('div') + .attr('class', 'preset-form inspector-inner fillL3') + .merge(form); + + + var fields = form.selectAll('.wrap-form-field') + .data(shown, function(d) { return d.id; }); + + fields.exit() + .remove(); + + // Enter + var enter = fields.enter() + .append('div') + .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.id; }); + + // Update + fields = fields + .merge(enter); + + fields + .order() + .each(function(d) { + d3.select(this) + .call(d.render); + }); + + + notShown = notShown.map(function(field) { + return { + title: field.label(), + value: field.label(), + field: field + }; + }); + + + var more = selection.selectAll('.more-fields') + .data((notShown.length > 0) ? [0] : []); + + more.exit() + .remove(); + + more = more.enter() + .append('div') + .attr('class', 'more-fields') + .append('label') + .text(t('inspector.add_fields')) + .merge(more); + + + var input = more.selectAll('.value') + .data([0]); + + input.exit() + .remove(); + + input = input.enter() + .append('input') + .attr('class', 'value') + .attr('type', 'text') + .call(utilNoAuto) + .merge(input); + + input + .call(utilGetSetValue, '') + .attr('placeholder', function() { + var placeholder = []; + for (var field in notShown) { + placeholder.push(notShown[field].title); + } + return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : ''); + }) + .call(d3combobox() + .container(context.container()) + .data(notShown) + .minItems(1) + .on('accept', function (d) { + var field = d.field; + field.show = true; + changesetEditor(selection); + field.focus(); + }) + ); + } + + + changesetEditor.changeset = function(_) { + if (!arguments.length) return changeset; + changeset = _; + fieldsArr = null; + return changesetEditor; + }; + + + changesetEditor.tags = function(_) { + if (!arguments.length) return tags; + tags = _; + // Don't reset fieldsArr here. + return changesetEditor; + }; + + + return utilRebind(changesetEditor, dispatch, 'on'); +} diff --git a/modules/ui/commit.js b/modules/ui/commit.js index bfae8b19ad..7b9b7d8a7e 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -6,6 +6,7 @@ import { osmChangeset } from '../osm'; import { modeSelect } from '../modes'; import { svgIcon } from '../svg'; import { tooltip } from '../util/tooltip'; +import { uiChangesetEditor } from './changeset_editor'; import { uiRawTagEditor } from './raw_tag_editor'; import { utilDetect } from '../util/detect'; import { @@ -41,10 +42,13 @@ export function uiCommit(context) { changeset = new osmChangeset({ tags: tags }); } + var changesetEditor = uiChangesetEditor(context) + .on('change', changeTags); + var rawTagEditor = uiRawTagEditor(context) + .on('change', changeTags); var changes = context.history().changes(), summary = context.history().difference().summary(), - rawTagEditor = uiRawTagEditor(context).on('change', changeTags), comment = context.storage('comment') || '', commentDate = +context.storage('commentDate') || 0, currDate = Date.now(), @@ -65,6 +69,14 @@ export function uiCommit(context) { .append('div') .attr('class', 'body'); + body + .append('div') + .attr('class', 'modal-section changeset-editor') + .call(changesetEditor + .changeset(changeset) + .tags(tags) + ); + // Fields var fieldSection = body diff --git a/modules/ui/index.js b/modules/ui/index.js index 3abe1ebb27..874e0354d8 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -2,6 +2,7 @@ export { uiInit } from './init'; export { uiAccount } from './account'; export { uiAttribution } from './attribution'; export { uiBackground } from './background'; +export { uiChangesetEditor } from './changeset_editor'; export { uiCmd } from './cmd'; export { uiCommit } from './commit'; export { uiConfirm } from './confirm'; From e0266da08373ba64455754b7f1e5391c8023f270 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 12 Aug 2017 14:26:34 -0400 Subject: [PATCH 09/31] WIP: add changeset fields and refactor --- data/presets.yaml | 6 + data/presets/fields.json | 10 + data/presets/fields/comment.json | 5 + data/presets/fields/hashtags.json | 5 + dist/locales/en.json | 6 + modules/ui/changeset_editor.js | 33 +-- modules/ui/commit.js | 403 ++++++++++++++++++------------ 7 files changed, 282 insertions(+), 186 deletions(-) create mode 100644 data/presets/fields/comment.json create mode 100644 data/presets/fields/hashtags.json diff --git a/data/presets.yaml b/data/presets.yaml index 5895a11b2c..8c4726f57d 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -339,6 +339,9 @@ en: collection_times: # collection_times=* label: Collection Times + comment: + # comment=* + label: Changeset Comment communication_multi: # 'communication:=*' label: Communication Types @@ -571,6 +574,9 @@ en: handrail: # handrail=* label: Handrail + hashtags: + # hashtags=* + label: Hashtags height: # height=* label: Height (Meters) diff --git a/data/presets/fields.json b/data/presets/fields.json index e099f5416d..1a083f6d6d 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -437,6 +437,11 @@ "type": "text", "label": "Collection Times" }, + "comment": { + "key": "comment", + "type": "textarea", + "label": "Changeset Comment" + }, "communication_multi": { "key": "communication:", "type": "multiCombo", @@ -788,6 +793,11 @@ "type": "check", "label": "Handrail" }, + "hashtags": { + "key": "hashtags", + "type": "text", + "label": "Hashtags" + }, "height": { "key": "height", "type": "number", diff --git a/data/presets/fields/comment.json b/data/presets/fields/comment.json new file mode 100644 index 0000000000..1c213deb46 --- /dev/null +++ b/data/presets/fields/comment.json @@ -0,0 +1,5 @@ +{ + "key": "comment", + "type": "textarea", + "label": "Changeset Comment" +} diff --git a/data/presets/fields/hashtags.json b/data/presets/fields/hashtags.json new file mode 100644 index 0000000000..4ea1277d91 --- /dev/null +++ b/data/presets/fields/hashtags.json @@ -0,0 +1,5 @@ +{ + "key": "hashtags", + "type": "text", + "label": "Hashtags" +} diff --git a/dist/locales/en.json b/dist/locales/en.json index f9fa2bdfe9..9f4ddb7707 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1355,6 +1355,9 @@ "collection_times": { "label": "Collection Times" }, + "comment": { + "label": "Changeset Comment" + }, "communication_multi": { "label": "Communication Types" }, @@ -1567,6 +1570,9 @@ "handrail": { "label": "Handrail" }, + "hashtags": { + "label": "Hashtags" + }, "height": { "label": "Height (Meters)" }, diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index e3c6a4103b..935e3c4b43 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -1,4 +1,5 @@ import * as d3 from 'd3'; +import _ from 'lodash'; import { d3combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { uiField } from './field'; @@ -12,30 +13,20 @@ import { export function uiChangesetEditor(context) { var dispatch = d3.dispatch('change'), fieldsArr, - changeset, - tags; + changeset; function changesetEditor(selection) { + var tags = _.clone(changeset.tags); if (!fieldsArr) { var presets = context.presets(); - fieldsArr = []; - -// FIXME: for testing - if (presets.field('brand')) { - fieldsArr.push( - uiField(context, presets.field('brand'), changeset) - ); - } - -// FIXME: for testing - presets.universal().forEach(function(field) { - fieldsArr.push( - uiField(context, field, changeset, { show: false }) - ); - }); + fieldsArr = [ + uiField(context, presets.field('comment'), changeset), + uiField(context, presets.field('source'), changeset, { show: false }), + uiField(context, presets.field('hashtags'), changeset, { show: false }), + ]; fieldsArr.forEach(function(field) { field @@ -153,13 +144,5 @@ export function uiChangesetEditor(context) { }; - changesetEditor.tags = function(_) { - if (!arguments.length) return tags; - tags = _; - // Don't reset fieldsArr here. - return changesetEditor; - }; - - return utilRebind(changesetEditor, dispatch, 'on'); } diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 7b9b7d8a7e..b030571705 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -1,7 +1,7 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../util/locale'; -import { d3combobox } from '../lib/d3.combobox.js'; +// import { d3combobox } from '../lib/d3.combobox.js'; import { osmChangeset } from '../osm'; import { modeSelect } from '../modes'; import { svgIcon } from '../svg'; @@ -14,7 +14,7 @@ import { utilDisplayType, utilEntityOrMemberSelector, utilRebind, - utilTriggerEvent + // utilTriggerEvent } from '../util'; @@ -30,18 +30,6 @@ export function uiCommit(context) { var osm = context.connection(); if (!osm) return; - if (!changeset) { - var detected = utilDetect(), - tags = { - created_by: ('iD ' + context.version).substr(0, 255), - imagery_used: context.history().imageryUsed().join(';').substr(0, 255), - host: detected.host.substr(0, 255), - locale: detected.locale.substr(0, 255) - }; - - changeset = new osmChangeset({ tags: tags }); - } - var changesetEditor = uiChangesetEditor(context) .on('change', changeTags); var rawTagEditor = uiRawTagEditor(context) @@ -59,82 +47,110 @@ export function uiCommit(context) { comment = ''; } - selection + if (!changeset) { + var detected = utilDetect(), + tags = { + comment: comment, + created_by: ('iD ' + context.version).substr(0, 255), + imagery_used: context.history().imageryUsed().join(';').substr(0, 255), + host: detected.host.substr(0, 255), + locale: detected.locale.substr(0, 255) + }; + + changeset = new osmChangeset({ tags: tags }); + } + + + var header = selection.selectAll('.header') + .data([0]); + + header.enter() .append('div') .attr('class', 'header fillL') .append('h3') .text(t('commit.title')); - var body = selection + var body = selection.selectAll('.body') + .data([0]); + + body = body.enter() .append('div') - .attr('class', 'body'); + .attr('class', 'body') + .merge(body); + - body + // Changeset Section + var changesetSection = body.selectAll('.changeset-editor') + .data([0]); + + changesetSection = changesetSection.enter() .append('div') .attr('class', 'modal-section changeset-editor') + .merge(changesetSection); + + changesetSection .call(changesetEditor .changeset(changeset) - .tags(tags) ); - // Fields - var fieldSection = body - .append('div') - .attr('class', 'modal-section form-field commit-form'); - - fieldSection - .append('label') - .attr('class', 'form-label') - .text(t('commit.message_label')); - - var commentField = fieldSection - .append('textarea') - .attr('class', 'commit-form-comment') - .attr('placeholder', t('commit.description_placeholder')) - .attr('maxlength', 255) - .property('value', comment) - .on('input.save', change(true)) - .on('change.save', change()) - .on('blur.save', function() { - context.storage('comment', this.value); - context.storage('commentDate', Date.now()); - }); - - - commentField.node().select(); - - osm.userChangesets(function (err, changesets) { - if (err) return; - - var comments = changesets.map(function(changeset) { - return { - title: changeset.tags.comment, - value: changeset.tags.comment - }; - }); - - commentField - .call(d3combobox() - .container(context.container()) - .caseSensitive(true) - .data(_.uniqBy(comments, 'title')) - ); - }); - - var commentWarning = fieldSection.append('div') - .attr('class', 'field-warning comment-warning'); - - var changeSetInfo = fieldSection.append('div') - .attr('class', 'changeset-info'); - - changeSetInfo.append('a') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#icon-out-link', 'inline')) - .attr('href', t('commit.about_changeset_comments_link')) - .append('span') - .text(t('commit.about_changeset_comments')); + // // Fields + // var fieldSection = body + // .append('div') + // .attr('class', 'modal-section form-field commit-form'); + + // fieldSection + // .append('label') + // .attr('class', 'form-label') + // .text(t('commit.message_label')); + + // var commentField = fieldSection + // .append('textarea') + // .attr('class', 'commit-form-comment') + // .attr('placeholder', t('commit.description_placeholder')) + // .attr('maxlength', 255) + // .property('value', comment) + // .on('input.save', changeComment(true)) + // .on('change.save', changeComment()) + // .on('blur.save', function() { + // context.storage('comment', this.value); + // context.storage('commentDate', Date.now()); + // }); + + + // commentField.node().select(); + + // osm.userChangesets(function (err, changesets) { + // if (err) return; + + // var comments = changesets.map(function(changeset) { + // return { + // title: changeset.tags.comment, + // value: changeset.tags.comment + // }; + // }); + + // commentField + // .call(d3combobox() + // .container(context.container()) + // .caseSensitive(true) + // .data(_.uniqBy(comments, 'title')) + // ); + // }); + + // var commentWarning = fieldSection.append('div') + // .attr('class', 'field-warning comment-warning'); + + // var changeSetInfo = fieldSection.append('div') + // .attr('class', 'changeset-info'); + + // changeSetInfo.append('a') + // .attr('target', '_blank') + // .attr('tabindex', -1) + // .call(svgIcon('#icon-out-link', 'inline')) + // .attr('href', t('commit.about_changeset_comments_link')) + // .append('span') + // .text(t('commit.about_changeset_comments')); // Warnings @@ -180,14 +196,22 @@ export function uiCommit(context) { // Upload Explanation - var saveSection = body + var saveSection = body.selectAll('.save-section') + .data([0]); + + saveSection = saveSection.enter() .append('div') - .attr('class','modal-section save-section fillL cf'); + .attr('class','modal-section save-section fillL cf') + .merge(saveSection); + + var prose = saveSection.selectAll('.commit-info') + .data([0]); - var prose = saveSection + prose = prose.enter() .append('p') .attr('class', 'commit-info') - .text(t('commit.upload_explanation')); + .text(t('commit.upload_explanation')) + .merge(prose); osm.userDetails(function(err, user) { if (err) return; @@ -214,38 +238,61 @@ export function uiCommit(context) { }); - var requestReview = saveSection + var requestReview = saveSection.selectAll('.request-review') + .data([0]); + + requestReview = requestReview.enter() .append('p') .attr('class', 'request-review') - .text(t('commit.request_review')); + .text(t('commit.request_review')) + .merge(requestReview); - var requestReviewField = requestReview + var requestReviewField = requestReview.selectAll('input') + .data([0]); + + requestReviewField = requestReviewField.enter() .append('input') .attr('type', 'checkbox') + .merge(requestReviewField); + + requestReviewField .property('checked', isReviewRequested(changeset.tags)) - .on('change', toggleRequestReview()); + .on('change', toggleRequestReview); // Buttons - var buttonSection = saveSection + var buttonSection = saveSection.selectAll('.buttons') + .data([0]); + + // enter + var buttonEnter = buttonSection.enter() .append('div') .attr('class', 'buttons fillL cf'); - var cancelButton = buttonSection + buttonEnter .append('button') .attr('class', 'secondary-action col5 button cancel-button') - .on('click.cancel', function() { - dispatch.call('cancel'); - }); - - cancelButton .append('span') .attr('class', 'label') .text(t('commit.cancel')); - var saveButton = buttonSection + buttonEnter .append('button') .attr('class', 'action col5 button save-button') + .append('span') + .attr('class', 'label') + .text(t('commit.save')); + + // update + buttonSection = buttonSection + .merge(buttonEnter); + + buttonSection.selectAll('.cancel-button') + .on('click.cancel', function() { + dispatch.call('cancel'); + }); + + buttonSection.selectAll('.save-button') .attr('disabled', function() { var n = d3.select('.commit-form textarea').node(); return (n && n.value.length) ? null : true; @@ -254,27 +301,40 @@ export function uiCommit(context) { dispatch.call('save', this, changeset); }); - saveButton - .append('span') - .attr('class', 'label') - .text(t('commit.save')); - // Raw Tag Editor - var tagSection = body + var tagSection = body.selectAll('.tag-section.raw-tag-editor') + .data([0]); + + tagSection = tagSection.enter() .append('div') - .attr('class', 'modal-section tag-section raw-tag-editor'); + .attr('class', 'modal-section tag-section raw-tag-editor') + .merge(tagSection); + + var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); + tagSection + .call(rawTagEditor + .expanded(expanded) + .readOnlyTags(readOnlyTags) + .tags(_.clone(changeset.tags)) + ); +// TODO check this below... + // Changes - var changeSection = body + var changeSection = body.selectAll('.modal-section.commit-section') + .data([0]); + + var changeEnter = changeSection.enter() .append('div') .attr('class', 'commit-section modal-section fillL2'); - changeSection.append('h3') + changeEnter + .append('h3') .text(t('commit.changes', { count: summary.length })); - var li = changeSection + var li = changeEnter .append('ul') .attr('class', 'changeset-list') .selectAll('li') @@ -317,9 +377,9 @@ export function uiCommit(context) { .style('opacity', 1); - // Call change() off the bat, in case a changeset - // comment is recovered from localStorage - utilTriggerEvent(commentField, 'input'); + // // Call changeComment() off the bat, in case a changeset + // // comment is recovered from localStorage + // utilTriggerEvent(commentField, 'input'); function mouseover(d) { @@ -361,72 +421,87 @@ export function uiCommit(context) { d3.selectAll('.save-section .save-button') .attr('disabled', (comment.length ? null : true)); - // Warn if comment mentions Google.. - var googleWarning = commentWarning - .html('') - .selectAll('a') - .data(comment.match(/google/i) ? [true] : []); - - googleWarning.exit() - .remove(); - - googleWarning.enter() - .append('a') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#icon-alert', 'inline')) - .attr('href', t('commit.google_warning_link')) - .append('span') - .text(t('commit.google_warning')); + // // Warn if comment mentions Google.. + // var googleWarning = commentWarning + // .html('') + // .selectAll('a') + // .data(comment.match(/google/i) ? [true] : []); + + // googleWarning.exit() + // .remove(); + + // googleWarning.enter() + // .append('a') + // .attr('target', '_blank') + // .attr('tabindex', -1) + // .call(svgIcon('#icon-alert', 'inline')) + // .attr('href', t('commit.google_warning_link')) + // .append('span') + // .text(t('commit.google_warning')); } - function change(onInput) { - return function() { - var comment = commentField.property('value').trim(); - if (!onInput) { - commentField.property('value', comment); - } + // function changeComment(onInput) { + // return function() { + // var comment = commentField.property('value').trim(); + // if (!onInput) { + // commentField.property('value', comment); + // } - checkComment(comment); + // checkComment(comment); - var changeset = updateChangeset({ comment: comment }); - var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); + // var changeset = updateChangeset({ comment: comment }, onInput); + // var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); - tagSection - .call(rawTagEditor - .expanded(expanded) - .readOnlyTags(readOnlyTags) - .tags(_.clone(changeset.tags)) - ); - }; - } + // changesetSection + // .call(changesetEditor + // .changeset(changeset) + // ); + + // tagSection + // .call(rawTagEditor + // .expanded(expanded) + // .readOnlyTags(readOnlyTags) + // .tags(_.clone(changeset.tags)) + // ); + // }; + // } function toggleRequestReview() { - return function() { - var rr = requestReviewField.property('checked'); - var changeset = updateChangeset({ review_requested: (rr ? 'yes' : undefined) }); - var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); - - tagSection - .call(rawTagEditor - .expanded(expanded) - .readOnlyTags(readOnlyTags) - .tags(_.clone(changeset.tags)) - ); - }; + var rr = requestReviewField.property('checked'); + updateChangeset({ review_requested: (rr ? 'yes' : undefined) }); + + var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); + + tagSection + .call(rawTagEditor + .expanded(expanded) + .readOnlyTags(readOnlyTags) + .tags(_.clone(changeset.tags)) + ); } - function changeTags(changed) { + function changeTags(changed, onInput) { + // if (changed.hasOwnProperty('comment')) { + // if (changed.comment === undefined) { + // changed.comment = ''; + // } + // changed.comment = changed.comment.trim(); + // commentField + // .property('value', changed.comment); + // } if (changed.hasOwnProperty('comment')) { if (changed.comment === undefined) { changed.comment = ''; } - changed.comment = changed.comment.trim(); - commentField - .property('value', changed.comment); + // if (!onInput) { + // changed.comment = changed.comment.trim(); + // } + // commentField + // .property('value', changed.comment); + } if (changed.hasOwnProperty('review_requested')) { @@ -440,8 +515,14 @@ export function uiCommit(context) { } } - updateChangeset(changed); - utilTriggerEvent(commentField, 'input'); + updateChangeset(changed, onInput); + + if (changed.hasOwnProperty('comment')) { + checkComment(changed.comment); + } + + commit(selection); + // utilTriggerEvent(commentField, 'input'); } @@ -453,7 +534,7 @@ export function uiCommit(context) { } - function updateChangeset(changed) { + function updateChangeset(changed, onInput) { var tags = _.clone(changeset.tags); _.forEach(changed, function(v, k) { @@ -461,7 +542,9 @@ export function uiCommit(context) { if (readOnlyTags.indexOf(k) !== -1) return; if (k !== '' && v !== undefined) { - tags[k] = v.trim().substr(0, 255); + if (!onInput) { + tags[k] = v.trim().substr(0, 255); + } } else { delete tags[k]; } @@ -470,8 +553,6 @@ export function uiCommit(context) { if (!_.isEqual(changeset.tags, tags)) { changeset = changeset.update({ tags: tags }); } - - return changeset; } } From fba6871e5ce9de5045dbdda3c93c11efc6ec2627 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 13 Aug 2017 12:08:49 -0400 Subject: [PATCH 10/31] WIP: persist uiRawTagEditor, uiChangesetEditor between renders --- modules/ui/commit.js | 175 ++++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 84 deletions(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index b030571705..f416236d83 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -23,18 +23,21 @@ var readOnlyTags = ['created_by', 'imagery_used', 'host', 'locale']; export function uiCommit(context) { - var dispatch = d3.dispatch('cancel', 'save'); + var dispatch = d3.dispatch('cancel', 'save'), + _selection; + + var changesetEditor = uiChangesetEditor(context) + .on('change', changeTags); + var rawTagEditor = uiRawTagEditor(context) + .on('change', changeTags); function commit(selection) { + _selection = selection; + var osm = context.connection(); if (!osm) return; - var changesetEditor = uiChangesetEditor(context) - .on('change', changeTags); - var rawTagEditor = uiRawTagEditor(context) - .on('change', changeTags); - var changes = context.history().changes(), summary = context.history().difference().summary(), comment = context.storage('comment') || '', @@ -320,7 +323,7 @@ export function uiCommit(context) { ); -// TODO check this below... +// TODO check this below (maybe refactor to own module)... // Changes var changeSection = body.selectAll('.modal-section.commit-section') @@ -416,29 +419,29 @@ export function uiCommit(context) { } - function checkComment(comment) { - // Save button disabled if there is no comment.. - d3.selectAll('.save-section .save-button') - .attr('disabled', (comment.length ? null : true)); - - // // Warn if comment mentions Google.. - // var googleWarning = commentWarning - // .html('') - // .selectAll('a') - // .data(comment.match(/google/i) ? [true] : []); - - // googleWarning.exit() - // .remove(); - - // googleWarning.enter() - // .append('a') - // .attr('target', '_blank') - // .attr('tabindex', -1) - // .call(svgIcon('#icon-alert', 'inline')) - // .attr('href', t('commit.google_warning_link')) - // .append('span') - // .text(t('commit.google_warning')); - } + // function checkComment(comment) { + // // Save button disabled if there is no comment.. + // d3.selectAll('.save-section .save-button') + // .attr('disabled', (comment.length ? null : true)); + + // // // Warn if comment mentions Google.. + // // var googleWarning = commentWarning + // // .html('') + // // .selectAll('a') + // // .data(comment.match(/google/i) ? [true] : []); + + // // googleWarning.exit() + // // .remove(); + + // // googleWarning.enter() + // // .append('a') + // // .attr('target', '_blank') + // // .attr('tabindex', -1) + // // .call(svgIcon('#icon-alert', 'inline')) + // // .attr('href', t('commit.google_warning_link')) + // // .append('span') + // // .text(t('commit.google_warning')); + // } // function changeComment(onInput) { @@ -482,82 +485,86 @@ export function uiCommit(context) { ); } + } - function changeTags(changed, onInput) { - // if (changed.hasOwnProperty('comment')) { - // if (changed.comment === undefined) { - // changed.comment = ''; - // } + function changeTags(changed, onInput) { + // if (changed.hasOwnProperty('comment')) { + // if (changed.comment === undefined) { + // changed.comment = ''; + // } + // changed.comment = changed.comment.trim(); + // commentField + // .property('value', changed.comment); + // } + if (changed.hasOwnProperty('comment')) { + if (changed.comment === undefined) { + changed.comment = ''; + } + // if (!onInput) { // changed.comment = changed.comment.trim(); - // commentField - // .property('value', changed.comment); // } - if (changed.hasOwnProperty('comment')) { - if (changed.comment === undefined) { - changed.comment = ''; - } - // if (!onInput) { - // changed.comment = changed.comment.trim(); - // } - // commentField - // .property('value', changed.comment); + // commentField + // .property('value', changed.comment); - } + } - if (changed.hasOwnProperty('review_requested')) { - if (changed.review_requested === undefined) { - requestReviewField - .property('checked', false); - } else { - changed.review_requested = changed.review_requested.trim(); - requestReviewField - .property('checked', isReviewRequested(changed)); - } - } + // if (changed.hasOwnProperty('review_requested')) { + // if (changed.review_requested === undefined) { + // requestReviewField + // .property('checked', false); + // } else { + // changed.review_requested = changed.review_requested.trim(); + // requestReviewField + // .property('checked', isReviewRequested(changed)); + // } + // } - updateChangeset(changed, onInput); + updateChangeset(changed, onInput); - if (changed.hasOwnProperty('comment')) { - checkComment(changed.comment); - } + // if (changed.hasOwnProperty('comment')) { + // checkComment(changed.comment); + // } - commit(selection); - // utilTriggerEvent(commentField, 'input'); + if (_selection) { + _selection.call(commit); } + // utilTriggerEvent(commentField, 'input'); + } - function isReviewRequested(tags) { - var rr = tags.review_requested; - if (rr === undefined) return false; - rr = rr.trim().toLowerCase(); - return !(rr === '' || rr === 'no'); - } + function isReviewRequested(tags) { + var rr = tags.review_requested; + if (rr === undefined) return false; + rr = rr.trim().toLowerCase(); + return !(rr === '' || rr === 'no'); + } - function updateChangeset(changed, onInput) { - var tags = _.clone(changeset.tags); + function updateChangeset(changed, onInput) { + var tags = _.clone(changeset.tags); - _.forEach(changed, function(v, k) { - k = k.trim().substr(0, 255); - if (readOnlyTags.indexOf(k) !== -1) return; + _.forEach(changed, function(v, k) { + k = k.trim().substr(0, 255); + if (readOnlyTags.indexOf(k) !== -1) return; - if (k !== '' && v !== undefined) { - if (!onInput) { - tags[k] = v.trim().substr(0, 255); - } + if (k !== '' && v !== undefined) { + if (onInput) { + tags[k] = v; } else { - delete tags[k]; + tags[k] = v.trim().substr(0, 255); } - }); - - if (!_.isEqual(changeset.tags, tags)) { - changeset = changeset.update({ tags: tags }); + } else { + delete tags[k]; } - } + }); + if (!_.isEqual(changeset.tags, tags)) { + changeset = changeset.update({ tags: tags }); + } } + commit.reset = function() { changeset = null; }; From 78e874d6c4d79710d530e03c800a591660fe3f4a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 13 Aug 2017 23:24:09 -0400 Subject: [PATCH 11/31] Allow fields to work w/o entity, add options to disable buttons --- modules/ui/field.js | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/modules/ui/field.js b/modules/ui/field.js index 148369f1d0..ee70f98c55 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -10,7 +10,10 @@ import { utilRebind } from '../util'; export function uiField(context, presetField, entity, options) { options = _.extend({ show: true, - wrap: true + wrap: true, + remove: true, + revert: true, + info: true }, options); var dispatch = d3.dispatch('change'), @@ -24,7 +27,7 @@ export function uiField(context, presetField, entity, options) { dispatch.call('change', field, t, onInput); }); - if (field.impl.entity) { + if (entity && field.impl.entity) { field.impl.entity(entity); } @@ -34,6 +37,7 @@ export function uiField(context, presetField, entity, options) { function isModified() { + if (!entity) return false; var original = context.graph().base().entities[entity.id]; return _.some(field.keys, function(key) { return original ? tags[key] !== original.tags[key] : tags[key]; @@ -51,6 +55,7 @@ export function uiField(context, presetField, entity, options) { function revert(d) { d3.event.stopPropagation(); d3.event.preventDefault(); + if (!entity) return false; var original = context.graph().base().entities[entity.id], t = {}; @@ -96,19 +101,23 @@ export function uiField(context, presetField, entity, options) { .append('div') .attr('class', 'form-label-button-wrap'); - wrap - .append('button') - .attr('class', 'remove-icon') - .attr('tabindex', -1) - .call(svgIcon('#operation-delete')); - - wrap - .append('button') - .attr('class', 'modified-icon') - .attr('tabindex', -1) - .call( - (textDirection === 'rtl') ? svgIcon('#icon-redo') : svgIcon('#icon-undo') - ); + if (options.remove) { + wrap + .append('button') + .attr('class', 'remove-icon') + .attr('tabindex', -1) + .call(svgIcon('#operation-delete')); + } + + if (options.revert) { + wrap + .append('button') + .attr('class', 'modified-icon') + .attr('tabindex', -1) + .call( + (textDirection === 'rtl') ? svgIcon('#icon-redo') : svgIcon('#icon-undo') + ); + } } @@ -126,7 +135,7 @@ export function uiField(context, presetField, entity, options) { .classed('modified', isModified()) .classed('present', isPresent()) .each(function(d) { - if (options.wrap) { + if (options.wrap && options.info) { var referenceKey = d.key; if (d.type === 'multiCombo') { // lookup key without the trailing ':' referenceKey = referenceKey.replace(/:$/, ''); @@ -141,7 +150,7 @@ export function uiField(context, presetField, entity, options) { d3.select(this) .call(d.impl); - if (options.wrap) { + if (options.wrap && options.info) { d3.select(this) .call(reference.body) .select('.form-label-button-wrap') From 9b37ac7b6c8a4cdc5adbfa156b4be457fc06a3db Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 13 Aug 2017 23:25:37 -0400 Subject: [PATCH 12/31] Fix errors with changeset fields and warnings, improve styles --- css/80_app.css | 3 ++ modules/ui/changeset_editor.js | 43 +++++++++++++++++------- modules/ui/commit.js | 60 ++++++++++++++++++++-------------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index ef585e645e..028d6967ac 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1370,6 +1370,9 @@ button.save.has-count .count::before { padding: 0 20px 20px 20px; font-weight: bold; } +.changeset-editor .more-fields { + padding: 15px 20px 0 20px; +} .more-fields label { display: flex; diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index 935e3c4b43..a157e58f1a 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -1,5 +1,4 @@ import * as d3 from 'd3'; -import _ from 'lodash'; import { d3combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { uiField } from './field'; @@ -13,19 +12,26 @@ import { export function uiChangesetEditor(context) { var dispatch = d3.dispatch('change'), fieldsArr, - changeset; + tags, + changesetId; function changesetEditor(selection) { - var tags = _.clone(changeset.tags); + render(selection); + } + + + function render(selection) { + var initial = false; if (!fieldsArr) { + initial = true; var presets = context.presets(); fieldsArr = [ - uiField(context, presets.field('comment'), changeset), - uiField(context, presets.field('source'), changeset, { show: false }), - uiField(context, presets.field('hashtags'), changeset, { show: false }), + uiField(context, presets.field('comment'), null, { show: true, revert: false }), + uiField(context, presets.field('source'), null, { show: false, revert: false }), + uiField(context, presets.field('hashtags'), null, { show: false, revert: false }), ]; fieldsArr.forEach(function(field) { @@ -50,7 +56,7 @@ export function uiChangesetEditor(context) { form = form.enter() .append('div') - .attr('class', 'preset-form inspector-inner fillL3') + .attr('class', 'preset-form') .merge(form); @@ -77,6 +83,12 @@ export function uiChangesetEditor(context) { }); + if (initial) { + var node = d3.select('#preset-input-comment').node(); + node && node.focus(); + node && node.select(); + } + notShown = notShown.map(function(field) { return { title: field.label(), @@ -129,16 +141,25 @@ export function uiChangesetEditor(context) { .on('accept', function (d) { var field = d.field; field.show = true; - changesetEditor(selection); + render(selection); field.focus(); }) ); } - changesetEditor.changeset = function(_) { - if (!arguments.length) return changeset; - changeset = _; + changesetEditor.tags = function(_) { + if (!arguments.length) return tags; + tags = _; + // Don't reset fieldsArr here. + return changesetEditor; + }; + + + changesetEditor.changesetID = function(_) { + if (!arguments.length) return changesetId; + if (changesetId === _) return changesetEditor; + changesetId = _; fieldsArr = null; return changesetEditor; }; diff --git a/modules/ui/commit.js b/modules/ui/commit.js index f416236d83..e93fac86a7 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -50,19 +50,21 @@ export function uiCommit(context) { comment = ''; } + var tags; if (!changeset) { - var detected = utilDetect(), - tags = { - comment: comment, - created_by: ('iD ' + context.version).substr(0, 255), - imagery_used: context.history().imageryUsed().join(';').substr(0, 255), - host: detected.host.substr(0, 255), - locale: detected.locale.substr(0, 255) - }; + var detected = utilDetect(); + tags = { + comment: comment, + created_by: ('iD ' + context.version).substr(0, 255), + imagery_used: context.history().imageryUsed().join(';').substr(0, 255), + host: detected.host.substr(0, 255), + locale: detected.locale.substr(0, 255) + }; changeset = new osmChangeset({ tags: tags }); } + tags = _.clone(changeset.tags); var header = selection.selectAll('.header') .data([0]); @@ -93,7 +95,8 @@ export function uiCommit(context) { changesetSection .call(changesetEditor - .changeset(changeset) + .changesetID(changeset.id) + .tags(tags) ); @@ -157,41 +160,50 @@ export function uiCommit(context) { // Warnings - var warnings = body.selectAll('div.warning-section') - .data([context.history().validate(changes)]); + var warnings = context.history().validate(changes); + var warningSection = body.selectAll('div.warning-section') + .data(warnings.length ? [0] : []); - warnings = warnings.enter() + warningSection.exit() + .remove(); + + var warningEnter = warningSection.enter() .append('div') - .attr('class', 'modal-section warning-section fillL2') - .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; }) - .merge(warnings); + .attr('class', 'modal-section warning-section fillL2'); - warnings + warningEnter .append('h3') .text(t('commit.warnings')); - warnings + warningEnter .append('ul') .attr('class', 'changeset-list'); - var warningLi = warnings.select('ul').selectAll('li') - .data(function(d) { return d; }); + warningSection = warningEnter + .merge(warningSection); + + + var warningItems = warningSection.select('ul').selectAll('li') + .data(warnings); + + warningItems.exit() + .remove(); - warningLi = warningLi.enter() + warningItems = warningItems.enter() .append('li') .on('mouseover', mouseover) .on('mouseout', mouseout) .on('click', warningClick) - .merge(warningLi); + .merge(warningItems); - warningLi + warningItems .call(svgIcon('#icon-alert', 'pre-text')); - warningLi + warningItems .append('strong') .text(function(d) { return d.message; }); - warningLi.filter(function(d) { return d.tooltip; }) + warningItems.filter(function(d) { return d.tooltip; }) .call(tooltip() .title(function(d) { return d.tooltip; }) .placement('top') From 4971a204094de667a88d2fa8eaa8fbb99eb184f3 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 13 Aug 2017 23:42:06 -0400 Subject: [PATCH 13/31] Pacify eslint --- modules/ui/changeset_editor.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index a157e58f1a..b65c8f7cc7 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -85,8 +85,10 @@ export function uiChangesetEditor(context) { if (initial) { var node = d3.select('#preset-input-comment').node(); - node && node.focus(); - node && node.select(); + if (node) { + node.focus(); + node.select(); + } } notShown = notShown.map(function(field) { From 347177fa7ec4c909b54b78b635741ce6e6310516 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 14 Aug 2017 12:13:43 -0400 Subject: [PATCH 14/31] Restore placeholders --- data/core.yaml | 2 -- data/presets.yaml | 4 ++++ data/presets/fields.json | 6 ++++-- data/presets/fields/comment.json | 3 ++- data/presets/fields/source.json | 5 +++-- dist/locales/en.json | 8 ++++---- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index daad1e9435..9f45ae26c3 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -254,8 +254,6 @@ en: rateLimit: The API is limiting anonymous connections. You can fix this by logging in. commit: title: Upload to OpenStreetMap - description_placeholder: Brief description of your contributions (required) - message_label: Changeset Comment upload_explanation: "The changes you upload will be visible on all maps that use OpenStreetMap data." upload_explanation_with_user: "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data." request_review: "I would like someone to review my edits." diff --git a/data/presets.yaml b/data/presets.yaml index 8c4726f57d..1e4967d014 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -342,6 +342,8 @@ en: comment: # comment=* label: Changeset Comment + # comment field placeholder + placeholder: Brief description of your contributions (required) communication_multi: # 'communication:=*' label: Communication Types @@ -1225,6 +1227,8 @@ en: source: # source=* label: Source + # source field placeholder + placeholder: 'survey, local knowledge, aerial imagery' sport: # sport=* label: Sports diff --git a/data/presets/fields.json b/data/presets/fields.json index 1a083f6d6d..dcb437a6cf 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -440,7 +440,8 @@ "comment": { "key": "comment", "type": "textarea", - "label": "Changeset Comment" + "label": "Changeset Comment", + "placeholder": "Brief description of your contributions (required)" }, "communication_multi": { "key": "communication:", @@ -1637,7 +1638,8 @@ "type": "text", "icon": "source", "universal": true, - "label": "Source" + "label": "Source", + "placeholder": "survey, local knowledge, aerial imagery" }, "sport_ice": { "key": "sport", diff --git a/data/presets/fields/comment.json b/data/presets/fields/comment.json index 1c213deb46..71072e1d45 100644 --- a/data/presets/fields/comment.json +++ b/data/presets/fields/comment.json @@ -1,5 +1,6 @@ { "key": "comment", "type": "textarea", - "label": "Changeset Comment" + "label": "Changeset Comment", + "placeholder": "Brief description of your contributions (required)" } diff --git a/data/presets/fields/source.json b/data/presets/fields/source.json index 0cad9fa73a..e1464d99bf 100644 --- a/data/presets/fields/source.json +++ b/data/presets/fields/source.json @@ -3,5 +3,6 @@ "type": "text", "icon": "source", "universal": true, - "label": "Source" -} \ No newline at end of file + "label": "Source", + "placeholder": "survey, local knowledge, aerial imagery" +} diff --git a/dist/locales/en.json b/dist/locales/en.json index 9f4ddb7707..c8a5570a12 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -328,8 +328,6 @@ }, "commit": { "title": "Upload to OpenStreetMap", - "description_placeholder": "Brief description of your contributions (required)", - "message_label": "Changeset Comment", "upload_explanation": "The changes you upload will be visible on all maps that use OpenStreetMap data.", "upload_explanation_with_user": "The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.", "request_review": "I would like someone to review my edits.", @@ -1356,7 +1354,8 @@ "label": "Collection Times" }, "comment": { - "label": "Changeset Comment" + "label": "Changeset Comment", + "placeholder": "Brief description of your contributions (required)" }, "communication_multi": { "label": "Communication Types" @@ -2101,7 +2100,8 @@ "label": "Type" }, "source": { - "label": "Source" + "label": "Source", + "placeholder": "survey, local knowledge, aerial imagery" }, "sport_ice": { "label": "Sports" From e6eff907a2eaeec283245bdf06afb24e438d573d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 14 Aug 2017 12:46:10 -0400 Subject: [PATCH 15/31] Restore the previous changeset dropdown --- modules/ui/changeset_editor.js | 42 ++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index b65c8f7cc7..3bdfd44c0a 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -1,4 +1,5 @@ import * as d3 from 'd3'; +import _ from 'lodash'; import { d3combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { uiField } from './field'; @@ -83,13 +84,6 @@ export function uiChangesetEditor(context) { }); - if (initial) { - var node = d3.select('#preset-input-comment').node(); - if (node) { - node.focus(); - node.select(); - } - } notShown = notShown.map(function(field) { return { @@ -147,6 +141,40 @@ export function uiChangesetEditor(context) { field.focus(); }) ); + + + + if (initial) { + var commentField = d3.select('#preset-input-comment'), + commentNode = commentField.node(); + + if (commentNode) { + commentNode.focus(); + commentNode.select(); + } + + var osm = context.connection(); + if (osm) { + osm.userChangesets(function (err, changesets) { + if (err) return; + + var comments = changesets.map(function(changeset) { + return { + title: changeset.tags.comment, + value: changeset.tags.comment + }; + }); + + commentField + .call(d3combobox() + .container(context.container()) + .caseSensitive(true) + .data(_.uniqBy(comments, 'title')) + ); + }); + } + } + } From 194b56c0f0eda724d6859dce9ff3385e1354c2f5 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 14 Aug 2017 22:59:15 -0400 Subject: [PATCH 16/31] Restore the comment google warning --- modules/ui/changeset_editor.js | 42 ++++++++- modules/ui/commit.js | 153 +-------------------------------- 2 files changed, 43 insertions(+), 152 deletions(-) diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index 3bdfd44c0a..36cc855c1a 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -2,6 +2,7 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { d3combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; +import { svgIcon } from '../svg'; import { uiField } from './field'; import { utilGetSetValue, @@ -84,7 +85,6 @@ export function uiChangesetEditor(context) { }); - notShown = notShown.map(function(field) { return { title: field.label(), @@ -143,7 +143,6 @@ export function uiChangesetEditor(context) { ); - if (initial) { var commentField = d3.select('#preset-input-comment'), commentNode = commentField.node(); @@ -175,6 +174,45 @@ export function uiChangesetEditor(context) { } } + var matches = tags.comment.match(/google/i); + var commentWarning = d3.select('.form-field-comment').selectAll('.comment-warning') + .data(matches ? [0] : []); + + commentWarning.exit() + .transition() + .style('opacity', 0) + .remove(); + + var commentEnter = commentWarning.enter() + .insert('div', '.tag-reference-body') + .attr('class', 'field-warning comment-warning') + .style('opacity', 0); + + commentEnter + .append('a') + .attr('target', '_blank') + .attr('tabindex', -1) + .call(svgIcon('#icon-alert', 'inline')) + .attr('href', t('commit.google_warning_link')) + .append('span') + .text(t('commit.google_warning')); + + commentEnter + .transition() + .style('opacity', 1); + + + // var changeSetInfo = fieldSection.append('div') + // .attr('class', 'changeset-info'); + + // changeSetInfo.append('a') + // .attr('target', '_blank') + // .attr('tabindex', -1) + // .call(svgIcon('#icon-out-link', 'inline')) + // .attr('href', t('commit.about_changeset_comments_link')) + // .append('span') + // .text(t('commit.about_changeset_comments')); + } diff --git a/modules/ui/commit.js b/modules/ui/commit.js index e93fac86a7..83732a9432 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -1,7 +1,6 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../util/locale'; -// import { d3combobox } from '../lib/d3.combobox.js'; import { osmChangeset } from '../osm'; import { modeSelect } from '../modes'; import { svgIcon } from '../svg'; @@ -13,8 +12,7 @@ import { utilDisplayName, utilDisplayType, utilEntityOrMemberSelector, - utilRebind, - // utilTriggerEvent + utilRebind } from '../util'; @@ -100,65 +98,6 @@ export function uiCommit(context) { ); - // // Fields - // var fieldSection = body - // .append('div') - // .attr('class', 'modal-section form-field commit-form'); - - // fieldSection - // .append('label') - // .attr('class', 'form-label') - // .text(t('commit.message_label')); - - // var commentField = fieldSection - // .append('textarea') - // .attr('class', 'commit-form-comment') - // .attr('placeholder', t('commit.description_placeholder')) - // .attr('maxlength', 255) - // .property('value', comment) - // .on('input.save', changeComment(true)) - // .on('change.save', changeComment()) - // .on('blur.save', function() { - // context.storage('comment', this.value); - // context.storage('commentDate', Date.now()); - // }); - - - // commentField.node().select(); - - // osm.userChangesets(function (err, changesets) { - // if (err) return; - - // var comments = changesets.map(function(changeset) { - // return { - // title: changeset.tags.comment, - // value: changeset.tags.comment - // }; - // }); - - // commentField - // .call(d3combobox() - // .container(context.container()) - // .caseSensitive(true) - // .data(_.uniqBy(comments, 'title')) - // ); - // }); - - // var commentWarning = fieldSection.append('div') - // .attr('class', 'field-warning comment-warning'); - - // var changeSetInfo = fieldSection.append('div') - // .attr('class', 'changeset-info'); - - // changeSetInfo.append('a') - // .attr('target', '_blank') - // .attr('tabindex', -1) - // .call(svgIcon('#icon-out-link', 'inline')) - // .attr('href', t('commit.about_changeset_comments_link')) - // .append('span') - // .text(t('commit.about_changeset_comments')); - - // Warnings var warnings = context.history().validate(changes); var warningSection = body.selectAll('div.warning-section') @@ -309,7 +248,7 @@ export function uiCommit(context) { buttonSection.selectAll('.save-button') .attr('disabled', function() { - var n = d3.select('.commit-form textarea').node(); + var n = d3.select('#preset-input-comment').node(); return (n && n.value.length) ? null : true; }) .on('click.save', function() { @@ -392,11 +331,6 @@ export function uiCommit(context) { .style('opacity', 1); - // // Call changeComment() off the bat, in case a changeset - // // comment is recovered from localStorage - // utilTriggerEvent(commentField, 'input'); - - function mouseover(d) { if (d.entity) { context.surface().selectAll( @@ -431,58 +365,6 @@ export function uiCommit(context) { } - // function checkComment(comment) { - // // Save button disabled if there is no comment.. - // d3.selectAll('.save-section .save-button') - // .attr('disabled', (comment.length ? null : true)); - - // // // Warn if comment mentions Google.. - // // var googleWarning = commentWarning - // // .html('') - // // .selectAll('a') - // // .data(comment.match(/google/i) ? [true] : []); - - // // googleWarning.exit() - // // .remove(); - - // // googleWarning.enter() - // // .append('a') - // // .attr('target', '_blank') - // // .attr('tabindex', -1) - // // .call(svgIcon('#icon-alert', 'inline')) - // // .attr('href', t('commit.google_warning_link')) - // // .append('span') - // // .text(t('commit.google_warning')); - // } - - - // function changeComment(onInput) { - // return function() { - // var comment = commentField.property('value').trim(); - // if (!onInput) { - // commentField.property('value', comment); - // } - - // checkComment(comment); - - // var changeset = updateChangeset({ comment: comment }, onInput); - // var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty(); - - // changesetSection - // .call(changesetEditor - // .changeset(changeset) - // ); - - // tagSection - // .call(rawTagEditor - // .expanded(expanded) - // .readOnlyTags(readOnlyTags) - // .tags(_.clone(changeset.tags)) - // ); - // }; - // } - - function toggleRequestReview() { var rr = requestReviewField.property('checked'); updateChangeset({ review_requested: (rr ? 'yes' : undefined) }); @@ -499,48 +381,19 @@ export function uiCommit(context) { } + function changeTags(changed, onInput) { - // if (changed.hasOwnProperty('comment')) { - // if (changed.comment === undefined) { - // changed.comment = ''; - // } - // changed.comment = changed.comment.trim(); - // commentField - // .property('value', changed.comment); - // } if (changed.hasOwnProperty('comment')) { if (changed.comment === undefined) { changed.comment = ''; } - // if (!onInput) { - // changed.comment = changed.comment.trim(); - // } - // commentField - // .property('value', changed.comment); - } - // if (changed.hasOwnProperty('review_requested')) { - // if (changed.review_requested === undefined) { - // requestReviewField - // .property('checked', false); - // } else { - // changed.review_requested = changed.review_requested.trim(); - // requestReviewField - // .property('checked', isReviewRequested(changed)); - // } - // } - updateChangeset(changed, onInput); - // if (changed.hasOwnProperty('comment')) { - // checkComment(changed.comment); - // } - if (_selection) { _selection.call(commit); } - // utilTriggerEvent(commentField, 'input'); } From d7a07208e1bba30a2de33f3b18c47c2431feb9fb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 14 Aug 2017 23:14:05 -0400 Subject: [PATCH 17/31] Restore saving comment to localstorage --- modules/ui/commit.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 83732a9432..7e57519e6f 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -387,6 +387,10 @@ export function uiCommit(context) { if (changed.comment === undefined) { changed.comment = ''; } + if (!onInput) { + context.storage('comment', changed.comment); + context.storage('commentDate', Date.now()); + } } updateChangeset(changed, onInput); From ba3f82314168f5d95c2b2162cc1be9d1e45cf70a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 01:50:52 -0400 Subject: [PATCH 18/31] Split out commitWarnings and commitChanges sections into smaller modules --- modules/ui/commit.js | 160 ++-------------------------------- modules/ui/commit_changes.js | 116 ++++++++++++++++++++++++ modules/ui/commit_warnings.js | 95 ++++++++++++++++++++ modules/ui/index.js | 2 + 4 files changed, 222 insertions(+), 151 deletions(-) create mode 100644 modules/ui/commit_changes.js create mode 100644 modules/ui/commit_warnings.js diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 7e57519e6f..db773a2bb7 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -2,18 +2,12 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../util/locale'; import { osmChangeset } from '../osm'; -import { modeSelect } from '../modes'; -import { svgIcon } from '../svg'; -import { tooltip } from '../util/tooltip'; import { uiChangesetEditor } from './changeset_editor'; +import { uiCommitChanges } from './commit_changes'; +import { uiCommitWarnings } from './commit_warnings'; import { uiRawTagEditor } from './raw_tag_editor'; import { utilDetect } from '../util/detect'; -import { - utilDisplayName, - utilDisplayType, - utilEntityOrMemberSelector, - utilRebind -} from '../util'; +import { utilRebind } from '../util'; var changeset; @@ -28,6 +22,8 @@ export function uiCommit(context) { .on('change', changeTags); var rawTagEditor = uiRawTagEditor(context) .on('change', changeTags); + var commitChanges = uiCommitChanges(context); + var commitWarnings = uiCommitWarnings(context); function commit(selection) { @@ -36,9 +32,7 @@ export function uiCommit(context) { var osm = context.connection(); if (!osm) return; - var changes = context.history().changes(), - summary = context.history().difference().summary(), - comment = context.storage('comment') || '', + var comment = context.storage('comment') || '', commentDate = +context.storage('commentDate') || 0, currDate = Date.now(), cutoff = 2 * 86400 * 1000; // 2 days @@ -99,54 +93,7 @@ export function uiCommit(context) { // Warnings - var warnings = context.history().validate(changes); - var warningSection = body.selectAll('div.warning-section') - .data(warnings.length ? [0] : []); - - warningSection.exit() - .remove(); - - var warningEnter = warningSection.enter() - .append('div') - .attr('class', 'modal-section warning-section fillL2'); - - warningEnter - .append('h3') - .text(t('commit.warnings')); - - warningEnter - .append('ul') - .attr('class', 'changeset-list'); - - warningSection = warningEnter - .merge(warningSection); - - - var warningItems = warningSection.select('ul').selectAll('li') - .data(warnings); - - warningItems.exit() - .remove(); - - warningItems = warningItems.enter() - .append('li') - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .on('click', warningClick) - .merge(warningItems); - - warningItems - .call(svgIcon('#icon-alert', 'pre-text')); - - warningItems - .append('strong') - .text(function(d) { return d.message; }); - - warningItems.filter(function(d) { return d.tooltip; }) - .call(tooltip() - .title(function(d) { return d.tooltip; }) - .placement('top') - ); + body.call(commitWarnings); // Upload Explanation @@ -274,95 +221,8 @@ export function uiCommit(context) { ); -// TODO check this below (maybe refactor to own module)... - - // Changes - var changeSection = body.selectAll('.modal-section.commit-section') - .data([0]); - - var changeEnter = changeSection.enter() - .append('div') - .attr('class', 'commit-section modal-section fillL2'); - - changeEnter - .append('h3') - .text(t('commit.changes', { count: summary.length })); - - var li = changeEnter - .append('ul') - .attr('class', 'changeset-list') - .selectAll('li') - .data(summary); - - li = li.enter() - .append('li') - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .on('click', zoomToEntity) - .merge(li); - - li.each(function(d) { - d3.select(this) - .call(svgIcon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType)); - }); - - li.append('span') - .attr('class', 'change-type') - .text(function(d) { return t('commit.' + d.changeType) + ' '; }); - - li.append('strong') - .attr('class', 'entity-type') - .text(function(d) { - var matched = context.presets().match(d.entity, d.graph); - return (matched && matched.name()) || utilDisplayType(d.entity.id); - }); - - li.append('span') - .attr('class', 'entity-name') - .text(function(d) { - var name = utilDisplayName(d.entity) || '', - string = ''; - if (name !== '') string += ':'; - return string += ' ' + name; - }); - - li.style('opacity', 0) - .transition() - .style('opacity', 1); - - - function mouseover(d) { - if (d.entity) { - context.surface().selectAll( - utilEntityOrMemberSelector([d.entity.id], context.graph()) - ).classed('hover', true); - } - } - - - function mouseout() { - context.surface().selectAll('.hover') - .classed('hover', false); - } - - - function warningClick(d) { - if (d.entity) { - context.map().zoomTo(d.entity); - context.enter(modeSelect(context, [d.entity.id])); - } - } - - - function zoomToEntity(change) { - var entity = change.entity; - if (change.changeType !== 'deleted' && - context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') { - context.map().zoomTo(entity); - context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())) - .classed('hover', true); - } - } + // Change summary + body.call(commitChanges); function toggleRequestReview() { @@ -378,7 +238,6 @@ export function uiCommit(context) { .tags(_.clone(changeset.tags)) ); } - } @@ -433,7 +292,6 @@ export function uiCommit(context) { } - commit.reset = function() { changeset = null; }; diff --git a/modules/ui/commit_changes.js b/modules/ui/commit_changes.js new file mode 100644 index 0000000000..e47fd81b2e --- /dev/null +++ b/modules/ui/commit_changes.js @@ -0,0 +1,116 @@ +import * as d3 from 'd3'; +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; +import { + utilDisplayName, + utilDisplayType, + utilEntityOrMemberSelector +} from '../util'; + + +export function uiCommitChanges(context) { + + function commitChanges(selection) { + + var summary = context.history().difference().summary(); + + var container = selection.selectAll('.modal-section.commit-section') + .data([0]); + + var containerEnter = container.enter() + .append('div') + .attr('class', 'commit-section modal-section fillL2'); + + containerEnter + .append('h3') + .text(t('commit.changes', { count: summary.length })); + + containerEnter + .append('ul') + .attr('class', 'changeset-list'); + + container = containerEnter + .merge(container); + + + var items = container.select('ul').selectAll('li') + .data(summary); + + var itemsEnter = items.enter() + .append('li') + .attr('class', 'change-item'); + + itemsEnter + .each(function(d) { + d3.select(this) + .call(svgIcon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType)); + }); + + itemsEnter + .append('span') + .attr('class', 'change-type') + .text(function(d) { return t('commit.' + d.changeType) + ' '; }); + + itemsEnter + .append('strong') + .attr('class', 'entity-type') + .text(function(d) { + var matched = context.presets().match(d.entity, d.graph); + return (matched && matched.name()) || utilDisplayType(d.entity.id); + }); + + itemsEnter + .append('span') + .attr('class', 'entity-name') + .text(function(d) { + var name = utilDisplayName(d.entity) || '', + string = ''; + if (name !== '') { + string += ':'; + } + return string += ' ' + name; + }); + + itemsEnter + .style('opacity', 0) + .transition() + .style('opacity', 1); + + items = itemsEnter + .merge(items); + + items + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .on('click', zoomToEntity); + + + function mouseover(d) { + if (d.entity) { + context.surface().selectAll( + utilEntityOrMemberSelector([d.entity.id], context.graph()) + ).classed('hover', true); + } + } + + + function mouseout() { + context.surface().selectAll('.hover') + .classed('hover', false); + } + + + function zoomToEntity(change) { + var entity = change.entity; + if (change.changeType !== 'deleted' && + context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') { + context.map().zoomTo(entity); + context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())) + .classed('hover', true); + } + } + } + + + return commitChanges; +} diff --git a/modules/ui/commit_warnings.js b/modules/ui/commit_warnings.js new file mode 100644 index 0000000000..1dfba52cb7 --- /dev/null +++ b/modules/ui/commit_warnings.js @@ -0,0 +1,95 @@ +import { t } from '../util/locale'; +import { modeSelect } from '../modes'; +import { svgIcon } from '../svg'; +import { tooltip } from '../util/tooltip'; +import { utilEntityOrMemberSelector } from '../util'; + + +export function uiCommitWarnings(context) { + + function commitWarnings(selection) { + + var changes = context.history().changes(); + var warnings = context.history().validate(changes); + + var container = selection.selectAll('.warning-section') + .data(warnings.length ? [0] : []); + + container.exit() + .remove(); + + var containerEnter = container.enter() + .append('div') + .attr('class', 'modal-section warning-section fillL2'); + + containerEnter + .append('h3') + .text(t('commit.warnings')); + + containerEnter + .append('ul') + .attr('class', 'changeset-list'); + + container = containerEnter + .merge(container); + + + var items = container.select('ul').selectAll('li') + .data(warnings); + + items.exit() + .remove(); + + var itemsEnter = items.enter() + .append('li') + .attr('class', 'warning-item'); + + itemsEnter + .call(svgIcon('#icon-alert', 'pre-text')); + + itemsEnter + .append('strong') + .text(function(d) { return d.message; }); + + itemsEnter.filter(function(d) { return d.tooltip; }) + .call(tooltip() + .title(function(d) { return d.tooltip; }) + .placement('top') + ); + + items = itemsEnter + .merge(items); + + items + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .on('click', warningClick); + + + function mouseover(d) { + if (d.entity) { + context.surface().selectAll( + utilEntityOrMemberSelector([d.entity.id], context.graph()) + ).classed('hover', true); + } + } + + + function mouseout() { + context.surface().selectAll('.hover') + .classed('hover', false); + } + + + function warningClick(d) { + if (d.entity) { + context.map().zoomTo(d.entity); + context.enter(modeSelect(context, [d.entity.id])); + } + } + + } + + + return commitWarnings; +} diff --git a/modules/ui/index.js b/modules/ui/index.js index 874e0354d8..6eaef95d52 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -5,6 +5,8 @@ export { uiBackground } from './background'; export { uiChangesetEditor } from './changeset_editor'; export { uiCmd } from './cmd'; export { uiCommit } from './commit'; +export { uiCommitChanges } from './commit_changes'; +export { uiCommitWarnings } from './commit_warnings'; export { uiConfirm } from './confirm'; export { uiConflicts } from './conflicts'; export { uiContributors } from './contributors'; From 7563f3ac167a9fbf484d40e80399a22101335db9 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 09:35:57 -0400 Subject: [PATCH 19/31] Refactor duplicate form-field code to uiFormFields --- css/80_app.css | 4 +- modules/ui/changeset_editor.js | 106 +++-------------------------- modules/ui/form_fields.js | 120 +++++++++++++++++++++++++++++++++ modules/ui/index.js | 1 + modules/ui/preset_editor.js | 113 +++---------------------------- 5 files changed, 142 insertions(+), 202 deletions(-) create mode 100644 modules/ui/form_fields.js diff --git a/css/80_app.css b/css/80_app.css index 028d6967ac..006652a122 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1131,13 +1131,13 @@ button.save.has-count .count::before { margin: 0 20px 10px 20px; } -.preset-editor .preset-form { +.preset-editor .form-fields-container { padding: 10px; margin: 0 10px 10px 10px; border-radius: 8px; } -.preset-editor .preset-form:empty { +.preset-editor .form-fields-container:empty { display: none; } diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index 36cc855c1a..fe43eef184 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -4,20 +4,19 @@ import { d3combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { svgIcon } from '../svg'; import { uiField } from './field'; -import { - utilGetSetValue, - utilNoAuto, - utilRebind -} from '../util'; +import { uiFormFields } from './form_fields'; +import { utilRebind } from '../util'; export function uiChangesetEditor(context) { var dispatch = d3.dispatch('change'), + formFields = uiFormFields(context), fieldsArr, tags, changesetId; + function changesetEditor(selection) { render(selection); } @@ -49,102 +48,13 @@ export function uiChangesetEditor(context) { .tags(tags); }); - var shown = fieldsArr.filter(function(field) { return field.isShown(); }), - notShown = fieldsArr.filter(function(field) { return !field.isShown(); }); - - - var form = selection.selectAll('.preset-form') - .data([0]); - - form = form.enter() - .append('div') - .attr('class', 'preset-form') - .merge(form); - - - var fields = form.selectAll('.wrap-form-field') - .data(shown, function(d) { return d.id; }); - - fields.exit() - .remove(); - - // Enter - var enter = fields.enter() - .append('div') - .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.id; }); - - // Update - fields = fields - .merge(enter); - - fields - .order() - .each(function(d) { - d3.select(this) - .call(d.render); - }); - - - notShown = notShown.map(function(field) { - return { - title: field.label(), - value: field.label(), - field: field - }; - }); - - - var more = selection.selectAll('.more-fields') - .data((notShown.length > 0) ? [0] : []); - - more.exit() - .remove(); - - more = more.enter() - .append('div') - .attr('class', 'more-fields') - .append('label') - .text(t('inspector.add_fields')) - .merge(more); - - - var input = more.selectAll('.value') - .data([0]); - - input.exit() - .remove(); - input = input.enter() - .append('input') - .attr('class', 'value') - .attr('type', 'text') - .call(utilNoAuto) - .merge(input); - - input - .call(utilGetSetValue, '') - .attr('placeholder', function() { - var placeholder = []; - for (var field in notShown) { - placeholder.push(notShown[field].title); - } - return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : ''); - }) - .call(d3combobox() - .container(context.container()) - .data(notShown) - .minItems(1) - .on('accept', function (d) { - var field = d.field; - field.show = true; - render(selection); - field.focus(); - }) - ); + selection + .call(formFields.fieldsArr(fieldsArr)); if (initial) { - var commentField = d3.select('#preset-input-comment'), + var commentField = selection.select('#preset-input-comment'), commentNode = commentField.node(); if (commentNode) { @@ -175,7 +85,7 @@ export function uiChangesetEditor(context) { } var matches = tags.comment.match(/google/i); - var commentWarning = d3.select('.form-field-comment').selectAll('.comment-warning') + var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning') .data(matches ? [0] : []); commentWarning.exit() diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js new file mode 100644 index 0000000000..6606e28f09 --- /dev/null +++ b/modules/ui/form_fields.js @@ -0,0 +1,120 @@ +import * as d3 from 'd3'; +import { d3combobox } from '../lib/d3.combobox.js'; +import { t } from '../util/locale'; +import { utilGetSetValue, utilNoAuto } from '../util'; + + +export function uiFormFields(context) { + var fieldsArr; + + + function formFields(selection, klass) { + render(selection, klass); + } + + + function render(selection, klass) { + + var shown = fieldsArr.filter(function(field) { return field.isShown(); }), + notShown = fieldsArr.filter(function(field) { return !field.isShown(); }); + + var container = selection.selectAll('.form-fields-container') + .data([0]); + + container = container.enter() + .append('div') + .attr('class', 'form-fields-container ' + (klass || '')) + .merge(container); + + + var fields = container.selectAll('.wrap-form-field') + .data(shown, function(d) { return d.id; }); + + fields.exit() + .remove(); + + // Enter + var enter = fields.enter() + .append('div') + .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.id; }); + + // Update + fields = fields + .merge(enter); + + fields + .order() + .each(function(d) { + d3.select(this) + .call(d.render); + }); + + + notShown = notShown.map(function(field) { + return { + title: field.label(), + value: field.label(), + field: field + }; + }); + + + var more = selection.selectAll('.more-fields') + .data((notShown.length > 0) ? [0] : []); + + more.exit() + .remove(); + + more = more.enter() + .append('div') + .attr('class', 'more-fields') + .append('label') + .text(t('inspector.add_fields')) + .merge(more); + + + var input = more.selectAll('.value') + .data([0]); + + input.exit() + .remove(); + + input = input.enter() + .append('input') + .attr('class', 'value') + .attr('type', 'text') + .call(utilNoAuto) + .merge(input); + + input + .call(utilGetSetValue, '') + .attr('placeholder', function() { + var placeholder = []; + for (var field in notShown) { + placeholder.push(notShown[field].title); + } + return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : ''); + }) + .call(d3combobox() + .container(context.container()) + .data(notShown) + .minItems(1) + .on('accept', function (d) { + var field = d.field; + field.show = true; + render(selection); + field.focus(); + }) + ); + } + + + formFields.fieldsArr = function(_) { + if (!arguments.length) return fieldsArr; + fieldsArr = _; + return formFields; + }; + + + return formFields; +} diff --git a/modules/ui/index.js b/modules/ui/index.js index 6eaef95d52..742aa7b1ca 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -18,6 +18,7 @@ export { uiFeatureInfo } from './feature_info'; export { uiFeatureList } from './feature_list'; export { uiField } from './field'; export { uiFlash } from './flash'; +export { uiFormFields } from './form_fields'; export { uiFullScreen } from './full_screen'; export { uiGeolocate } from './geolocate'; export { uiHelp } from './help'; diff --git a/modules/ui/preset_editor.js b/modules/ui/preset_editor.js index beeb67cc5d..c597256c56 100644 --- a/modules/ui/preset_editor.js +++ b/modules/ui/preset_editor.js @@ -1,18 +1,15 @@ import * as d3 from 'd3'; -import { d3combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { modeBrowse } from '../modes'; import { uiDisclosure } from './disclosure'; import { uiField } from './field'; -import { - utilGetSetValue, - utilNoAuto, - utilRebind -} from '../util'; +import { uiFormFields } from './form_fields'; +import { utilRebind } from '../util'; export function uiPresetEditor(context) { var dispatch = d3.dispatch('change'), + formFields = uiFormFields(context), expandedPreference = (context.storage('preset_fields.expanded') !== 'false'), state, fieldsArr, @@ -80,106 +77,18 @@ export function uiPresetEditor(context) { .tags(tags); }); - var shown = fieldsArr.filter(function(field) { return field.isShown(); }), - notShown = fieldsArr.filter(function(field) { return !field.isShown(); }); + selection + .call(formFields.fieldsArr(fieldsArr), 'inspector-inner fillL3'); - var form = selection.selectAll('.preset-form') - .data([0]); - form = form.enter() - .append('div') - .attr('class', 'preset-form inspector-inner fillL3') - .merge(form); - - - var fields = form.selectAll('.wrap-form-field') - .data(shown, function(d) { return d.id; }); - - fields.exit() - .remove(); - - // Enter - var enter = fields.enter() - .append('div') - .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.id; }); - - // Update - fields = fields - .merge(enter); - - fields - .order() - .each(function(d) { - d3.select(this) - .call(d.render) - .selectAll('input') - .on('keydown', function() { - // if user presses enter, and combobox is not active, accept edits.. - if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) { - context.enter(modeBrowse(context)); - } - }); - }); - - - notShown = notShown.map(function(field) { - return { - title: field.label(), - value: field.label(), - field: field - }; - }); - - - var more = selection.selectAll('.more-fields') - .data((notShown.length > 0) ? [0] : []); - - more.exit() - .remove(); - - more = more.enter() - .append('div') - .attr('class', 'more-fields') - .append('label') - .text(t('inspector.add_fields')) - .merge(more); - - - var input = more.selectAll('.value') - .data([0]); - - input.exit() - .remove(); - - input = input.enter() - .append('input') - .attr('class', 'value') - .attr('type', 'text') - .call(utilNoAuto) - .merge(input); - - input - .call(utilGetSetValue, '') - .attr('placeholder', function() { - var placeholder = []; - for (var field in notShown) { - placeholder.push(notShown[field].title); + selection.selectAll('.wrap-form-field input') + .on('keydown', function() { + // if user presses enter, and combobox is not active, accept edits.. + if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) { + context.enter(modeBrowse(context)); } - return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : ''); - }) - .call(d3combobox() - .container(context.container()) - .data(notShown) - .minItems(1) - .on('accept', function (d) { - var field = d.field; - field.show = true; - render(selection); - field.focus(); - }) - ); - + }); } From 09a661c7184f1d6347649b5b7ec1ac86e1ddaf24 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 13:12:57 -0400 Subject: [PATCH 20/31] Restore "good changeset comments" link in tag-reference section Also includes a bunch of css cleanups for the tag-reference section to fix style for readonly raw-tag-editor rows and adjust margin widths --- css/80_app.css | 45 ++++++++++++----------------- modules/ui/changeset_editor.js | 16 ++-------- modules/ui/tag_reference.js | 53 ++++++++++++++++++++++++---------- 3 files changed, 60 insertions(+), 54 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 006652a122..e652cbecf1 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2034,18 +2034,17 @@ button.minor.tag-reference-loading { clear: both; } -.tag-reference-body p, -.tag-reference-body img { - margin-top: 20px; -} - -.tag-reference-body p:last-child { - padding-bottom: 10px; +.tag-reference-body .tag-reference-description { + margin: 10px 5px 0 5px; } .tag-reference-body a { display: block; - padding-bottom: 10px; +} + +.tag-reference-body .tag-reference-description:last-child, +.tag-reference-body a:last-child { + margin-bottom: 15px; } .preset-list .tag-reference-body { @@ -2053,41 +2052,35 @@ button.minor.tag-reference-loading { width: 100%; } -.preset-list .tag-reference-body a { - padding-bottom: 20px; -} - -.preset-list .tag-reference-body p, -.preset-list .tag-reference-body img { - margin-top: 10px; -} - .raw-tag-editor .tag-reference-body { - border-bottom: 1px solid #ccc; float: left; width: 100%; } -.raw-tag-editor .tag-reference-body p:last-child { - padding-bottom: 20px; +.raw-tag-editor .tag-row.readonly .tag-reference-body { + background: #f6f6f6; + color: #333; } -.raw-tag-editor .tag-reference-body a { - padding-bottom: 20px; +.raw-tag-editor .tag-row:not(:last-child) .tag-reference-body { + border-bottom: 1px solid #ccc; } -img.wiki-image { +.raw-tag-editor .tag-row.readonly .tag-reference-body.expanded { + border-top: 1px solid #ccc; +} + +img.tag-reference-wiki-image { float: right; width: 33.3333%; width: -webkit-calc(33.3333% - 10px); width: calc(33.3333% - 10px); - margin-left: 20px; - margin-right: 10px; border-radius: 4px; max-height: 200px; - margin-bottom: 20px; + margin: 10px 5px 15px 20px; } + /* Raw relation membership editor */ .raw-member-editor .member-list li:first-child, diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index fe43eef184..c05181e52a 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -84,12 +84,14 @@ export function uiChangesetEditor(context) { } } + // Add comment warning var matches = tags.comment.match(/google/i); var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning') .data(matches ? [0] : []); commentWarning.exit() .transition() + .duration(200) .style('opacity', 0) .remove(); @@ -109,20 +111,8 @@ export function uiChangesetEditor(context) { commentEnter .transition() + .duration(200) .style('opacity', 1); - - - // var changeSetInfo = fieldSection.append('div') - // .attr('class', 'changeset-info'); - - // changeSetInfo.append('a') - // .attr('target', '_blank') - // .attr('tabindex', -1) - // .call(svgIcon('#icon-out-link', 'inline')) - // .attr('href', t('commit.about_changeset_comments_link')) - // .append('span') - // .text(t('commit.about_changeset_comments')); - } diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js index 77531839fc..b5e5e5ec87 100644 --- a/modules/ui/tag_reference.js +++ b/modules/ui/tag_reference.js @@ -9,8 +9,8 @@ import { svgIcon } from '../svg/index'; export function uiTagReference(tag) { var taginfo = services.taginfo, tagReference = {}, - button, - body, + button = d3.select(null), + body = d3.select(null), loaded, showing; @@ -46,7 +46,8 @@ export function uiTagReference(tag) { function load(param) { if (!taginfo) return; - button.classed('tag-reference-loading', true); + button + .classed('tag-reference-loading', true); taginfo.docs(param, function show(err, data) { var docs; @@ -56,22 +57,23 @@ export function uiTagReference(tag) { body.html(''); - if (!docs || !docs.title) { if (param.hasOwnProperty('value')) { load(_.omit(param, 'value')); // retry with key only } else { - body.append('p').text(t('inspector.no_documentation_key')); + body + .append('p') + .attr('class', 'tag-reference-description') + .text(t('inspector.no_documentation_key')); done(); } return; } - if (docs.image && docs.image.thumb_url_prefix) { body .append('img') - .attr('class', 'wiki-image') + .attr('class', 'tag-reference-wiki-image') .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix) .on('load', function() { done(); }) .on('error', function() { d3.select(this).remove(); done(); }); @@ -81,16 +83,31 @@ export function uiTagReference(tag) { body .append('p') + .attr('class', 'tag-reference-description') .text(docs.description || t('inspector.documentation_redirect')); body .append('a') + .attr('class', 'tag-reference-link') .attr('target', '_blank') .attr('tabindex', -1) .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title) .call(svgIcon('#icon-out-link', 'inline')) .append('span') .text(t('inspector.reference')); + + // Add link to info about "good changeset comments" - #2923 + if (param.key === 'comment') { + body + .append('a') + .attr('class', 'tag-reference-comment-link') + .attr('target', '_blank') + .attr('tabindex', -1) + .call(svgIcon('#icon-out-link', 'inline')) + .attr('href', t('commit.about_changeset_comments_link')) + .append('span') + .text(t('commit.about_changeset_comments')); + } }); } @@ -98,9 +115,12 @@ export function uiTagReference(tag) { function done() { loaded = true; - button.classed('tag-reference-loading', false); + button + .classed('tag-reference-loading', false); - body.transition() + body + .classed('expanded', true) + .transition() .duration(200) .style('max-height', '200px') .style('opacity', '1'); @@ -109,12 +129,15 @@ export function uiTagReference(tag) { } - function hide(selection) { - selection = selection || body.transition().duration(200); - - selection + function hide() { + body + .transition() + .duration(200) .style('max-height', '0px') - .style('opacity', '0'); + .style('opacity', '0') + .on('end', function () { + body.classed('expanded', false); + }); showing = false; } @@ -158,7 +181,7 @@ export function uiTagReference(tag) { .merge(body); if (showing === false) { - hide(body); + hide(); } }; From c56dc5afc39ae9f190856a5d7fbdae50b417f516 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 14:32:16 -0400 Subject: [PATCH 21/31] In save mode, escape should cancel and return to browse mode (closes #4230) --- modules/modes/save.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/modes/save.js b/modules/modes/save.js index 766f667799..391fe2f47a 100644 --- a/modules/modes/save.js +++ b/modules/modes/save.js @@ -1,6 +1,7 @@ import * as d3 from 'd3'; import _ from 'lodash'; +import { d3keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; import { JXON } from '../util/jxon'; @@ -34,6 +35,8 @@ export function modeSave(context) { id: 'save' }; + var keybinding = d3keybinding('select'); + var commit = uiCommit(context) .on('cancel', cancel) .on('save', save); @@ -360,6 +363,12 @@ export function modeSave(context) { context.ui().sidebar.show(commit); } + keybinding + .on('⎋', cancel, true); + + d3.select(document) + .call(keybinding); + context.container().selectAll('#content') .attr('class', 'inactive'); @@ -381,6 +390,8 @@ export function modeSave(context) { mode.exit = function() { + keybinding.off(); + context.container().selectAll('#content') .attr('class', 'active'); From e9909717c7b1ad396c240922b148b52a4cca7c63 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 15:15:39 -0400 Subject: [PATCH 22/31] Change source field to semiCombo, let users pick common source types --- data/presets.yaml | 4 +--- data/presets/fields.json | 12 +++++++++--- data/presets/fields/source.json | 12 +++++++++--- dist/locales/en.json | 3 +-- modules/ui/form_fields.js | 4 +++- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 1e4967d014..3b20e23319 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1226,9 +1226,7 @@ en: label: People Served source: # source=* - label: Source - # source field placeholder - placeholder: 'survey, local knowledge, aerial imagery' + label: Sources sport: # sport=* label: Sports diff --git a/data/presets/fields.json b/data/presets/fields.json index dcb437a6cf..56e95d3947 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -1635,11 +1635,17 @@ }, "source": { "key": "source", - "type": "text", + "type": "semiCombo", "icon": "source", "universal": true, - "label": "Source", - "placeholder": "survey, local knowledge, aerial imagery" + "label": "Sources", + "options": [ + "survey", + "local knowledge", + "gps", + "aerial imagery", + "streetlevel imagery" + ] }, "sport_ice": { "key": "sport", diff --git a/data/presets/fields/source.json b/data/presets/fields/source.json index e1464d99bf..c9b746500c 100644 --- a/data/presets/fields/source.json +++ b/data/presets/fields/source.json @@ -1,8 +1,14 @@ { "key": "source", - "type": "text", + "type": "semiCombo", "icon": "source", "universal": true, - "label": "Source", - "placeholder": "survey, local knowledge, aerial imagery" + "label": "Sources", + "options": [ + "survey", + "local knowledge", + "gps", + "aerial imagery", + "streetlevel imagery" + ] } diff --git a/dist/locales/en.json b/dist/locales/en.json index c8a5570a12..1cb360e7c5 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2100,8 +2100,7 @@ "label": "Type" }, "source": { - "label": "Source", - "placeholder": "survey, local knowledge, aerial imagery" + "label": "Sources" }, "sport_ice": { "label": "Sports" diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js index 6606e28f09..bcb2345580 100644 --- a/modules/ui/form_fields.js +++ b/modules/ui/form_fields.js @@ -103,7 +103,9 @@ export function uiFormFields(context) { var field = d.field; field.show = true; render(selection); - field.focus(); + if (field.type !== 'semiCombo' && field.type !== 'multiCombo') { + field.focus(); + } }) ); } From 746b54f3d2b12b82c6eccb10c4763d5575ac5973 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 15:34:34 -0400 Subject: [PATCH 23/31] Make combo field work without an entity --- modules/ui/fields/combo.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ui/fields/combo.js b/modules/ui/fields/combo.js index 1f61bf0bd0..baef52aa07 100644 --- a/modules/ui/fields/combo.js +++ b/modules/ui/fields/combo.js @@ -161,12 +161,17 @@ export function uiFieldCombo(field, context) { query = country + ':'; } - taginfo[fn]({ + var params = { debounce: (q !== ''), key: field.key, - geometry: context.geometry(entity.id), query: query - }, function(err, data) { + }; + + if (entity) { + params.geometry = context.geometry(entity.id); + } + + taginfo[fn](params, function(err, data) { if (err) return; if (hasCountryPrefix) { data = _.filter(data, function(d) { From df16568e1cd5221c55b20aca0746e0c9db6e44eb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 15 Aug 2017 15:35:22 -0400 Subject: [PATCH 24/31] Change hashtags field to semiCombo --- data/presets.yaml | 2 ++ data/presets/fields.json | 5 +++-- data/presets/fields/hashtags.json | 5 +++-- dist/locales/en.json | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 3b20e23319..aede647a20 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -579,6 +579,8 @@ en: hashtags: # hashtags=* label: Hashtags + # hashtags field placeholder + placeholder: '#example' height: # height=* label: Height (Meters) diff --git a/data/presets/fields.json b/data/presets/fields.json index 56e95d3947..2a0ea2a53e 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -796,8 +796,9 @@ }, "hashtags": { "key": "hashtags", - "type": "text", - "label": "Hashtags" + "type": "semiCombo", + "label": "Hashtags", + "placeholder": "#example" }, "height": { "key": "height", diff --git a/data/presets/fields/hashtags.json b/data/presets/fields/hashtags.json index 4ea1277d91..d3045507c3 100644 --- a/data/presets/fields/hashtags.json +++ b/data/presets/fields/hashtags.json @@ -1,5 +1,6 @@ { "key": "hashtags", - "type": "text", - "label": "Hashtags" + "type": "semiCombo", + "label": "Hashtags", + "placeholder": "#example" } diff --git a/dist/locales/en.json b/dist/locales/en.json index 1cb360e7c5..618af1d786 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1570,7 +1570,8 @@ "label": "Handrail" }, "hashtags": { - "label": "Hashtags" + "label": "Hashtags", + "placeholder": "#example" }, "height": { "label": "Height (Meters)" From 4cf357f61a324c4fd69bb87fc2d62dd2c35299e7 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 16 Aug 2017 00:30:34 -0400 Subject: [PATCH 25/31] Add code to extract hashtags from changeset comment --- modules/ui/changeset_editor.js | 12 ++++++++---- modules/ui/commit.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/modules/ui/changeset_editor.js b/modules/ui/changeset_editor.js index c05181e52a..f04b47e3d7 100644 --- a/modules/ui/changeset_editor.js +++ b/modules/ui/changeset_editor.js @@ -5,7 +5,7 @@ import { t } from '../util/locale'; import { svgIcon } from '../svg'; import { uiField } from './field'; import { uiFormFields } from './form_fields'; -import { utilRebind } from '../util'; +import { utilRebind, utilTriggerEvent } from '../util'; export function uiChangesetEditor(context) { @@ -62,6 +62,10 @@ export function uiChangesetEditor(context) { commentNode.select(); } + // trigger a 'blur' event so that comment field can be cleaned + // and checked for hashtags, even if retrieved from localstorage + utilTriggerEvent(commentField, 'blur'); + var osm = context.connection(); if (osm) { osm.userChangesets(function (err, changesets) { @@ -84,10 +88,10 @@ export function uiChangesetEditor(context) { } } - // Add comment warning - var matches = tags.comment.match(/google/i); + // Add warning if comment mentions Google + var hasGoogle = tags.comment.match(/google/i); var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning') - .data(matches ? [0] : []); + .data(hasGoogle ? [0] : []); commentWarning.exit() .transition() diff --git a/modules/ui/commit.js b/modules/ui/commit.js index db773a2bb7..5b492a949c 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -260,6 +260,30 @@ export function uiCommit(context) { } + function findHashtags(tags) { + return _.unionBy(commentTags(), hashTags(), function (s) { + return s.toLowerCase(); + }); + + // Extract hashtags from `comment` + function commentTags() { + return tags.comment.match(/#[^\s\#]+/g); + } + + // Extract and clean hashtags from `hashtags` + function hashTags() { + var t = tags.hashtags || ''; + return t + .split(/[,;\s]+/) + .map(function (s) { + if (s[0] !== '#') { s = '#' + s; } // prepend '#' + var matched = s.match(/#[^\s\#]+/g); // match valid hashtags + return matched && matched[0]; + }).filter(Boolean); // exclude falsey + } + } + + function isReviewRequested(tags) { var rr = tags.review_requested; if (rr === undefined) return false; @@ -286,6 +310,13 @@ export function uiCommit(context) { } }); + if (!onInput) { + var hashtags = findHashtags(tags); + if (hashtags.length) { + tags.hashtags = hashtags.join(';').substr(0, 255); + } + } + if (!_.isEqual(changeset.tags, tags)) { changeset = changeset.update({ tags: tags }); } From a42aa789e9bc4d8778ec3f6fe3814848c3eed2ea Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 16 Aug 2017 10:47:17 -0400 Subject: [PATCH 26/31] Add hashtags API parameter and localStorage (closes #2834) --- API.md | 53 ++++++++++++++++++++++------------------ modules/behavior/hash.js | 8 +++++- modules/ui/commit.js | 14 ++++++++--- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/API.md b/API.md index 8e4ab5194e..3a506b0202 100644 --- a/API.md +++ b/API.md @@ -8,28 +8,32 @@ iD supports several URL parameters. When constructing a URL to a standalone inst of iD (e.g. `http://preview.ideditor.com/release/`), the following parameters are available in the hash portion of the URL: -* `map` - A slash separated `zoom/latitude/longitude`. Example: - `map=20.00/38.90085/-77.02271` -* `id` - The character 'n', 'w', or 'r', followed by the OSM ID of a node, +* __`map`__ - A slash separated `zoom/latitude/longitude`.
+ _Example:_ `map=20.00/38.90085/-77.02271` +* __`id`__ - The character 'n', 'w', or 'r', followed by the OSM ID of a node, way or relation, respectively. Selects the specified entity, and, unless a `map` parameter is also provided, centers the map on it. -* `background` - The value from a `sourcetag` property in iD's +* __`background`__ - The value from a `sourcetag` property in iD's [imagery list](https://github.com/openstreetmap/iD/blob/master/data/imagery.json), or a custom tile URL. A custom URL is specified in the format `custom:`, where the URL can contain the standard tile URL placeholders `{x}`, `{y}` and `{z}`/`{zoom}`, `{ty}` for flipped TMS-style Y coordinates, and `{switch:a,b,c}` for - DNS multiplexing. Example: - `background=custom:https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png` -* `gpx` - A custom URL for loading a gpx track. Specifying a `gpx` parameter will - automatically enable the gpx layer for display. Example: - `gpx=https://tasks.hotosm.org/project/592/task/16.gpx` -* `offset` - imagery offset in meters, formatted as `east,north`. Example: - `offset=-10,5` -* `comment` - Prefills the changeset comment box, for use when integrating iD with - external task management or quality assurance tools. Example: - `comment=CAR%20crisis%2C%20refugee%20areas%20in%20Cameroon%20%23hotosm-task-592`. -* `rtl=true` - Force iD into right-to-left mode (useful for testing). -* `walkthrough=true` - Start the walkthrough automatically + DNS multiplexing.
+ _Example:_ `background=custom:https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png` +* __`gpx`__ - A custom URL for loading a gpx track. Specifying a `gpx` parameter will + automatically enable the gpx layer for display.
+ _Example:_ `gpx=https://tasks.hotosm.org/project/592/task/16.gpx` +* __`offset`__ - imagery offset in meters, formatted as `east,north`.
+ _Example:_ `offset=-10,5` +* __`comment`__ - Prefills the changeset comment. Pass a url encoded string.
+ _Example:_ `comment=CAR%20crisis%2C%20refugee%20areas%20in%20Cameroon` +* __`hashtags`__ - Prefills the changeset hashtags. Pass a url encoded list of event + hashtags separated by commas, semicolons, or spaces. Leading '#' symbols are + optional and will be added automatically. (Note that hashtag-like strings are + automatically detected in the `comment`).
+ _Example:_ `hashtags=%23hotosm-task-592,%23MissingMaps` +* __`rtl=true`__ - Force iD into right-to-left mode (useful for testing). +* __`walkthrough=true`__ - Start the walkthrough automatically ##### iD on openstreetmap.org (Rails Port) @@ -37,14 +41,15 @@ When constructing a URL to an instance of iD embedded in the OpenStreetMap Rails Port (e.g. `http://www.openstreetmap.org/edit?editor=id`), the following parameters are available as regular URL query parameters: -* `map` - same as standalone -* `lat`, `lon`, `zoom` - Self-explanatory. -* `node`, `way`, `relation` - Select the specified entity. -* `background` - same as standalone -* `gpx` - same as standalone -* `offset` - same as standalone -* `comment` - same as standalone -* `walkthrough` - same as standalone +* __`map`__ - same as standalone +* __`lat`__, __`lon`__, __`zoom`__ - Self-explanatory. +* __`node`__, __`way`__, __`relation`__ - Select the specified entity. +* __`background`__ - same as standalone +* __`gpx`__ - same as standalone +* __`offset`__ - same as standalone +* __`comment`__ - same as standalone +* __`hashtags`__ - same as standalone +* __`walkthrough`__ - same as standalone ## CSS selectors diff --git a/modules/behavior/hash.js b/modules/behavior/hash.js index 431594a7c9..63cece2dfa 100644 --- a/modules/behavior/hash.js +++ b/modules/behavior/hash.js @@ -37,7 +37,9 @@ export function behaviorHash(context) { var center = map.center(), zoom = map.zoom(), precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)), - q = _.omit(utilStringQs(window.location.hash.substring(1)), ['comment', 'walkthrough']), + q = _.omit(utilStringQs(window.location.hash.substring(1)), + ['comment', 'hashtags', 'walkthrough'] + ), newParams = {}; delete q.id; @@ -99,6 +101,10 @@ export function behaviorHash(context) { context.storage('commentDate', Date.now()); } + if (q.hashtags) { + context.storage('hashtags', q.hashtags); + } + if (q.walkthrough === 'true') { hash.startWalkthrough = true; } diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 5b492a949c..aaab49f4cf 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -34,12 +34,14 @@ export function uiCommit(context) { var comment = context.storage('comment') || '', commentDate = +context.storage('commentDate') || 0, + hashtags = context.storage('hashtags'), currDate = Date.now(), cutoff = 2 * 86400 * 1000; // 2 days - // expire the stored comment if it is too old - #3947 + // expire stored comment and hashtags after cutoff datetime - #3947 if (commentDate > currDate || currDate - commentDate > cutoff) { comment = ''; + hashtags = undefined; } var tags; @@ -49,6 +51,7 @@ export function uiCommit(context) { comment: comment, created_by: ('iD ' + context.version).substr(0, 255), imagery_used: context.history().imageryUsed().join(';').substr(0, 255), + hashtags: hashtags, host: detected.host.substr(0, 255), locale: detected.locale.substr(0, 255) }; @@ -311,9 +314,12 @@ export function uiCommit(context) { }); if (!onInput) { - var hashtags = findHashtags(tags); - if (hashtags.length) { - tags.hashtags = hashtags.join(';').substr(0, 255); + var arr = findHashtags(tags); + if (arr.length) { + tags.hashtags = arr.join(';').substr(0, 255); + context.storage('hashtags', tags.hashtags); + } else { + context.storage('hashtags', null); } } From 8cb04a087cca0e9163ce0d131cf647c0dd7e59e0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 16 Aug 2017 14:30:21 -0400 Subject: [PATCH 27/31] Track walkthrough progress in localStorage --- modules/ui/intro/intro.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/ui/intro/intro.js b/modules/ui/intro/intro.js index bb351c452a..f87bb9b162 100644 --- a/modules/ui/intro/intro.js +++ b/modules/ui/intro/intro.js @@ -1,4 +1,5 @@ import * as d3 from 'd3'; +import _ from 'lodash'; import { t, textDirection } from '../../util/locale'; import { localize } from './helper'; @@ -88,6 +89,13 @@ export function uiIntro(context) { var curtain = uiCurtain(); selection.call(curtain); + // store that the user started the walkthrough.. + context.storage('walkthrough_started', 'yes'); + + // restore previous walkthrough progress.. + var storedProgress = context.storage('walkthrough_progress') || ''; + var progress = storedProgress.split(';').filter(Boolean); + var chapters = chapterFlow.map(function(chapter, i) { var s = chapterUi[chapter](context, curtain.reveal) .on('done', function() { @@ -102,11 +110,25 @@ export function uiIntro(context) { d3.select('button.chapter-' + next) .classed('next', true); } + + // store walkthrough progress.. + progress.push(chapter); + context.storage('walkthrough_progress', _.uniq(progress).join(';')); }); return s; }); chapters[chapters.length - 1].on('startEditing', function() { + // store walkthrough progress.. + progress.push('startEditing'); + context.storage('walkthrough_progress', _.uniq(progress).join(';')); + + // store if walkthrough is completed.. + var incomplete = _.difference(chapterFlow, progress); + if (!incomplete.length) { + context.storage('walkthrough_completed', 'yes'); + } + curtain.remove(); navwrap.remove(); d3.selectAll('#map .layer-background').style('opacity', opacity); From 25d8a8aa08f9353e01c1d1e893a9cd1025ebd806 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 16 Aug 2017 16:31:21 -0400 Subject: [PATCH 28/31] Store changesets_count in a changeset tag (it will say "0" for someone making their first edit) --- modules/ui/commit.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index aaab49f4cf..0e5ffc75d9 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -11,11 +11,18 @@ import { utilRebind } from '../util'; var changeset; -var readOnlyTags = ['created_by', 'imagery_used', 'host', 'locale']; +var readOnlyTags = [ + 'changesets_count', + 'created_by', + 'imagery_used', + 'host', + 'locale' +]; export function uiCommit(context) { var dispatch = d3.dispatch('cancel', 'save'), + userDetails, _selection; var changesetEditor = uiChangesetEditor(context) @@ -122,6 +129,8 @@ export function uiCommit(context) { var userLink = d3.select(document.createElement('div')); + userDetails = user; + if (user.image_url) { userLink .append('img') @@ -323,6 +332,13 @@ export function uiCommit(context) { } } + // always update userdetails, just in case user reauthenticates as someone else + if (userDetails && userDetails.changesets_count !== undefined) { + tags.changesets_count = String(userDetails.changesets_count); + } else { + delete tags.changesets_count; + } + if (!_.isEqual(changeset.tags, tags)) { changeset = changeset.update({ tags: tags }); } From ea298b0b18fca844b845fe32fb7cce518cda33c5 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 16 Aug 2017 19:54:53 -0400 Subject: [PATCH 29/31] Change raw tag editor readOnlyTags to accept array of regex --- modules/ui/commit.js | 10 +++++----- modules/ui/raw_tag_editor.js | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 0e5ffc75d9..b3681d602b 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -12,11 +12,11 @@ import { utilRebind } from '../util'; var changeset; var readOnlyTags = [ - 'changesets_count', - 'created_by', - 'imagery_used', - 'host', - 'locale' + /^changesets_count$/, + /^created_by$/, + /^imagery_used$/, + /^host$/, + /^locale$/ ]; diff --git a/modules/ui/raw_tag_editor.js b/modules/ui/raw_tag_editor.js index c3bbb4f8a1..2991895d6e 100644 --- a/modules/ui/raw_tag_editor.js +++ b/modules/ui/raw_tag_editor.js @@ -175,7 +175,12 @@ export function uiRawTagEditor(context) { function isReadOnly(d) { - return readOnlyTags.indexOf(d.key) !== -1; + for (var i = 0; i < readOnlyTags.length; i++) { + if (d.key.match(readOnlyTags[i]) !== null) { + return true; + } + } + return false; } From 2bf7a5e08bbf539ce71884f1bb086a693ab99a8c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 16 Aug 2017 20:07:12 -0400 Subject: [PATCH 30/31] Write changeset tags for new mappers to indicate walkthrough progress These tags all start with `ideditor:` (closes #3968) ``` ideditor:walkthrough_completed=yes ideditor:walkthrough_progress=welcome;navigation;point;area;line;building;startEditing ideditor:walkthrough_started=yes ``` --- modules/ui/commit.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index b3681d602b..0d1fff7e9b 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -14,6 +14,7 @@ var changeset; var readOnlyTags = [ /^changesets_count$/, /^created_by$/, + /^ideditor:/, /^imagery_used$/, /^host$/, /^locale$/ @@ -335,6 +336,25 @@ export function uiCommit(context) { // always update userdetails, just in case user reauthenticates as someone else if (userDetails && userDetails.changesets_count !== undefined) { tags.changesets_count = String(userDetails.changesets_count); + + // first 100 edits - new user + if (parseInt(tags.changesets_count, 10) < 100) { + var s; + s = context.storage('walkthrough_completed'); + if (s) { + tags['ideditor:walkthrough_completed'] = s; + } + + s = context.storage('walkthrough_progress'); + if (s) { + tags['ideditor:walkthrough_progress'] = s; + } + + s = context.storage('walkthrough_started'); + if (s) { + tags['ideditor:walkthrough_started'] = s; + } + } } else { delete tags.changesets_count; } From 121a1fa629991e3239607ce9161d6a6d5bb493a4 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 17 Aug 2017 12:06:16 -0400 Subject: [PATCH 31/31] Avoid making an empty `hashtags` tag --- modules/ui/commit.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 0d1fff7e9b..9c54a6d4fa 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -59,11 +59,14 @@ export function uiCommit(context) { comment: comment, created_by: ('iD ' + context.version).substr(0, 255), imagery_used: context.history().imageryUsed().join(';').substr(0, 255), - hashtags: hashtags, host: detected.host.substr(0, 255), locale: detected.locale.substr(0, 255) }; + if (hashtags) { + tags.hashtags = hashtags; + } + changeset = new osmChangeset({ tags: tags }); } @@ -329,6 +332,7 @@ export function uiCommit(context) { tags.hashtags = arr.join(';').substr(0, 255); context.storage('hashtags', tags.hashtags); } else { + delete tags.hashtags; context.storage('hashtags', null); } }