diff --git a/css/80_app.css b/css/80_app.css
index 8a9712807..592a12f19 100644
--- a/css/80_app.css
+++ b/css/80_app.css
@@ -4542,6 +4542,9 @@ img.tile-debug {
padding: 20px;
border-bottom: 1px solid #ccc;
}
+.modal-section p:not(:last-of-type) {
+ padding-bottom: 20px;
+}
.modal-section.header h3 {
padding: 0;
}
diff --git a/data/core.yaml b/data/core.yaml
index bd54547aa..14e4c06be 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -739,8 +739,8 @@ en:
title: Privacy
privacy_link: View the iD privacy policy
third_party_icons:
- description: Show Third Party Preset Icons
- tooltip: Uncheck this box to avoid loading preset icons from third party sites such as Wikimedia Commons, Facebook, or Twitter.
+ description: Show Third Party Icons
+ tooltip: Uncheck this box to avoid loading icons from third party sites such as Wikimedia Commons, Facebook, or Twitter.
restore:
heading: You have unsaved changes
description: "Do you wish to restore unsaved changes from a previous editing session?"
@@ -802,6 +802,8 @@ en:
splash:
welcome: Welcome to the iD OpenStreetMap editor
text: "iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}."
+ privacy_update: "Our privacy policy has recently been updated."
+ privacy: "{updateMessage} By using this software, you agree to do so in accordance with the iD privacy policy, which can be found {here}."
walkthrough: "Start the Walkthrough"
start: "Edit now"
source_switch:
diff --git a/dist/locales/en.json b/dist/locales/en.json
index 2d51ec950..cb83b0a8e 100644
--- a/dist/locales/en.json
+++ b/dist/locales/en.json
@@ -933,8 +933,8 @@
"title": "Privacy",
"privacy_link": "View the iD privacy policy",
"third_party_icons": {
- "description": "Show Third Party Preset Icons",
- "tooltip": "Uncheck this box to avoid loading preset icons from third party sites such as Wikimedia Commons, Facebook, or Twitter."
+ "description": "Show Third Party Icons",
+ "tooltip": "Uncheck this box to avoid loading icons from third party sites such as Wikimedia Commons, Facebook, or Twitter."
}
}
},
@@ -1004,6 +1004,8 @@
"splash": {
"welcome": "Welcome to the iD OpenStreetMap editor",
"text": "iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.",
+ "privacy_update": "Our privacy policy has recently been updated.",
+ "privacy": "{updateMessage} By using this software, you agree to do so in accordance with the iD privacy policy, which can be found {here}.",
"walkthrough": "Start the Walkthrough",
"start": "Edit now"
},
diff --git a/modules/core/context.js b/modules/core/context.js
index ec37236b0..1a3736bad 100644
--- a/modules/core/context.js
+++ b/modules/core/context.js
@@ -26,6 +26,7 @@ export function coreContext() {
var _deferred = new Set();
context.version = '2.16.0';
+ context.privacyVersion = '20191217';
// create a special translation that contains the keys in place of the strings
var tkeys = JSON.parse(JSON.stringify(dataEn)); // clone deep
diff --git a/modules/ui/loading.js b/modules/ui/loading.js
index 899c56ae6..bf195fc34 100644
--- a/modules/ui/loading.js
+++ b/modules/ui/loading.js
@@ -3,53 +3,53 @@ import { uiModal } from './modal';
export function uiLoading(context) {
- var _modalSelection = d3_select(null);
- var _message = '';
- var _blocking = false;
+ let _modalSelection = d3_select(null);
+ let _message = '';
+ let _blocking = false;
- var loading = function(selection) {
- _modalSelection = uiModal(selection, _blocking);
+ let loading = (selection) => {
+ _modalSelection = uiModal(selection, _blocking);
- var loadertext = _modalSelection.select('.content')
- .classed('loading-modal', true)
- .append('div')
- .attr('class', 'modal-section fillL');
+ let loadertext = _modalSelection.select('.content')
+ .classed('loading-modal', true)
+ .append('div')
+ .attr('class', 'modal-section fillL');
- loadertext
- .append('img')
- .attr('class', 'loader')
- .attr('src', context.imagePath('loader-white.gif'));
+ loadertext
+ .append('img')
+ .attr('class', 'loader')
+ .attr('src', context.imagePath('loader-white.gif'));
- loadertext
- .append('h3')
- .text(_message);
-
- _modalSelection.select('button.close')
- .attr('class', 'hide');
-
- return loading;
- };
-
-
- loading.message = function(_) {
- if (!arguments.length) return _message;
- _message = _;
- return loading;
- };
-
-
- loading.blocking = function(_) {
- if (!arguments.length) return _blocking;
- _blocking = _;
- return loading;
- };
-
-
- loading.close = function() {
- _modalSelection.remove();
- };
+ loadertext
+ .append('h3')
+ .text(_message);
+ _modalSelection.select('button.close')
+ .attr('class', 'hide');
return loading;
+ };
+
+
+ loading.message = (val) => {
+ if (!arguments.length) return _message;
+ _message = val;
+ return loading;
+ };
+
+
+ loading.blocking = (val) => {
+ if (!arguments.length) return _blocking;
+ _blocking = val;
+ return loading;
+ };
+
+
+ loading.close = () => {
+ _modalSelection.remove();
+ };
+
+
+ return loading;
}
diff --git a/modules/ui/modal.js b/modules/ui/modal.js
index fdf923487..5b783a7b0 100644
--- a/modules/ui/modal.js
+++ b/modules/ui/modal.js
@@ -1,78 +1,75 @@
-import {
- event as d3_event,
- select as d3_select
-} from 'd3-selection';
+import { event as d3_event, select as d3_select } from 'd3-selection';
import { svgIcon } from '../svg/icon';
import { utilKeybinding } from '../util';
export function uiModal(selection, blocking) {
- var keybinding = utilKeybinding('modal');
- var previous = selection.select('div.modal');
- var animate = previous.empty();
+ let keybinding = utilKeybinding('modal');
+ let previous = selection.select('div.modal');
+ let animate = previous.empty();
- previous.transition()
- .duration(200)
- .style('opacity', 0)
- .remove();
+ previous.transition()
+ .duration(200)
+ .style('opacity', 0)
+ .remove();
- var shaded = selection
- .append('div')
- .attr('class', 'shaded')
- .style('opacity', 0);
+ let shaded = selection
+ .append('div')
+ .attr('class', 'shaded')
+ .style('opacity', 0);
- shaded.close = function() {
- shaded
- .transition()
- .duration(200)
- .style('opacity',0)
- .remove();
-
- modal
- .transition()
- .duration(200)
- .style('top','0px');
-
- d3_select(document)
- .call(keybinding.unbind);
- };
-
-
- var modal = shaded
- .append('div')
- .attr('class', 'modal fillL');
-
- if (!blocking) {
- shaded.on('click.remove-modal', function() {
- if (d3_event.target === this) {
- shaded.close();
- }
- });
-
- modal.append('button')
- .attr('class', 'close')
- .on('click', shaded.close)
- .call(svgIcon('#iD-icon-close'));
-
- keybinding
- .on('⌫', shaded.close)
- .on('⎋', shaded.close);
-
- d3_select(document)
- .call(keybinding);
- }
+ shaded.close = () => {
+ shaded
+ .transition()
+ .duration(200)
+ .style('opacity',0)
+ .remove();
modal
- .append('div')
- .attr('class', 'content');
+ .transition()
+ .duration(200)
+ .style('top','0px');
- if (animate) {
- shaded.transition().style('opacity', 1);
- } else {
- shaded.style('opacity', 1);
- }
+ d3_select(document)
+ .call(keybinding.unbind);
+ };
- return shaded;
+ let modal = shaded
+ .append('div')
+ .attr('class', 'modal fillL');
+
+ if (!blocking) {
+ shaded.on('click.remove-modal', () => {
+ if (d3_event.target === this) {
+ shaded.close();
+ }
+ });
+
+ modal
+ .append('button')
+ .attr('class', 'close')
+ .on('click', shaded.close)
+ .call(svgIcon('#iD-icon-close'));
+
+ keybinding
+ .on('⌫', shaded.close)
+ .on('⎋', shaded.close);
+
+ d3_select(document)
+ .call(keybinding);
+ }
+
+ modal
+ .append('div')
+ .attr('class', 'content');
+
+ if (animate) {
+ shaded.transition().style('opacity', 1);
+ } else {
+ shaded.style('opacity', 1);
+ }
+
+ return shaded;
}
diff --git a/modules/ui/restore.js b/modules/ui/restore.js
index 7330e8bf4..ae15ee0f8 100644
--- a/modules/ui/restore.js
+++ b/modules/ui/restore.js
@@ -3,70 +3,68 @@ import { uiModal } from './modal';
export function uiRestore(context) {
+ return function(selection) {
+ if (!context.history().lock() || !context.history().restorableChanges()) return;
- return function(selection) {
- if (!context.history().lock() || !context.history().restorableChanges())
- return;
+ let modalSelection = uiModal(selection, true);
- var modalSelection = uiModal(selection, true);
+ modalSelection.select('.modal')
+ .attr('class', 'modal fillL');
- modalSelection.select('.modal')
- .attr('class', 'modal fillL');
+ let introModal = modalSelection.select('.content');
- var introModal = modalSelection.select('.content');
+ introModal
+ .append('div')
+ .attr('class', 'modal-section')
+ .append('h3')
+ .text(t('restore.heading'));
- introModal
- .append('div')
- .attr('class', 'modal-section')
- .append('h3')
- .text(t('restore.heading'));
+ introModal
+ .append('div')
+ .attr('class','modal-section')
+ .append('p')
+ .text(t('restore.description'));
- introModal
- .append('div')
- .attr('class','modal-section')
- .append('p')
- .text(t('restore.description'));
+ let buttonWrap = introModal
+ .append('div')
+ .attr('class', 'modal-actions');
- var buttonWrap = introModal
- .append('div')
- .attr('class', 'modal-actions');
+ let restore = buttonWrap
+ .append('button')
+ .attr('class', 'restore')
+ .on('click', () => {
+ context.history().restore();
+ modalSelection.remove();
+ });
- var restore = buttonWrap
- .append('button')
- .attr('class', 'restore')
- .on('click', function() {
- context.history().restore();
- modalSelection.remove();
- });
+ restore
+ .append('svg')
+ .attr('class', 'logo logo-restore')
+ .append('use')
+ .attr('xlink:href', '#iD-logo-restore');
- restore
- .append('svg')
- .attr('class', 'logo logo-restore')
- .append('use')
- .attr('xlink:href', '#iD-logo-restore');
+ restore
+ .append('div')
+ .text(t('restore.restore'));
- restore
- .append('div')
- .text(t('restore.restore'));
+ let reset = buttonWrap
+ .append('button')
+ .attr('class', 'reset')
+ .on('click', () => {
+ context.history().clearSaved();
+ modalSelection.remove();
+ });
- var reset = buttonWrap
- .append('button')
- .attr('class', 'reset')
- .on('click', function() {
- context.history().clearSaved();
- modalSelection.remove();
- });
+ reset
+ .append('svg')
+ .attr('class', 'logo logo-reset')
+ .append('use')
+ .attr('xlink:href', '#iD-logo-reset');
- reset
- .append('svg')
- .attr('class', 'logo logo-reset')
- .append('use')
- .attr('xlink:href', '#iD-logo-reset');
+ reset
+ .append('div')
+ .text(t('restore.reset'));
- reset
- .append('div')
- .text(t('restore.reset'));
-
- restore.node().focus();
- };
+ restore.node().focus();
+ };
}
diff --git a/modules/ui/splash.js b/modules/ui/splash.js
index 61fd7cd01..b73ef229e 100644
--- a/modules/ui/splash.js
+++ b/modules/ui/splash.js
@@ -4,77 +4,97 @@ import { uiModal } from './modal';
export function uiSplash(context) {
+ return (selection) => {
+ // Exception - if there are restorable changes, skip this splash screen.
+ // This is because we currently only support one `uiModal` at a time
+ // and we need to show them `uiRestore`` instead of this one.
+ if (context.history().lock() && context.history().restorableChanges()) return;
- return function(selection) {
- if (context.storage('sawSplash'))
- return;
+ // If user has not seen this version of the privacy policy, show the splash again.
+ let updateMessage = '';
+ const sawPrivacyVersion = context.storage('sawPrivacyVersion');
+ if (sawPrivacyVersion !== context.privacyVersion) {
+ updateMessage = t('splash.privacy_update');
+ context.storage('sawSplash', null);
+ }
- context.storage('sawSplash', true);
+ if (context.storage('sawSplash')) return;
- var modalSelection = uiModal(selection);
+ context.storage('sawSplash', true);
+ context.storage('sawPrivacyVersion', context.privacyVersion);
- modalSelection.select('.modal')
- .attr('class', 'modal-splash modal');
+ let modalSelection = uiModal(selection);
- var introModal = modalSelection.select('.content')
- .append('div')
- .attr('class', 'fillL');
+ modalSelection.select('.modal')
+ .attr('class', 'modal-splash modal');
- introModal
- .append('div')
- .attr('class','modal-section')
- .append('h3').text(t('splash.welcome'));
+ let introModal = modalSelection.select('.content')
+ .append('div')
+ .attr('class', 'fillL');
- introModal
- .append('div')
- .attr('class','modal-section')
- .append('p')
- .html(t('splash.text', {
- version: context.version,
- website: 'ideditor.com',
- github: 'github.com'
- }));
+ introModal
+ .append('div')
+ .attr('class','modal-section')
+ .append('h3')
+ .text(t('splash.welcome'));
- var buttonWrap = introModal
- .append('div')
- .attr('class', 'modal-actions');
+ let modalSection = introModal
+ .append('div')
+ .attr('class','modal-section');
- var walkthrough = buttonWrap
- .append('button')
- .attr('class', 'walkthrough')
- .on('click', function() {
- context.container().call(uiIntro(context));
- modalSelection.close();
- });
+ modalSection
+ .append('p')
+ .html(t('splash.text', {
+ version: context.version,
+ website: 'ideditor.blog',
+ github: 'github.com'
+ }));
- walkthrough
- .append('svg')
- .attr('class', 'logo logo-walkthrough')
- .append('use')
- .attr('xlink:href', '#iD-logo-walkthrough');
+ modalSection
+ .append('p')
+ .html(t('splash.privacy', {
+ updateMessage: updateMessage,
+ here: 'here'
+ }));
- walkthrough
- .append('div')
- .text(t('splash.walkthrough'));
+ let buttonWrap = introModal
+ .append('div')
+ .attr('class', 'modal-actions');
- var startEditing = buttonWrap
- .append('button')
- .attr('class', 'start-editing')
- .on('click', modalSelection.close);
+ let walkthrough = buttonWrap
+ .append('button')
+ .attr('class', 'walkthrough')
+ .on('click', () => {
+ context.container().call(uiIntro(context));
+ modalSelection.close();
+ });
- startEditing
- .append('svg')
- .attr('class', 'logo logo-features')
- .append('use')
- .attr('xlink:href', '#iD-logo-features');
+ walkthrough
+ .append('svg')
+ .attr('class', 'logo logo-walkthrough')
+ .append('use')
+ .attr('xlink:href', '#iD-logo-walkthrough');
- startEditing
- .append('div')
- .text(t('splash.start'));
+ walkthrough
+ .append('div')
+ .text(t('splash.walkthrough'));
+ let startEditing = buttonWrap
+ .append('button')
+ .attr('class', 'start-editing')
+ .on('click', modalSelection.close);
- modalSelection.select('button.close')
- .attr('class','hide');
+ startEditing
+ .append('svg')
+ .attr('class', 'logo logo-features')
+ .append('use')
+ .attr('xlink:href', '#iD-logo-features');
- };
+ startEditing
+ .append('div')
+ .text(t('splash.start'));
+
+ modalSelection.select('button.close')
+ .attr('class','hide');
+ };
}