Add map aside to challenges
This commit is contained in:
@ -1115,3 +1115,4 @@ code {
|
||||
@import "jobs.less";
|
||||
@import "challenge.less";
|
||||
@import "toastr.less";
|
||||
@import "map.less";
|
||||
|
125
client/less/map.less
Normal file
125
client/less/map.less
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* based off of https://github.com/gitterHQ/sidecar
|
||||
* license: MIT
|
||||
*/
|
||||
.map-aside {
|
||||
|
||||
z-index: 20000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 60%;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
background-color: @body-bg;
|
||||
border-left: 1px solid #333;
|
||||
box-shadow: -12px 0 18px 0 rgba(50, 50, 50, 0.3);
|
||||
|
||||
transition: transform 0.3s cubic-bezier(0.16, 0.22, 0.22, 1.7);
|
||||
|
||||
&.is-collapsed {
|
||||
transform: translateX(110%);
|
||||
}
|
||||
|
||||
/* Add some "extension" so that there isn't a gap
|
||||
* when we translate(via animation) more than 100% */
|
||||
&:after {
|
||||
content: '';
|
||||
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
bottom: 0;
|
||||
right: -100%;
|
||||
|
||||
background-color: @body-bg;
|
||||
}
|
||||
|
||||
@media (max-width: 1150px) {
|
||||
left: 45%;
|
||||
}
|
||||
@media (max-width: 944px) {
|
||||
left: 30%;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
left: 15%;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
left: 0;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
& > .map-aside-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
border: 0;
|
||||
overflow: auto
|
||||
}
|
||||
}
|
||||
|
||||
.map-aside-action-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
padding-bottom: 0.7em;
|
||||
|
||||
background: linear-gradient(to bottom, #ffffff 0%, #ffffff 50%, rgba(255, 255, 255, 0) 100%);
|
||||
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.map-aside-action-item {
|
||||
display: flex;
|
||||
/* main axis */
|
||||
justify-content: center;
|
||||
/* cross axis */
|
||||
align-items: center;
|
||||
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
||||
opacity: 0.65;
|
||||
background: none;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 22px 22px;
|
||||
border: 0;
|
||||
outline: none;
|
||||
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
filter: hue-rotate(80deg) saturate(150);
|
||||
}
|
||||
}
|
||||
|
||||
.map-aside-action-pop-out {
|
||||
margin-right: -4px;
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcxLjQyOSIgZmlsbD0iIzNhMzEzMyI+PHBhdGggZD0iTTE1Ny4xNDMsMTAzLjU3MXYzNS43MTRjMCw4Ljg1NC0zLjE0NCwxNi40MjYtOS40MzEsMjIuNzEzcy0xMy44NTgsOS40MzEtMjIuNzEyLDkuNDMxSDMyLjE0MyBjLTguODU0LDAtMTYuNDI1LTMuMTQ0LTIyLjcxMi05LjQzMVMwLDE0OC4xNCwwLDEzOS4yODVWNDYuNDI5YzAtOC44NTQsMy4xNDQtMTYuNDI1LDkuNDMxLTIyLjcxMiBjNi4yODctNi4yODcsMTMuODU4LTkuNDMxLDIyLjcxMi05LjQzMWg3OC41NzJjMS4wNDEsMCwxLjg5NiwwLjMzNSwyLjU2NiwxLjAwNGMwLjY3LDAuNjcsMS4wMDQsMS41MjUsMS4wMDQsMi41NjdWMjUgYzAsMS4wNDItMC4zMzQsMS44OTctMS4wMDQsMi41NjdjLTAuNjcsMC42Ny0xLjUyNSwxLjAwNC0yLjU2NiwxLjAwNEgzMi4xNDNjLTQuOTExLDAtOS4xMTUsMS43NDktMTIuNjEyLDUuMjQ2IHMtNS4yNDYsNy43MDEtNS4yNDYsMTIuNjEydjkyLjg1NmMwLDQuOTExLDEuNzQ5LDkuMTE1LDUuMjQ2LDEyLjYxMnM3LjcwMSw1LjI0NSwxMi42MTIsNS4yNDVIMTI1YzQuOTEsMCw5LjExNS0xLjc0OCwxMi42MTEtNS4yNDUgYzMuNDk3LTMuNDk3LDUuMjQ2LTcuNzAxLDUuMjQ2LTEyLjYxMnYtMzUuNzE0YzAtMS4wNDIsMC4zMzQtMS44OTcsMS4wMDQtMi41NjdjMC42Ny0wLjY2OSwxLjUyNS0xLjAwNCwyLjU2Ny0xLjAwNGg3LjE0MyBjMS4wNDIsMCwxLjg5NywwLjMzNSwyLjU2NywxLjAwNEMxNTYuODA5LDEwMS42NzQsMTU3LjE0MywxMDIuNTI5LDE1Ny4xNDMsMTAzLjU3MXogTTIwMCw3LjE0M3Y1Ny4xNDMgYzAsMS45MzUtMC43MDcsMy42MDktMi4xMjEsNS4wMjJjLTEuNDEzLDEuNDE0LTMuMDg4LDIuMTIxLTUuMDIxLDIuMTIxYy0xLjkzNSwwLTMuNjA5LTAuNzA3LTUuMDIyLTIuMTIxbC0xOS42NDQtMTkuNjQzIGwtNzIuNzY3LDcyLjc2OWMtMC43NDQsMC43NDQtMS42LDEuMTE1LTIuNTY3LDEuMTE1cy0xLjgyMy0wLjM3MS0yLjU2Ny0xLjExNUw3Ny41NjcsMTA5LjcxYy0wLjc0NC0wLjc0NC0xLjExNi0xLjYtMS4xMTYtMi41NjcgYzAtMC45NjcsMC4zNzItMS44MjIsMS4xMTYtMi41NjZsNzIuNzY4LTcyLjc2OGwtMTkuNjQ0LTE5LjY0M2MtMS40MTMtMS40MTQtMi4xMi0zLjA4OC0yLjEyLTUuMDIyYzAtMS45MzUsMC43MDctMy42MDksMi4xMi01LjAyMiBDMTMyLjEwNSwwLjcwNywxMzMuNzc5LDAsMTM1LjcxNSwwaDU3LjE0M2MxLjkzNCwwLDMuNjA4LDAuNzA3LDUuMDIxLDIuMTIxQzE5OS4yOTMsMy41MzQsMjAwLDUuMjA4LDIwMCw3LjE0M3oiLz48L3N2Zz4=)
|
||||
}
|
||||
|
||||
.map-aside-action-collapse {
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzEuNDI5IDE3MS40MjkiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik0xMjIuNDMzLDEwNi4xMzhsLTE2LjI5NSwxNi4yOTVjLTAuNzQ0LDAuNzQ0LTEuNiwxLjExNi0yLjU2NiwxLjExNmMtMC45NjgsMC0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTUuMjktMTUuMjkgbC0xNS4yOSwxNS4yOWMtMC43NDQsMC43NDQtMS42LDEuMTE2LTIuNTY3LDEuMTE2cy0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTYuMjk0LTE2LjI5NWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY2IGMwLTAuOTY4LDAuMzcyLTEuODIzLDEuMTE2LTIuNTY3bDE1LjI5LTE1LjI5bC0xNS4yOS0xNS4yOWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY3czAuMzcyLTEuODIzLDEuMTE2LTIuNTY3IEw2NS4yOSw0OC45OTZjMC43NDQtMC43NDQsMS42LTEuMTE2LDIuNTY3LTEuMTE2czEuODIzLDAuMzcyLDIuNTY3LDEuMTE2bDE1LjI5LDE1LjI5bDE1LjI5LTE1LjI5IGMwLjc0NC0wLjc0NCwxLjYtMS4xMTYsMi41NjctMS4xMTZjMC45NjcsMCwxLjgyMiwwLjM3MiwyLjU2NiwxLjExNmwxNi4yOTUsMTYuMjk0YzAuNzQ0LDAuNzQ0LDEuMTE2LDEuNiwxLjExNiwyLjU2NyBzLTAuMzcyLDEuODIzLTEuMTE2LDIuNTY3bC0xNS4yOSwxNS4yOWwxNS4yOSwxNS4yOWMwLjc0NCwwLjc0NCwxLjExNiwxLjYsMS4xMTYsMi41NjcgQzEyMy41NDksMTA0LjUzOSwxMjMuMTc3LDEwNS4zOTQsMTIyLjQzMywxMDYuMTM4eiBNMTQ2LjQyOSw4NS43MTRjMC0xMS4wMTItMi43MTctMjEuMTY4LTguMTQ4LTMwLjQ2OSBzLTEyLjc5Ny0xNi42NjctMjIuMDk4LTIyLjA5OFM5Ni43MjYsMjUsODUuNzE0LDI1cy0yMS4xNjgsMi43MTYtMzAuNDY5LDguMTQ3UzM4LjU3OSw0NS45NDUsMzMuMTQ3LDU1LjI0NlMyNSw3NC43MDMsMjUsODUuNzE0IHMyLjcxNiwyMS4xNjgsOC4xNDcsMzAuNDY5czEyLjc5NywxNi42NjYsMjIuMDk4LDIyLjA5OHMxOS40NTcsOC4xNDgsMzAuNDY5LDguMTQ4czIxLjE2OC0yLjcxNywzMC40NjktOC4xNDggczE2LjY2Ni0xMi43OTcsMjIuMDk4LTIyLjA5OFMxNDYuNDI5LDk2LjcyNiwxNDYuNDI5LDg1LjcxNHogTTE3MS40MjksODUuNzE0YzAsMTUuNTUxLTMuODMyLDI5Ljg5My0xMS40OTYsNDMuMDI0IGMtNy42NjQsMTMuMTMzLTE4LjA2MiwyMy41My0zMS4xOTQsMzEuMTk0Yy0xMy4xMzIsNy42NjQtMjcuNDc0LDExLjQ5Ni00My4wMjQsMTEuNDk2cy0yOS44OTItMy44MzItNDMuMDI0LTExLjQ5NiBjLTEzLjEzMy03LjY2NC0yMy41MzEtMTguMDYyLTMxLjE5NC0zMS4xOTRDMy44MzIsMTE1LjYwNywwLDEwMS4yNjUsMCw4NS43MTRTMy44MzIsNTUuODIyLDExLjQ5Niw0Mi42OSBjNy42NjQtMTMuMTMzLDE4LjA2Mi0yMy41MzEsMzEuMTk0LTMxLjE5NEM1NS44MjIsMy44MzIsNzAuMTY0LDAsODUuNzE0LDBzMjkuODkzLDMuODMyLDQzLjAyNCwxMS40OTYgYzEzLjEzMyw3LjY2NCwyMy41MywxOC4wNjIsMzEuMTk0LDMxLjE5NEMxNjcuNTk3LDU1LjgyMiwxNzEuNDI5LDcwLjE2NCwxNzEuNDI5LDg1LjcxNHoiLz48L3N2Zz4=)
|
||||
}
|
@ -259,4 +259,13 @@ $(document).ready(function() {
|
||||
window.ga('send', 'event', 'FB_LINK', 'SHARE', 'Facebook map share');
|
||||
window.location.href = link;
|
||||
});
|
||||
|
||||
// map
|
||||
$('#nav-map-btn').on('click', () => {
|
||||
$('.map-aside').removeClass('is-collapsed');
|
||||
});
|
||||
|
||||
$('.map-aside-action-collapse').on('click', () => {
|
||||
$('.map-aside').addClass('is-collapsed');
|
||||
});
|
||||
});
|
||||
|
@ -189,6 +189,15 @@ function getRenderData$(user, challenge$, origChallengeName, solution) {
|
||||
});
|
||||
}
|
||||
|
||||
function getCompletedChallengeIds(user = {}) {
|
||||
// if user
|
||||
// get the id's of all the users completed challenges
|
||||
return !user.completedChallenges ?
|
||||
[] :
|
||||
_.uniq(user.completedChallenges)
|
||||
.map(({ id, _id }) => id || _id);
|
||||
}
|
||||
|
||||
// create a stream of an array of all the challenge blocks
|
||||
function getSuperBlocks$(challenge$, completedChallenges) {
|
||||
return challenge$
|
||||
@ -406,8 +415,19 @@ module.exports = function(app) {
|
||||
|
||||
function showChallenge(req, res, next) {
|
||||
const solution = req.query.solution;
|
||||
const completedChallenges = getCompletedChallengeIds(req.user);
|
||||
|
||||
getRenderData$(req.user, challenge$, req.params.challengeName, solution)
|
||||
Observable.combineLatest(
|
||||
getRenderData$(req.user, challenge$, req.params.challengeName, solution),
|
||||
getSuperBlocks$(challenge$, completedChallenges),
|
||||
({ data, ...rest }, superBlocks) => ({
|
||||
...rest,
|
||||
data: {
|
||||
...data,
|
||||
superBlocks
|
||||
}
|
||||
})
|
||||
)
|
||||
.subscribe(
|
||||
({ type, redirectUrl, message, data }) => {
|
||||
if (message) {
|
||||
@ -553,14 +573,9 @@ module.exports = function(app) {
|
||||
);
|
||||
}
|
||||
|
||||
function showMap({ user = {} }, res, next) {
|
||||
// if user
|
||||
// get the id's of all the users completed challenges
|
||||
const completedChallenges = !user.completedChallenges ?
|
||||
[] :
|
||||
_.uniq(user.completedChallenges).map(({ id, _id }) => id || _id);
|
||||
function showMap({ user }, res, next) {
|
||||
|
||||
getSuperBlocks$(challenge$, completedChallenges)
|
||||
getSuperBlocks$(challenge$, getCompletedChallengeIds(user))
|
||||
.subscribe(
|
||||
superBlocks => {
|
||||
res.render('map/show', {
|
||||
|
@ -5,3 +5,47 @@ script.
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('currentDashedName', typeof common !== 'undefined' && common.dashedName || '');
|
||||
}
|
||||
aside.map-aside.is-collapsed
|
||||
.map-aside-action-bar
|
||||
a.map-aside-action-item.map-aside-action-pop-out(href='/map' target='_blank' aria-label='open map in new tap')
|
||||
button.map-aside-action-item.map-aside-action-collapse(aria-label='close map aside')
|
||||
.map-aside-container
|
||||
.col-xs-10.col-xs-offset-1
|
||||
for superBlock, index in superBlocks
|
||||
for challengeBlock in superBlock.blocks
|
||||
h4.bold #{challengeBlock.name} (#{challengeBlock.time})
|
||||
for challenge in challengeBlock.challenges
|
||||
if challenge.completed
|
||||
p.text-primary.ion-checkmark-circled.padded-ionic-icon.negative-15(name="#{challenge.dashedName}")
|
||||
a(href="/challenges/#{challenge.dashedName}")
|
||||
= challenge.title
|
||||
span.sr-only= " Complete"
|
||||
else
|
||||
if challenge.type === "bonfire"
|
||||
p.ion-asterisk.padded-ionic-icon.negative-15(name="#{challenge.dashedName}")
|
||||
a(name="#{challenge.dashedName}" href="/challenges/#{challenge.dashedName}" class=challenge.isComingSoon ? 'disabled' : '')
|
||||
span= challenge.title
|
||||
span.sr-only= " Incomplete"
|
||||
if challenge.markNew
|
||||
span.text-success.small    
|
||||
strong
|
||||
em New
|
||||
if challengeBlock.isComingSoon
|
||||
span.text-success.small    
|
||||
strong
|
||||
em Coming Soon
|
||||
else
|
||||
p.ion-ios-circle-outline.padded-ionic-icon.negative-15(name="#{challenge.dashedName}")
|
||||
a(name="#{challenge.dashedName}" href="/challenges/#{challenge.dashedName}" class=challenge.isComingSoon ? 'disabled' : '')
|
||||
span= challenge.title
|
||||
span.sr-only= " Incomplete"
|
||||
if challenge.markNew
|
||||
span.text-success.small    
|
||||
strong
|
||||
em New
|
||||
if challengeBlock.isComingSoon
|
||||
span.text-success.small    
|
||||
strong
|
||||
em Coming Soon
|
||||
if (index < superBlocks.length)
|
||||
hr
|
||||
|
@ -9,7 +9,9 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
|
||||
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
|
||||
li
|
||||
a.learn-btn(href='#') Learn
|
||||
li
|
||||
li.hidden-xs
|
||||
a#nav-map-btn(href='#' onclick='return false') Map
|
||||
li.visible-xs
|
||||
a(href='/map') Map
|
||||
li.hidden-xs
|
||||
a#nav-chat-btn(href='#' onclick="return false") Chat
|
||||
|
Reference in New Issue
Block a user