Merge branch 'ux-improvements' of https://github.com/FreeCodeCamp/freecodecamp into ux-improvements

Conflicts:
	controllers/resources.js
	views/account/show.jade
	views/coursewares/showHTML.jade
	views/coursewares/showJS.jade
	views/partials/bonfires.jade
This commit is contained in:
Michael Q Larson
2015-03-21 16:36:35 -07:00
13 changed files with 579 additions and 628 deletions

38
app.js
View File

@ -372,18 +372,6 @@ app.post(
storyController.upvote
);
/**
* Challenge related routes
*/
app.get(
'/challenges/',
challengesController.returnNextChallenge
);
app.get(
'/challenges/:challengeNumber',
challengesController.returnChallenge
);
app.all('/account', passportConf.isAuthenticated);
app.get('/account/api', userController.getAccountAngular);
@ -422,10 +410,10 @@ app.post('/completed-bonfire/', bonfireController.completedBonfire);
* Courseware related routes
*/
app.get('/coursewares/', coursewareController.returnNextCourseware);
app.get('/coursewares/getCoursewareList', coursewareController.showAllCoursewares);
app.get('/challenges/', coursewareController.returnNextCourseware);
app.get('/challenges/getCoursewareList', coursewareController.showAllCoursewares);
app.get(
'/coursewares/:coursewareName',
'/challenges/:coursewareName',
coursewareController.returnIndividualCourseware
);
app.post('/completed-courseware/', coursewareController.completedCourseware);
@ -441,25 +429,7 @@ app.post('/account/delete', userController.postDeleteAccount);
app.get('/account/unlink/:provider', userController.getOauthUnlink);
app.get('/sitemap.xml', resourcesController.sitemap);
/**
* API examples routes.
* accepts a post request. the challenge id req.body.challengeNumber
* and updates user.challengesHash & user.challengesCompleted
*
*/
app.post('/completed-challenge', function (req, res) {
req.user.challengesHash[parseInt(req.body.challengeNumber)] =
Math.round(+new Date() / 1000);
var timestamp = req.user.challengesHash;
var points = 0;
for (var key in timestamp) {
if (timestamp[key] > 0 && req.body.challengeNumber < 54) {
points += 1;
}
}
req.user.points = points;
req.user.save();
});
/**
* OAuth sign-in routes.

View File

@ -23,7 +23,7 @@ exports.showAllCoursewares = function(req, res) {
exports.returnNextCourseware = function(req, res) {
if (!req.user) {
return res.redirect('../coursewares/start-our-challenges');
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
var completed = req.user.completedCoursewares.map(function (elem) {
return elem._id;
@ -50,10 +50,10 @@ exports.returnNextCourseware = function(req, res) {
req.flash('errors', {
msg: "It looks like you've completed all the courses we have available. Good job!"
});
return res.redirect('../coursewares/start-our-challenges');
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
nameString = courseware.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../coursewares/' + nameString);
return res.redirect('../challenges/' + nameString);
});
};
@ -71,14 +71,14 @@ exports.returnIndividualCourseware = function(req, res, next) {
req.flash('errors', {
msg: "404: We couldn't find a challenge with that name. Please double check the name."
});
return res.redirect('/coursewares')
return res.redirect('/challenges');
}
courseware = courseware.pop();
// Redirect to full name if the user only entered a partial
var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../coursewares/' + dashedNameFull);
return res.redirect('../challenges/' + dashedNameFull);
}
var challengeType = {
@ -217,15 +217,19 @@ exports.completedCourseware = function (req, res) {
var isCompletedDate = Math.round(+new Date() / 1000);
var coursewareHash = req.body.coursewareInfo.coursewareHash;
debug('this is the coursewarehash we got', coursewareHash);
req.user.completedCoursewares.push({
_id: coursewareHash,
completedDate: isCompletedDate
completedDate: isCompletedDate,
name: req.body.coursewareInfo.coursewareName
});
var index = req.user.uncompletedCoursewares.indexOf(coursewareHash);
if (index > -1) {
var index = req.user.completedCoursewares.indexOf(coursewareHash);
debug('this is the index of the found courseware', index);
if (index === -1) {
req.user.progressTimestamps.push(Date.now() / 1000 | 0);
req.user.uncompletedCoursewares.splice(index, 1)
req.user.uncompletedCoursewares.splice(index, 1);
}
req.user.save(function (err, user) {
@ -233,7 +237,7 @@ exports.completedCourseware = function (req, res) {
throw err;
}
if (user) {
res.send(true)
res.send(true);
}
});
};

View File

@ -21,6 +21,17 @@ var async = require('async'),
* Resources.
*/
Array.zip = function(left, right, combinerFunction) {
var counter,
results = [];
for (counter = 0; counter < Math.min(left.length, right.length); counter++) {
results.push(combinerFunction(left[counter],right[counter]));
}
return results;
};
module.exports = {
privacy: function privacy(req, res) {
res.render('resources/privacy', {
@ -185,17 +196,36 @@ module.exports = {
req.user.save();
}
}
var date1 = new Date("10/15/2014");
var date1 = new Date('10/15/2014');
var date2 = new Date();
var progressTimestamps = req.user.progressTimestamps;
var now = Date.now() / 1000 | 0;
if (req.user.pointsNeedMigration) {
var challengesHash = req.user.challengesHash;
for(var key in challengesHash) {
for (var key in challengesHash) {
if (challengesHash[key] > 0) {
req.user.progressTimestamps.push(challengesHash[key]);
}
}
var timeStamps = [];
R.keys(req.user.challengesHash).forEach(function(key) {
"use strict";
timeStamps.push({timeStamp: challengesHash[key]});
});
req.user.completedCoursewares = Array.zip(timeStamps, coursewares,
function(left, right) {
"use strict";
return ({
completedDate: left.timeStamp,
_id: right._id,
name: right.name
});
}).filter(function(elem) {
"use strict";
return elem.completedDate !== 0;
});
req.user.pointsNeedMigration = false;
req.user.save();
}
@ -278,6 +308,11 @@ module.exports = {
});
},
getAllCourses: function() {
"use strict";
return coursewares;
},
allCoursewareIds: function() {
return coursewares.map(function(elem) {
return {
@ -298,16 +333,16 @@ module.exports = {
name: elem.name,
difficulty: elem.difficulty,
_id: elem._id
}
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name : elem.name,
name: elem.name,
_id: elem._id
}
};
});
},
whichEnvironment: function() {

View File

@ -95,6 +95,7 @@ exports.getEmailSignup = function(req, res) {
exports.postEmailSignup = function(req, res, next) {
req.assert('email', 'valid email required').isEmail();
var errors = req.validationErrors();
@ -248,7 +249,7 @@ exports.checkExistingUsername = function(req, res) {
exports.checkUniqueEmail = function(req, res) {
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
if (data == 1) {
if (data === 1) {
return res.send(true);
} else {
return res.send(false);
@ -267,18 +268,15 @@ exports.returnUser = function(req, res, next) {
if (err) { debug('Username err: ', err); next(err); }
if (user[0]) {
var user = user[0];
Challenge.find({}, null, {sort: {challengeNumber: 1}}, function (err, c) {
data = {};
progressTimestamps = user.progressTimestamps;
// dummy data to experiment with visualizations
progressTimestamps = [1417117319, 1384091493, 1367893914, 1411547157, 1366875140, 1382614404, 1374973026, 1363495510, 1372229313, 1389795294, 1393820136, 1395425437, 1383366211, 1402063449, 1368384561, 1413460738, 1390013511, 1408510076, 1395530419, 1391588683, 1410480320, 1360219531, 1367248635, 1408531181, 1374214772, 1424038529, 1387468139, 1381934158, 1409278748, 1390696161, 1415933043, 1389573689, 1395703336, 1401223291, 1375539279, 1371229698, 1371990948, 1422236826, 1363017438, 1359619855, 1364850739, 1401982108, 1381270295, 1420063854, 1406540493, 1409122251, 1360775035, 1367712723, 1395305605, 1382037418, 1378402477, 1377563090, 1398930836, 1417371909, 1377417393, 1423763002, 1357511908, 1377375961, 1388374304, 1406416407, 1399463258, 1422593990, 1383434425, 1420200570, 1379435518, 1414512582, 1416263148, 1398635260, 1381815565, 1369178539, 1378414973, 1394409827, 1398463526, 1379564971, 1385849279, 1392899666, 1367053659, 1417730793, 1400112915, 1379923357, 1417768487, 1415779985, 1416150640, 1399820237, 1370498715, 1374800622, 1363924512, 1402497668, 1400146327, 1362456746, 1394935898, 1414980963, 1413942775, 1367606840, 1387144705, 1407906392, 1417213587, 1422640891, 1414033139, 1365323522, 1424661148];
for (i = 0; i < progressTimestamps.length; i++) {
var data = {};
var progressTimestamps = user.progressTimestamps;
for (var i = 0; i < progressTimestamps.length; i++) {
data[progressTimestamps[i].toString()] = 1;
}
res.render('account/show', {
title: 'Camper: ',
title: 'Camper ' + user.profile.username + '\'s portfolio',
username: user.profile.username,
name: user.profile.name,
location: user.profile.location,
@ -288,8 +286,8 @@ exports.returnUser = function(req, res, next) {
twitterHandle: user.profile.twitterHandle,
bio: user.profile.bio,
picture: user.profile.picture,
progressTimestamps: req.user.progressTimestamps,
points: user.progressTimestamps,
progressTimestamps: user.progressTimestamps,
points: user.progressTimestamps.length,
website1Link: user.portfolio.website1Link,
website1Title: user.portfolio.website1Title,
website1Image: user.portfolio.website1Image,
@ -299,11 +297,11 @@ exports.returnUser = function(req, res, next) {
website3Link: user.portfolio.website3Link,
website3Title: user.portfolio.website3Title,
website3Image: user.portfolio.website3Image,
challenges: c,
ch: user.challengesHash,
calender: data,
moment: moment
});
});
} else {
req.flash('errors', {
msg: "404: We couldn't find a page with that url. Please double check the link."

View File

@ -240,30 +240,6 @@ var userSchema = new mongoose.Schema({
53: {
type: Number,
default: 0
},
54: {
type: Number,
default: 0
},
55: {
type: Number,
default: 0
},
56: {
type: Number,
default: 0
},
57: {
type: Number,
default: 0
},
58: {
type: Number,
default: 0
},
59: {
type: Number,
default: 0
}
},
profile: {

View File

@ -864,8 +864,11 @@ iframe.iphone {
}
#cal-heatmap {
margin: 0 auto;
background-color: #fefefe;
width: 361px;
}
.cal-heatmap-container {
background-color: #EEEEEE;
}
//uncomment this to see the dimensions of all elements outlined in red

View File

@ -3,12 +3,11 @@
*/
var widgets = [];
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
var editor = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
lineNumbers: true,
mode: "text/html",
theme: 'monokai',
runnable: true,
//lint: true,
matchBrackets: true,
autoCloseBrackets: true,
scrollbarStyle: 'null',
@ -16,7 +15,6 @@ var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor")
gutters: ["CodeMirror-lint-markers"],
onKeyEvent: doLinting
});
var editor = myCodeMirror;
// Hijack tab key to insert two spaces instead
@ -36,10 +34,6 @@ editor.setOption("extraKeys", {
var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
cm.replaceSelection(spaces);
}
},
"Ctrl-Enter": function() {
bonfireExecute();
return false;
}
});
@ -117,7 +111,7 @@ var allSeeds = '';
});
})();
myCodeMirror.setValue(allSeeds);
editor.setValue(allSeeds);
function doLinting () {
editor.operation(function () {

View File

@ -30,19 +30,19 @@ $(document).ready(function() {
.addClass('animated fadeInDown');
});
$('.completed-challenge').on('click', function() {
$('#complete-challenge-dialog').modal('show');
// Only post to server if there is an authenticated user
if ($('.signup-btn-nav').length < 1) {
l = location.pathname.split('/');
cn = l[l.length - 1];
$.ajax({
type: 'POST',
data: {challengeNumber: cn},
url: '/completed-challenge/'
});
}
});
//$('.completed-challenge').on('click', function() {
// $('#complete-challenge-dialog').modal('show');
// // Only post to server if there is an authenticated user
// if ($('.signup-btn-nav').length < 1) {
// l = location.pathname.split('/');
// cn = l[l.length - 1];
// $.ajax({
// type: 'POST',
// data: {challengeNumber: cn},
// url: '/completed-challenge/'
// });
// }
//});
function completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash) {
@ -74,7 +74,8 @@ $(document).ready(function() {
completedBonfire(didCompleteWith, bonfireSolution, thisBonfireHash);
});
$('#complete-courseware').on('click', function() {
$('#completed-courseware').on('click', function() {
console.log('trying to show modal');
$('#complete-courseware-dialog').modal('show');
});
@ -105,18 +106,22 @@ $(document).ready(function() {
editor.focus();
});
$('#next-courseware-button').on('click', function() {
console.log(passedCoursewareHash);
if ($('.signup-btn-nav').length < 1) {
$.post(
'/completed-courseware',
'/completed-courseware/',
{
coursewareInfo: {
coursewareHash: passedCoursewareHash
coursewareHash: passedCoursewareHash,
coursewareName: passedCoursewareName
}
}).success(
function() {
window.location.href = '/coursewares';
function(res) {
if (res) {
window.location.href = '/challenges';
}
)
}
);
}
});

View File

@ -41,7 +41,7 @@ block content
.col-xs-12.col-sm-12.col-md-3.text-center
.background-svg.img-center
.points-on-top
= "[ " + progressTimestamps.length + " ]"
= "[ " + user.progressTimestamps.length + " ]"
.row
@ -96,13 +96,11 @@ block content
i.fa.icon-beaker
| Try it out
html.
<script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.css" />
.col-xs-12
#cal-heatmap
.hidden-xs.col-sm-12
#cal-heatmap.img-center
script(src="//d3js.org/d3.v3.min.js")
script(src="//cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.min.js")
link(rel="stylesheet", href="//cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.css")
script.
var cal = new CalHeatMap();
var calendar = !{JSON.stringify(calender)};
@ -112,6 +110,7 @@ block content
subDomain: "day",
data: calendar,
cellSize: 15,
align: 'center',
cellRadius: 3,
cellPadding: 2,
tooltip: true,

View File

@ -18,8 +18,6 @@ block content
script(src='/js/lib/codemirror/mode/xml/xml.js')
script(src='/js/lib/codemirror/mode/css/css.js')
script(src='/js/lib/codemirror/mode/htmlmixed/htmlmixed.js')
script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js")
.row.courseware-height
.col-xs-12.col-sm-12.col-md-3.col-lg-3
.well
@ -40,23 +38,19 @@ block content
span.ion-arrow-up-b
| Less information
br
.btn.btn-info#showAllCoursewares
- if (cc)
a.btn.btn-primary.btn-lg.btn-block#complete-courseware
a.btn.btn-primary.btn-lg.btn-block#next-courseware-button
| Go to my next challenge
br
| (ctrl + enter)
script.
var userLoggedIn = true;
- else
a.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
script.
var userLoggedIn = false;
br
ul#testSuite.list-group
br
script(type="text/javascript").
$('#next-courseware-button').attr('disabled', 'disabled');
@ -64,58 +58,28 @@ block content
var challengeSeed = !{JSON.stringify(challengeSeed)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var passedCoursewareName = challengeName;
var prodOrDev = !{JSON.stringify(environment)};
var started = Math.floor(Date.now() / 1000);
var url = '';
var dashed = !{JSON.stringify(dashedName)};
console.log(dashed);
.col-xs-12.col-sm-12.col-md-5.col-lg-6
#mainEditorPanel
form.code
.form-group.codeMirrorView
textarea#codeEditor(autofocus=true)
textarea#codeEditor(autofocus=true, style='display: none;')
script(src='/js/lib/coursewares/coursewaresHCJQFramework_v0.1.1.js')
.col-md-4.col-lg-3
.hidden-xs.hidden-sm
img.iphone-position(src="https://s3.amazonaws.com/freecodecamp/iphone6-frame.png")
iframe.iphone#preview
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
.modal-content
.modal-header.challenge-list-header= compliment
.modal-header.challenge-list-header
= compliment
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
.text-center
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(target="_blank")
i.fa.fa-twitter &nbsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress
#all-coursewares-dialog.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation
.modal-content
.modal-header.all-list-header Challenges
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
.modal-body
include ../partials/coursewares
script.
$.ajax({
url: 'https://api-ssl.bitly.com/v3/shorten?access_token=75e7931a19befaafcf108021b6d597e554b2c5c3&longUrl=http%3A%2F%2Ffreecodecamp.com%2Fchallenges%2F' + dashed + '&format=txt'
})
.success(
function(data) {
console.log(data);
url = "https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge:%20#{name}&url=" + data + "&hashtags=LearnToCode, JavaScript";
$('.btn-twitter').attr('href', url);
}
);
script(src="/js/lib/coursewares/coursewaresHCJQFramework_v0.1.1.js")

View File

@ -66,8 +66,8 @@ block content
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Challenge:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &nbsp;
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &thinsp;
= phrase
- else
a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress

View File

@ -25,6 +25,7 @@ block content
var tests = !{JSON.stringify(tests)};
var passedCoursewareHash = !{JSON.stringify(coursewareHash)};
var challengeName = !{JSON.stringify(name)};
var passedCoursewareName = challengeName;
var started = Math.floor(Date.now() / 1000);
#complete-courseware-dialog.modal(tabindex='-1')
.modal-dialog.animated.zoomIn.fast-animation
@ -36,7 +37,7 @@ block content
.animated.zoomInDown.delay-half
span.completion-icon.ion-checkmark-circled.text-primary
- if (cc)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf, ng-disabled='completedWithForm.$invalid && existingUser.length > 0') Go to my next challenge (ctrl + enter)
a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block#next-courseware-button(name='_csrf', value=_csrf) Go to my next challenge (ctrl + enter)
- if (points && points > 2)
a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20%40FreeCodeCamp%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank")
i.fa.fa-twitter &thinsp;

View File

@ -12,7 +12,7 @@
.collapse.navbar-collapse
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
li
a(href='/courseware') Challenges
a(href='/challenges') Challenges
li
a(href='/chat') Chat
li
@ -26,7 +26,9 @@
else
li
if (user.profile.username)
a(href='/' + user.profile.username) [&thinsp;#{user.points}&thinsp;]
a(href='/' + user.profile.username) [&nbsp;#{user.progressTimestamps.length}&nbsp;]
else
a(href='/account') [&thinsp;#{user.points}&thinsp;]
.hidden-xs