From dabf8556ddeb4a9d20298b0e5c1f13f13e8ac342 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 30 Mar 2017 17:48:18 -0400 Subject: [PATCH] Add continue/cleanups/restarts for each step of lines chapter --- data/core.yaml | 20 +-- dist/locales/en.json | 20 +-- modules/ui/intro/line.js | 303 ++++++++++++++++++++++++++------------- 3 files changed, 221 insertions(+), 122 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index fec550471..879e8bcd8 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -821,11 +821,11 @@ en: search: "You can also search for features in the current view, or worldwide. **Search for '{name}'**" choose: "**Choose {name} from the list to select it.**" chosen: "Great! {name} is now selected. See how the fields displayed for a street are different than the fields displayed for the town hall. **Close the feature editor by pressing the {button} button.**" - play: "Try moving the map and clicking on some other features. **When you are ready to continue to the next chapter, click '{next}'.**" + play: "Try moving the map and clicking on some other features to see what kinds of things can be added to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**" points: title: "Points" add: "Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**" - place: "The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**" + place: "The point can be placed by clicking on the map or pressing the spacebar. **Click the map to place the new point on top of the building.**" search: "There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**" choose: "**Choose Cafe from the list.**" describe: "The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**" @@ -834,28 +834,28 @@ en: fixname: "**Change the name, then click the {button} button to close the feature editor.**" rightclick: "You can right-click on features to see the list of operations that can be performed on them. **Right-click to select the point you created.**" delete: "**Click on the {button} button to delete the point.**" - play: "Try adding a few more points. **When you are ready to continue to the next chapter, click '{next}'.**" + play: "Now that you know how to create points, try creating a few more points for practice! **When you are ready to continue to the next chapter, click '{next}'.**" areas: title: "Areas" add: "Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**" - corner: "Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**" + corner: "Areas are drawn by placing nodes that mark the boundary of the area. **Click or press spacebar to place a starting node on one of the corners of the playground.**" place: "Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**" search: "**Search for '{name}'.**" choose: "**Choose Playground from the list.**" describe: "**Add a name, then click the {button} button to close the feature editor**" - play: "Try drawing a few more areas. **When you are ready to continue to the next chapter, click '{next}'.**" + play: "Good job! Try drawing a few more areas. Explore the presets and see what other kinds of areas you can add to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**" lines: title: "Lines" add: "Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**" - start: "**Start the line by clicking on the end of the road.**" - intersect: "Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**" - finish: "Lines can be finished by clicking on the last node again. **Finish drawing the road.**" + start: "Here is a road that is missing. Let's add it! In OpenStreetMap, lines should be drawn down the center of the road. You can drag the map while drawing if necessary. **Start a new line by clicking on the end of the road.**" + intersect: "Click or press spacebar to add more nodes to the line. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**" + finish: "Continue drawing the line for the new road. Remember that you can drag and zoom the map if needed. When you are finished drawing, click on the last node again. **Finish drawing the road.**" road: "**Select Road from the list**" residential: "There are different types of roads, the most common of which is Residential. **Choose the Residential road type**" describe: "**Name the road, then click the {button} button to close the feature editor.**" - restart: "The road needs to intersect {name}." + restart: "The road needs to intersect {name}. Let's try again!" wrong_preset: "You didn't select the Residential road type. **Click here to choose again**" - play: "Try drawing a few more lines. **When you are ready to continue to the next chapter, click '{next}'.**" + play: "You added a new road! Try drawing a few more lines, and see what other kinds of lines you can add to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**" startediting: title: "Start Editing" help: "You can replay this walkthrough or view more documentation by clicking the {button} Help button." diff --git a/dist/locales/en.json b/dist/locales/en.json index 897ad491b..bea8fbd06 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -680,12 +680,12 @@ "search": "You can also search for features in the current view, or worldwide. **Search for '{name}'**", "choose": "**Choose {name} from the list to select it.**", "chosen": "Great! {name} is now selected. See how the fields displayed for a street are different than the fields displayed for the town hall. **Close the feature editor by pressing the {button} button.**", - "play": "Try moving the map and clicking on some other features. **When you are ready to continue to the next chapter, click '{next}'.**" + "play": "Try moving the map and clicking on some other features to see what kinds of things can be added to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**" }, "points": { "title": "Points", "add": "Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**", - "place": "The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**", + "place": "The point can be placed by clicking on the map or pressing the spacebar. **Click the map to place the new point on top of the building.**", "search": "There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**", "choose": "**Choose Cafe from the list.**", "describe": "The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**", @@ -694,30 +694,30 @@ "fixname": "**Change the name, then click the {button} button to close the feature editor.**", "rightclick": "You can right-click on features to see the list of operations that can be performed on them. **Right-click to select the point you created.**", "delete": "**Click on the {button} button to delete the point.**", - "play": "Try adding a few more points. **When you are ready to continue to the next chapter, click '{next}'.**" + "play": "Now that you know how to create points, try creating a few more points for practice! **When you are ready to continue to the next chapter, click '{next}'.**" }, "areas": { "title": "Areas", "add": "Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**", - "corner": "Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**", + "corner": "Areas are drawn by placing nodes that mark the boundary of the area. **Click or press spacebar to place a starting node on one of the corners of the playground.**", "place": "Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**", "search": "**Search for '{name}'.**", "choose": "**Choose Playground from the list.**", "describe": "**Add a name, then click the {button} button to close the feature editor**", - "play": "Try drawing a few more areas. **When you are ready to continue to the next chapter, click '{next}'.**" + "play": "Good job! Try drawing a few more areas. Explore the presets and see what other kinds of areas you can add to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**" }, "lines": { "title": "Lines", "add": "Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**", - "start": "**Start the line by clicking on the end of the road.**", - "intersect": "Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**", - "finish": "Lines can be finished by clicking on the last node again. **Finish drawing the road.**", + "start": "Here is a road that is missing. Let's add it! In OpenStreetMap, lines should be drawn down the center of the road. You can drag the map while drawing if necessary. **Start a new line by clicking on the end of the road.**", + "intersect": "Click or press spacebar to add more nodes to the line. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**", + "finish": "Continue drawing the line for the new road. Remember that you can drag and zoom the map if needed. When you are finished drawing, click on the last node again. **Finish drawing the road.**", "road": "**Select Road from the list**", "residential": "There are different types of roads, the most common of which is Residential. **Choose the Residential road type**", "describe": "**Name the road, then click the {button} button to close the feature editor.**", - "restart": "The road needs to intersect {name}.", + "restart": "The road needs to intersect {name}. Let's try again!", "wrong_preset": "You didn't select the Residential road type. **Click here to choose again**", - "play": "Try drawing a few more lines. **When you are ready to continue to the next chapter, click '{next}'.**" + "play": "You added a new road! Try drawing a few more lines, and see what other kinds of lines you can add to OpenStreetMap. **When you are ready to continue to the next chapter, click '{next}'.**" }, "startediting": { "title": "Start Editing", diff --git a/modules/ui/intro/line.js b/modules/ui/intro/line.js index 84d6da0ec..2f155b6a5 100644 --- a/modules/ui/intro/line.js +++ b/modules/ui/intro/line.js @@ -2,19 +2,17 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../../util/locale'; import { utilRebind } from '../../util/rebind'; -import { utilBindOnce } from '../../util/bind_once'; import { icon, pad } from './helper'; export function uiIntroLine(context, reveal) { var dispatch = d3.dispatch('done'), timeouts = [], - centroid = [-85.62830, 41.95699], midpoint = [-85.62975395449628, 41.95787501510204], start = [-85.6297754121684, 41.95805253325314], intersection = [-85.62974496187628, 41.95742515554585], targetId = 'w17965351', - drawId = null; + lineId = null; var chapter = { @@ -43,143 +41,244 @@ export function uiIntroLine(context, reveal) { .append('use') .attr('xlink:href', '#feature-images'); - context.on('enter.intro', startLine); - } - - - function startLine(mode) { - if (mode.id !== 'add-line') return; - drawId = null; - context.on('enter.intro', drawLine); - - var padding = 150 * Math.pow(2, context.map().zoom() - 18); - var pointBox = pad(start, padding, context); - reveal(pointBox, t('intro.lines.start')); - - context.map().on('move.intro drawn.intro', function() { - padding = 150 * Math.pow(2, context.map().zoom() - 18); - pointBox = pad(start, padding, context); - reveal(pointBox, t('intro.lines.start'), {duration: 0}); + context.on('enter.intro', function(mode) { + if (mode.id !== 'add-line') return; + continueTo(startLine); }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } } - function drawLine(mode) { - if (mode.id !== 'draw-line') return; - drawId = mode.selectedIDs()[0]; - context.history().on('change.intro', checkIntersection); - context.on('enter.intro', retry); - - var padding = 300 * Math.pow(2, context.map().zoom() - 19); - var pointBox = pad(midpoint, padding, context); - reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')})); - - context.map().on('move.intro drawn.intro', function() { - padding = 300 * Math.pow(2, context.map().zoom() - 19); - pointBox = pad(midpoint, padding, context); - reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0}); - }); - } - - - // ended line before creating intersection - function retry(mode) { - if (mode.id !== 'select') return; - context.history().on('change.intro', null); - var pointBox = pad(intersection, 30, context); - reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')})); - d3.select(window).on('mousedown.intro', eventCancel, true); - - timeout(chapter.restart, 3000); - } - - - function checkIntersection() { - - function joinedTargetWay() { - var drawEntity = drawId && context.hasEntity(drawId); - if (!drawEntity) { - chapter.restart(); - return false; - } - - var drawNodes = context.graph().childNodes(drawEntity); - return _.some(drawNodes, function(node) { - return _.some(context.graph().parentWays(node), function(parent) { - return parent.id === targetId; - }); - }); + function startLine() { + if (context.mode().id !== 'add-line') { + return chapter.restart(); } - if (joinedTargetWay()) { - context.history().on('change.intro', null); - context.on('enter.intro', enterSelect); + lineId = null; - var padding = 900 * Math.pow(2, context.map().zoom() - 19); - var pointBox = pad(centroid, padding, context); - reveal(pointBox, t('intro.lines.finish')); + var padding = 100 * Math.pow(2, context.map().zoom() - 18); + var box = pad(start, padding, context); + box.height = box.height + 100; + reveal(box, t('intro.lines.start')); + + context.map().on('move.intro drawn.intro', function() { + padding = 100 * Math.pow(2, context.map().zoom() - 18); + box = pad(start, padding, context); + box.height = box.height + 100; + reveal(box, t('intro.lines.start'), { duration: 0 }); + }); + + context.on('enter.intro', function(mode) { + if (mode.id !== 'draw-line') return chapter.restart(); + continueTo(drawLine); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.on('enter.intro', null); + nextStep(); + } + } + + + function drawLine() { + if (context.mode().id !== 'draw-line') { + return chapter.restart(); + } + + lineId = context.mode().selectedIDs()[0]; + context.map().centerEase(midpoint); + + timeout(function() { + var padding = 200 * Math.pow(2, context.map().zoom() - 18.5); + var box = pad(midpoint, padding, context); + box.height = box.height * 2; + reveal(box, + t('intro.lines.intersect', { name: t('intro.graph.flower_st') }) + ); context.map().on('move.intro drawn.intro', function() { - padding = 900 * Math.pow(2, context.map().zoom() - 19); - pointBox = pad(centroid, padding, context); - reveal(pointBox, t('intro.lines.finish'), {duration: 0}); + padding = 200 * Math.pow(2, context.map().zoom() - 18.5); + box = pad(midpoint, padding, context); + box.height = box.height * 2; + reveal(box, + t('intro.lines.intersect', { name: t('intro.graph.flower_st') }), + { duration: 0 } + ); }); + }, 260); // after easing.. + + context.history().on('change.intro', function() { + var entity = lineId && context.hasEntity(lineId); + if (!entity) return chapter.restart(); + + if (isLineConnected()) { + continueTo(finishLine); + } + }); + + context.on('enter.intro', function(mode) { + if (mode.id === 'draw-line') + return; + else if (mode.id === 'select') { + var box = pad(intersection, 80, context); + reveal(box, t('intro.lines.restart', { name: t('intro.graph.flower_st') })); + d3.select(window).on('mousedown.intro', eventCancel, true); + timeout(chapter.restart, 3000); + return; + } + else + return chapter.restart(); + }); + + function continueTo(nextStep) { + context.map().on('move.intro drawn.intro', null); + context.history().on('change.intro', null); + context.on('enter.intro', null); + nextStep(); } } - function enterSelect(mode) { - if (mode.id !== 'select') return; - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - d3.select('#curtain').style('pointer-events', 'all'); - presetCategory(); + function isLineConnected() { + var entity = lineId && context.hasEntity(lineId); + if (!entity) return false; + + var drawNodes = context.graph().childNodes(entity); + return _.some(drawNodes, function(node) { + return _.some(context.graph().parentWays(node), function(parent) { + return parent.id === targetId; + }); + }); } - function presetCategory() { + function finishLine() { + if (context.mode().id !== 'draw-line') return chapter.restart(); + var entity = lineId && context.hasEntity(lineId); + if (!entity) return chapter.restart(); + + context.map().centerEase(intersection); + + reveal('#surface', t('intro.lines.finish')); + + context.on('enter.intro', function(mode) { + if (mode.id === 'draw-line') + return; + else if (mode.id === 'select') + return continueTo(enterSelect); + else + return chapter.restart(); + }); + + function continueTo(nextStep) { + context.on('enter.intro', null); + nextStep(); + } + } + + + function enterSelect() { + if (context.mode().id !== 'select') { + return chapter.restart(); + } + + context.on('exit.intro', function() { + return chapter.restart(); + }); + + var button = d3.select('.preset-category-road .preset-list-button'); + if (button.empty()) return chapter.restart(); + timeout(function() { - d3.select('#curtain').style('pointer-events', 'none'); - var road = d3.select('.preset-category-road .preset-list-button'); - reveal(road.node(), t('intro.lines.road')); - utilBindOnce(road, 'click.intro', roadCategory); + reveal(button.node(), t('intro.lines.road')); + button.on('click.intro', function() { continueTo(roadCategory); }); }, 500); + + function continueTo(nextStep) { + d3.select('.preset-list-button').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } } function roadCategory() { + if (context.mode().id !== 'select') { + return chapter.restart(); + } + + context.on('exit.intro', function() { + return chapter.restart(); + }); + + var subgrid = d3.select('.preset-category-road .subgrid'); + if (subgrid.empty()) return chapter.restart(); + + subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button') + .on('click.intro', function() { + continueTo(retryPreset); + }); + + subgrid.selectAll('.preset-highway-residential .preset-list-button') + .on('click.intro', function() { + continueTo(roadDetails); + }); + timeout(function() { - var grid = d3.select('.subgrid'); - reveal(grid.node(), t('intro.lines.residential')); - utilBindOnce(grid.selectAll(':not(.preset-highway-residential) .preset-list-button'), - 'click.intro', retryPreset); - utilBindOnce(grid.selectAll('.preset-highway-residential .preset-list-button'), - 'click.intro', roadDetails); + reveal(subgrid.node(), t('intro.lines.residential')); }, 500); + + function continueTo(nextStep) { + d3.select('.preset-list-button').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } } // selected wrong road type function retryPreset() { + if (context.mode().id !== 'select') { + return chapter.restart(); + } + + context.on('exit.intro', function() { + return chapter.restart(); + }); + timeout(function() { - var preset = d3.select('.entity-editor-pane .preset-list-button'); - reveal(preset.node(), t('intro.lines.wrong_preset')); - utilBindOnce(preset, 'click.intro', presetCategory); + var button = d3.select('.entity-editor-pane .preset-list-button'); + reveal(button.node(), t('intro.lines.wrong_preset')); + button.on('click.intro', function() { + continueTo(enterSelect); + }); }, 500); + + function continueTo(nextStep) { + d3.select('.preset-list-button').on('click.intro', null); + context.on('exit.intro', null); + nextStep(); + } } function roadDetails() { - reveal('.pane', - t('intro.lines.describe', { button: icon('#icon-apply', 'pre-text') })); - context.on('exit.intro', function() { - advance(); + continueTo(play); }); - function advance() { + reveal('.pane', + t('intro.lines.describe', { button: icon('#icon-apply', 'pre-text') }) + ); + + function continueTo(nextStep) { context.on('exit.intro', null); - play(); + nextStep(); } } @@ -197,7 +296,7 @@ export function uiIntroLine(context, reveal) { chapter.enter = function() { context.history().reset('initial'); - context.map().zoom(18).centerEase(start); + context.map().zoom(18.5).centerEase(start); addLine(); }; @@ -205,11 +304,11 @@ export function uiIntroLine(context, reveal) { chapter.exit = function() { timeouts.forEach(window.clearTimeout); d3.select(window).on('mousedown.intro', null, true); - d3.select('#curtain').style('pointer-events', 'none'); context.on('enter.intro', null); context.on('exit.intro', null); context.map().on('move.intro drawn.intro', null); context.history().on('change.intro', null); + d3.select('.preset-list-button').on('click.intro', null); };