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'); + }; }