From 60354d24e62779a09fb18a303beb3829f4f4eff4 Mon Sep 17 00:00:00 2001 From: Andrew Cay Date: Fri, 19 Jun 2015 17:47:21 -0700 Subject: [PATCH 01/12] Auto Save, Removed Stamps +more Removed stamps so code stays stored until code has been reset. --> Added a feature which returns if the storage has been saved since last edit. *codeStorage.hasSaved()* --> Changed "localBonfire" to *codeStorage* --- .../coursewaresJSFramework_0.0.6.js | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js index 16073e2554..c15a737e22 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js @@ -42,7 +42,7 @@ editor.setOption("extraKeys", { /* Local Storage Update System By Andrew Cay(Resto) - localBonfire: singleton object that contains properties and methods related to + codeStorage: singleton object that contains properties and methods related to dealing with the localStorage system. The keys work off of the variable challenge_name to make unique identifiers per bonfire @@ -50,54 +50,49 @@ editor.setOption("extraKeys", { Added anonymous version checking system incase of future updates to the system Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage */ -var localBonfire = { +var codeStorage = { version: 0.01, keyVersion:"saveVersion", - keyStamp: challenge_Name + 'Stamp', keyValue: challenge_Name + 'Val', - stampExpireTime: (1000 *60) *60 *24, - updateWait: 1500,// 1.5 seconds + updateWait: 2000,// 2 seconds updateTimeoutId: null }; -localBonfire.getEditorValue = function(){ - return localStorage.getItem(localBonfire.keyValue); +// Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere") +codeStorage.hasSaved = function(){ + return ( updateTimeoutId === null ); }; -localBonfire.getStampTime = function(){ - //localstorage always saves as strings. - return Number.parseInt( localStorage.getItem(localBonfire.keyStamp) ); +codeStorage.getEditorValue = function(){ + return localStorage.getItem(codeStorage.keyValue); }; -localBonfire.isAlive = function(){// returns true if IDE was edited within expire time - return ( Date.now() - localBonfire.getStampTime() < localBonfire.stampExpireTime ); -}; -localBonfire.updateStorage = function(){ +codeStorage.updateStorage = function(){ if(typeof(Storage) !== undefined) { - var stamp = Date.now(), - value = editor.getValue(); - localStorage.setItem(localBonfire.keyValue, value); - localStorage.setItem(localBonfire.keyStamp, stamp); + var value = editor.getValue(); + localStorage.setItem(codeStorage.keyValue, value); } else { + var debugging = false; if( debugging ){ console.log('no web storage'); } } - localBonfire.updateTimeoutId = null; + codeStorage.updateTimeoutId = null; }; // ANONYMOUS 1 TIME UPDATE VERSION (function(){ var savedVersion = localStorage.getItem('saveVersion'); if( savedVersion === null ){ - localStorage.setItem(localBonfire.keyVersion, localBonfire.version);//just write current version + localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version }else{ //do checking if not current version - if( savedVersion !== localBonfire.version ){ + if( savedVersion !== codeStorage.version ){ //update version } } })(); +/// Update local save when editor has changed editor.on('keyup', function(codMir, event){ - window.clearTimeout(localBonfire.updateTimeoutId); - localBonfire.updateTimeoutId = window.setTimeout(localBonfire.updateStorage, localBonfire.updateWait); + window.clearTimeout(codeStorage.updateTimeoutId); + codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait); }); var attempts = 0; @@ -107,7 +102,7 @@ if (attempts) { var resetEditor = function() { editor.setValue(allSeeds); - localBonfire.updateStorage(); + codeStorage.updateStorage(); }; var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { @@ -145,7 +140,7 @@ var allSeeds = ''; }); })(); -editorValue = (localBonfire.isAlive())? localBonfire.getEditorValue() : allSeeds; +editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds; myCodeMirror.setValue(editorValue); From 11305c152b93613d4390dd5f5c528d7519e735b1 Mon Sep 17 00:00:00 2001 From: Andrew Cay Date: Fri, 19 Jun 2015 18:02:49 -0700 Subject: [PATCH 02/12] Update coursewaresJSFramework_0.0.6.js --- .../js/lib/coursewares/coursewaresJSFramework_0.0.6.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js index c15a737e22..dcda001377 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js @@ -55,12 +55,16 @@ var codeStorage = { keyVersion:"saveVersion", keyValue: challenge_Name + 'Val', updateWait: 2000,// 2 seconds - updateTimeoutId: null + updateTimeoutId: null, + eventArray: []//for saves }; // Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere") codeStorage.hasSaved = function(){ return ( updateTimeoutId === null ); }; +codeStorage.onSave = function(func){ + codeStorage.eventArray.push(func); +}; codeStorage.getEditorValue = function(){ return localStorage.getItem(codeStorage.keyValue); }; @@ -75,6 +79,9 @@ codeStorage.updateStorage = function(){ } } codeStorage.updateTimeoutId = null; + codeStorage.eventArray.forEach(function(func){ + func(); + }); }; // ANONYMOUS 1 TIME UPDATE VERSION (function(){ From 437a564e343639ba13708b2edf0a0b84d37b8969 Mon Sep 17 00:00:00 2001 From: Andrew Cay Date: Fri, 19 Jun 2015 18:22:57 -0700 Subject: [PATCH 03/12] Changed dependency on challenge_key to function --- .../coursewaresJSFramework_0.0.6.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js index dcda001377..7d7867fd78 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js @@ -53,10 +53,10 @@ editor.setOption("extraKeys", { var codeStorage = { version: 0.01, keyVersion:"saveVersion", - keyValue: challenge_Name + 'Val', + keyValue: null,//where the value of the editor is saved updateWait: 2000,// 2 seconds updateTimeoutId: null, - eventArray: []//for saves + eventArray: []//for firing saves }; // Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere") codeStorage.hasSaved = function(){ @@ -65,6 +65,9 @@ codeStorage.hasSaved = function(){ codeStorage.onSave = function(func){ codeStorage.eventArray.push(func); }; +codeStorage.setSaveKey = function(key){ + codeStorage.keyValue = key + 'Val'; +}; codeStorage.getEditorValue = function(){ return localStorage.getItem(codeStorage.keyValue); }; @@ -83,25 +86,29 @@ codeStorage.updateStorage = function(){ func(); }); }; -// ANONYMOUS 1 TIME UPDATE VERSION +//Update Version (function(){ var savedVersion = localStorage.getItem('saveVersion'); if( savedVersion === null ){ localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version }else{ - //do checking if not current version if( savedVersion !== codeStorage.version ){ - //update version + //Update version } } })(); + + +///Set everything up one page /// Update local save when editor has changed -editor.on('keyup', function(codMir, event){ +codeStorage.setSaveKey(challenge_Name); +editor.on('keyup', function(){ window.clearTimeout(codeStorage.updateTimeoutId); codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait); }); + var attempts = 0; if (attempts) { attempts = 0; From 4d2b0844ae040775eef1541096f25051c756339d Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 13:35:26 -0700 Subject: [PATCH 04/12] fix use regex to find challenges --- server/boot/challenge.js | 46 +++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 4b3d23e0e0..3c7d85f8c8 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -31,6 +31,7 @@ */ var R = require('ramda'), + debug = require('debug')('freecc:challenges'), utils = require('../utils'), saveUser = require('../utils/rx').saveUser, userMigration = require('../utils/middleware').userMigration, @@ -42,6 +43,18 @@ var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames(); var getMDNLinks = utils.getMDNLinks; +var challangesRegex = /^(bonfire|waypoint|zipline|basejump)/i; +function dasherize(name) { + return ('' + name) + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^a-z0-9\-\.]/gi, ''); +} + +function unDasherize(name) { + return ('' + name).replace(/\-/g, ' '); +} + module.exports = function(app) { var router = app.loopback.Router(); var Challenge = app.models.Challenge; @@ -56,7 +69,7 @@ module.exports = function(app) { router.get('/map', challengeMap); router.get( '/challenges/next-challenge', - ifNoUserRedirectTo('../challenges/learn-how-free-code-camp-works'), + ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'), returnNextChallenge ); @@ -64,7 +77,7 @@ module.exports = function(app) { router.get( '/challenges/', - ifNoUserRedirectTo('../challenges/learn-how-free-code-camp-works'), + ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'), returnCurrentChallenge ); @@ -114,7 +127,7 @@ module.exports = function(app) { function() {}, next, function() { - res.redirect('../challenges/' + nextChallengeName); + res.redirect('/challenges/' + nextChallengeName); } ); } @@ -146,30 +159,43 @@ module.exports = function(app) { function() {}, next, function() { - res.redirect('../challenges/' + nameString); + res.redirect('/challenges/' + nameString); } ); } function returnIndividualChallenge(req, res, next) { - var dashedName = req.params.challengeName; + var origChallengeName = req.params.challengeName; + var unDashedName = unDasherize(origChallengeName); + var challengeName = challangesRegex.test(unDashedName) ? + // remove first word if matches + unDashedName.split(' ').slice(1).join(' ') : + unDashedName; + + debug('looking for ', challengeName); Challenge.findOne( - { where: { dashedName: dashedName }}, + { where: { name: { like: challengeName, options: 'i' } } }, function(err, challenge) { if (err) { return next(err); } // Handle not found if (!challenge) { + debug('did not find challenge for ' + origChallengeName); req.flash('errors', { msg: '404: We couldn\'t find a challenge with the name `' + - dashedName + + origChallengeName + '` Please double check the name.' }); return res.redirect('/challenges'); } // Redirect to full name if the user only entered a partial + if (dasherize(challenge.name) !== origChallengeName) { + debug('redirecting to fullname'); + return res.redirect('/challenges/' + dasherize(challenge.name)); + } + if (req.user) { req.user.currentChallenge = { challengeId: challenge.id, @@ -191,7 +217,7 @@ module.exports = function(app) { var commonLocals = { title: challenge.name, - dashedName: dashedName, + dashedName: origChallengeName, name: challenge.name, details: challenge.description.slice(1), tests: challenge.tests, @@ -243,7 +269,7 @@ module.exports = function(app) { if (isCompletedWith) { User.find({ - where: { 'profile.username': isCompletedWith.toLowerCase() }, + where: { username: isCompletedWith.toLowerCase() }, limit: 1 }, function (err, pairedWith) { if (err) { return next(err); } @@ -382,7 +408,7 @@ module.exports = function(app) { if (isCompletedWith) { User.find({ - where: { 'profile.username': isCompletedWith.toLowerCase() }, + where: { username: isCompletedWith.toLowerCase() }, limit: 1 }, function (err, pairedWithFromMongo) { if (err) { return next(err); } From 03ea412b4a0837122fefb3e87ebecb0814ae1081 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 19:52:37 -0700 Subject: [PATCH 05/12] major challenges refactor commented out resetEditor in main remove heatmap timout --- package.json | 1 + public/js/main_0.0.2.js | 2 + server/boot/challenge.js | 377 +++++++++++++++------------------ server/boot/user.js | 2 +- server/views/account/show.jade | 98 ++------- 5 files changed, 198 insertions(+), 282 deletions(-) diff --git a/package.json b/package.json index 4863f4d47a..7fe7c38bd8 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "node-slack": "0.0.7", "node-uuid": "^1.4.3", "nodemailer": "~1.3.0", + "object.assign": "^3.0.0", "passport-facebook": "^2.0.0", "passport-google-oauth2": "^0.1.6", "passport-linkedin-oauth2": "^1.2.1", diff --git a/public/js/main_0.0.2.js b/public/js/main_0.0.2.js index 65ec9f170c..1b0e0812a1 100644 --- a/public/js/main_0.0.2.js +++ b/public/js/main_0.0.2.js @@ -331,6 +331,8 @@ $(document).ready(function() { $('#story-submit').on('click', storySubmitButtonHandler); + // $('#reset-button').on('click', resetEditor); + var commentSubmitButtonHandler = function commentSubmitButtonHandler() { $('#comment-button').unbind('click'); var data = $('#comment-box').val(); diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 3c7d85f8c8..16e2ba371a 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -31,9 +31,15 @@ */ var R = require('ramda'), + Rx = require('rx'), + assign = require('object.assign'), debug = require('debug')('freecc:challenges'), utils = require('../utils'), + + // this would be so much cleaner with destructering... saveUser = require('../utils/rx').saveUser, + observableQueryFromModel = require('../utils/rx').observableQueryFromModel, + userMigration = require('../utils/middleware').userMigration, ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo; @@ -55,6 +61,16 @@ function unDasherize(name) { return ('' + name).replace(/\-/g, ' '); } +function updateUserProgress(user, challengeId, completedChallenge) { + var index = user.uncompletedChallenges.indexOf(challengeId); + if (index > -1) { + user.progressTimestamps.push(Date.now()); + user.uncompletedChallenges.splice(index, 1); + } + user.completedChallenges.push(completedChallenge); + return user; +} + module.exports = function(app) { var router = app.loopback.Router(); var Challenge = app.models.Challenge; @@ -143,6 +159,7 @@ module.exports = function(app) { return elem; } }); + if (!req.user.currentChallenge) { req.user.currentChallenge = {}; req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0]; @@ -201,11 +218,11 @@ module.exports = function(app) { challengeId: challenge.id, challengeName: challenge.name, dashedName: challenge.dashedName, - challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds). - map(function (key) { + challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds) + .map(function (key) { return challengeMapWithIds[key] .filter(function (elem) { - return String(elem) === challenge.id; + return elem === ('' + challenge.id); }) .map(function () { return key; @@ -261,143 +278,124 @@ module.exports = function(app) { } function completedBonfire(req, res, next) { - var isCompletedWith = req.body.challengeInfo.completedWith || ''; - var isCompletedDate = Math.round(+new Date()); + debug('compltedBonfire'); + var completedWith = req.body.challengeInfo.completedWith || false; var challengeId = req.body.challengeInfo.challengeId; - var isSolution = req.body.challengeInfo.solution; - var challengeName = req.body.challengeInfo.challengeName; - if (isCompletedWith) { - User.find({ - where: { username: isCompletedWith.toLowerCase() }, - limit: 1 - }, function (err, pairedWith) { - if (err) { return next(err); } + var challengeData = { + id: challengeId, + name: req.body.challengeInfo.challengeName, + completedDate: Math.round(+new Date()), + solution: req.body.challengeInfo.solution, + challengeType: 5 + }; - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - pairedWith = pairedWith.pop(); + observableQueryFromModel( + User, + 'findOne', + { where: { username: ('' + completedWith).toLowerCase() } } + ) + .doOnNext(function(pairedWith) { + debug('paired with ', pairedWith); if (pairedWith) { - - index = pairedWith.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - pairedWith.progressTimestamps.push(Date.now() || 0); - pairedWith.uncompletedChallenges.splice(index, 1); - - } - - pairedWith.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: req.user.id, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); - - req.user.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: pairedWith.id, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); + updateUserProgress( + pairedWith, + challengeId, + assign({ completedWith: req.user.id }, challengeData) + ); } - // User said they paired, but pair wasn't found - req.user.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: null, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); - - req.user.save(function (err, user) { - if (err) { return next(err); } - - if (pairedWith) { - pairedWith.save(function (err, paired) { - if (err) { - return next(err); - } - if (user && paired) { - return res.send(true); - } - }); - } else if (user) { - res.send(true); + }) + .withLatestFrom( + Rx.Observable.just(req.user), + function(pairedWith, user) { + debug('yo'); + return { + user: user, + pairedWith: pairedWith + }; + } + ) + // side effects should always be done in do's and taps + .doOnNext(function(dats) { + updateUserProgress( + dats.user, + challengeId, + dats.pairedWith ? + // paired programmer found and adding to data + assign({ completedWith: dats.pairedWith.id }, challengeData) : + // user said they paired, but pair wasn't found + challengeData + ); + }) + // not iterate users + .flatMap(function(dats) { + debug('flatmap'); + return Rx.Observable.from([dats.user, dats.pairedWith]); + }) + // save user + .flatMap(function(user) { + // save user will do nothing if user is falsey + debug('saving user', user.username); + return saveUser(user); + }) + .subscribe( + function(user) { + debug('onNext'); + if (user) { + debug('user %s saved', user.username); } - }); - }); - } else { - req.user.completedChallenges.push({ - id: challengeId, - name: challengeName, - completedWith: null, - completedDate: isCompletedDate, - solution: isSolution, - challengeType: 5 - }); - - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - - req.user.save(function (err) { - if (err) { return next(err); } - res.send(true); - }); - } + }, + next, + function() { + debug('completed'); + return res.status(200).send(true); + } + ); } function completedChallenge(req, res, next) { - var isCompletedDate = Math.round(+new Date()); + var completedDate = Math.round(+new Date()); var challengeId = req.body.challengeInfo.challengeId; - req.user.completedChallenges.push({ - id: challengeId, - completedDate: isCompletedDate, - name: req.body.challengeInfo.challengeName, - solution: null, - githubLink: null, - verified: true - }); - var index = req.user.uncompletedChallenges.indexOf(challengeId); - - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - - req.user.save(function (err, user) { - if (err) { - return next(err); + updateUserProgress( + req.user, + challengeId, + { + id: challengeId, + completedDate: completedDate, + name: req.body.challengeInfo.challengeName, + solution: null, + githubLink: null, + verified: true } - if (user) { - res.sendStatus(200); - } - }); + ); + + saveUser(req.user) + .subscribe( + function() { }, + next, + function() { + res.sendStatus(200); + } + ); } function completedZiplineOrBasejump(req, res, next) { - var isCompletedWith = req.body.challengeInfo.completedWith || false; - var isCompletedDate = Math.round(+new Date()); + var completedWith = req.body.challengeInfo.completedWith || false; + var completedDate = Math.round(+new Date()); var challengeId = req.body.challengeInfo.challengeId; var solutionLink = req.body.challengeInfo.publicURL; - var githubLink = req.body.challengeInfo.challengeType === '4' - ? req.body.challengeInfo.githubURL : true; + + var githubLink = req.body.challengeInfo.challengeType === '4' ? + req.body.challengeInfo.githubURL : + true; + var challengeType = req.body.challengeInfo.challengeType === '4' ? - 4 : 3; + 4 : + 3; + if (!solutionLink || !githubLink) { req.flash('errors', { msg: 'You haven\'t supplied the necessary URLs for us to inspect ' + @@ -406,93 +404,64 @@ module.exports = function(app) { return res.sendStatus(403); } - if (isCompletedWith) { - User.find({ - where: { username: isCompletedWith.toLowerCase() }, - limit: 1 - }, function (err, pairedWithFromMongo) { - if (err) { return next(err); } - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); + var challengeData = { + id: challengeId, + name: req.body.challengeInfo.challengeName, + completedDate: completedDate, + solution: solutionLink, + githubLink: githubLink, + challengeType: challengeType, + verified: false + }; + + observableQueryFromModel( + User, + 'findOne', + { where: { username: completedWith.toLowerCase() } } + ) + .doOnNext(function(pairedWith) { + if (pairedWith) { + updateUserProgress( + pairedWith, + challengeId, + assign({ completedWith: req.user.id }, challengeData) + ); } - var pairedWith = pairedWithFromMongo.pop(); - - req.user.completedChallenges.push({ - id: challengeId, - name: req.body.challengeInfo.challengeName, - completedWith: pairedWith.id, - completedDate: isCompletedDate, - solution: solutionLink, - githubLink: githubLink, - challengeType: challengeType, - verified: false - }); - - req.user.save(function (err, user) { - if (err) { return next(err); } - - if (req.user.id.toString() === pairedWith.id.toString()) { - return res.sendStatus(200); + }) + .withLatestFrom(Rx.Observable.just(req.user), function(user, pairedWith) { + return { + user: user, + pairedWith: pairedWith + }; + }) + .doOnNext(function(dats) { + updateUserProgress( + dats.user, + challengeId, + dats.pairedWith ? + assign({ completedWith: dats.pairedWith.id }, challengeData) : + challengeData + ); + }) + .flatMap(function(dats) { + return Rx.Observable.from([dats.user, dats.pairedWith]); + }) + // save users + .flatMap(function(user) { + // save user will do nothing if user is falsey + return saveUser(user); + }) + .subscribe( + function(user) { + if (user) { + debug('user %s saved', user.username); } - index = pairedWith.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - pairedWith.progressTimestamps.push(Date.now() || 0); - pairedWith.uncompletedChallenges.splice(index, 1); - - } - - pairedWith.completedChallenges.push({ - id: challengeId, - name: req.body.challengeInfo.coursewareName, - completedWith: req.user.id, - completedDate: isCompletedDate, - solution: solutionLink, - githubLink: githubLink, - challengeType: challengeType, - verified: false - }); - pairedWith.save(function (err, paired) { - if (err) { - return next(err); - } - if (user && paired) { - return res.sendStatus(200); - } - }); - }); - }); - } else { - - req.user.completedChallenges.push({ - id: challengeId, - name: req.body.challengeInfo.challengeName, - completedWith: null, - completedDate: isCompletedDate, - solution: solutionLink, - githubLink: githubLink, - challengeType: challengeType, - verified: false - }); - - var index = req.user.uncompletedChallenges.indexOf(challengeId); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedChallenges.splice(index, 1); - } - - req.user.save(function (err, user) { - if (err) { - return next(err); + }, + next, + function() { + return res.status(200).send(true); } - // NOTE(berks): under certain conditions this will not close - // the response. - if (user) { - return res.sendStatus(200); - } - }); - } + ); } function challengeMap(req, res, next) { diff --git a/server/boot/user.js b/server/boot/user.js index f36fbe56a2..4575a6decf 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -4,7 +4,7 @@ var _ = require('lodash'), crypto = require('crypto'), nodemailer = require('nodemailer'), moment = require('moment'), - //debug = require('debug')('freecc:cntr:userController'), + // debug = require('debug')('freecc:cntr:userController'), secrets = require('../../config/secrets'); diff --git a/server/views/account/show.jade b/server/views/account/show.jade index 0f7add7096..f1edc4d3c7 100644 --- a/server/views/account/show.jade +++ b/server/views/account/show.jade @@ -47,88 +47,32 @@ block content .background-svg.img-center .points-on-top = "[ " + (progressTimestamps.length) + " ]" - - - .row - .col-xs-12 - if (website1Title && website1Link && website1Image) - .row - .col-xs-12.col-md-5 - img.img-center.img-responsive.portfolio-image(src=website1Image, alt="@#{username}'s #{website1Title}") - .col-xs-12.col-md-7 - h3.text-center.wrappable.flat-top= website1Title - a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website1Title && website1Link && !website1Image) - .col-xs-12.col-md-12 - h3.text-center.wrappable.flat-top= website1Title - a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website2Title && website2Link && website2Image) - .row - .col-xs-12.col-md-5 - img.img-responsive.portfolio-image.img-center(src=website2Image, alt="@#{username}'s #{website2Title}") - .col-xs-12.col-md-7 - h3.text-center.wrappable.flat-top= website2Title - a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website2Title && website2Link && !website2Image) - .col-xs-12.col-md-12 - h3.text-center.wrappable.flat-top= website2Title - a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank') - i.fa.icon-beaker - | Try it out - br - if (website3Title && website3Link && website3Image) - .row - .col-xs-12.col-md-5 - img.img-responsive.portfolio-image.img-center(src=website3Image, alt="@#{username}'s #{website1Title}") - .col-xs-12.col-md-7 - h3.text-center.wrappable.flat-top= website3Title - a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank') - i.fa.icon-beaker - | Try it out - if (website3Title && website3Link && !website3Image) - .col-xs-12.col-md-12 - h3.text-center.wrappable.flat-top= website3Title - a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank') - i.fa.icon-beaker - | Try it out - .spacer .hidden-xs.hidden-sm.col-md-12 #cal-heatmap.d3-centered script. $(document).ready(function () { - setTimeout(function () { - var cal = new CalHeatMap(); - var calendar = !{JSON.stringify(calender)}; - cal.init({ - itemSelector: "#cal-heatmap", - domain: "month", - subDomain: "x_day", - domainGutter: 10, - data: calendar, - cellSize: 15, - align: 'center', - cellRadius: 3, - cellPadding: 2, - tooltip: true, - range: 6, - start: new Date().setDate(new Date().getDate() - 150), - legendColors: ["#cccccc", "#215f1e"], - legend: [1, 2, 3], - label: { - position: "top" - } - }); - }, 300); + var cal = new CalHeatMap(); + var calendar = !{JSON.stringify(calender)}; + cal.init({ + itemSelector: "#cal-heatmap", + domain: "month", + subDomain: "x_day", + domainGutter: 10, + data: calendar, + cellSize: 15, + align: 'center', + cellRadius: 3, + cellPadding: 2, + tooltip: true, + range: 6, + start: new Date().setDate(new Date().getDate() - 150), + legendColors: ["#cccccc", "#215f1e"], + legend: [1, 2, 3], + label: { + position: "top" + } + }); }); .row .hidden-xs.col-sm-12.text-center From 049233c0a190233181904fe27e5fee637cd24ece Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 19:55:22 -0700 Subject: [PATCH 06/12] fix add server/utils/rx to commit --- server/utils/rx.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 server/utils/rx.js diff --git a/server/utils/rx.js b/server/utils/rx.js new file mode 100644 index 0000000000..8a4003c00c --- /dev/null +++ b/server/utils/rx.js @@ -0,0 +1,25 @@ +var Rx = require('rx'); +var debug = require('debug')('freecc:rxUtils'); + +exports.saveUser = function saveUser(user) { + return new Rx.Observable.create(function(observer) { + if (!user || typeof user.save !== 'function') { + debug('no user or save method'); + observer.onNext(); + return observer.onCompleted(); + } + user.save(function(err, savedUser) { + if (err) { + return observer.onError(err); + } + debug('user saved'); + observer.onNext(savedUser); + observer.onCompleted(); + }); + }); +}; + +exports.observableQueryFromModel = + function observableQueryFromModel(Model, method, query) { + return Rx.Observable.fromNodeCallback(Model[method], Model)(query); + }; From 9fb3c0bff71a75b5316f7951d96a8dc3dcc7745e Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 20:43:36 -0700 Subject: [PATCH 07/12] fix use secrets.db instead of process.env.MONGOHQ in datasource.local.js this should eliminate timeout issue. I'd bet my bottom dollar. --- server/config.local.js | 2 +- server/datasources.local.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/config.local.js b/server/config.local.js index c781159696..9b9fc132d6 100644 --- a/server/config.local.js +++ b/server/config.local.js @@ -17,4 +17,4 @@ module.exports = { clientID: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET } -}; \ No newline at end of file +}; diff --git a/server/datasources.local.js b/server/datasources.local.js index 276a4e8dee..8ad9d08abc 100644 --- a/server/datasources.local.js +++ b/server/datasources.local.js @@ -3,8 +3,8 @@ var secrets = require('../config/secrets'); module.exports = { db: { connector: 'mongodb', - connectionTimeout: 15000, - url: process.env.MONGOHQ_URL + connectionTimeout: 5000, + url: secrets.db }, mail: { connector: 'mail', From f17c442018d06ad574e5f1687c6c376a25ec1026 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 20:54:39 -0700 Subject: [PATCH 08/12] fix should be story not store --- server/boot/randomAPIs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index 7dd89b1b4f..4a2e767884 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -15,7 +15,7 @@ module.exports = function(app) { var router = app.loopback.Router(); var User = app.models.User; var Challenge = app.models.Challenge; - var Story = app.models.Store; + var Story = app.models.Story; var FieldGuide = app.models.FieldGuide; var Nonprofit = app.models.Nonprofit; From 0623ace259f96542f5d5760da5032bad77794311 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 21:15:41 -0700 Subject: [PATCH 09/12] fix sitemap query's make rx mapping async --- server/boot/randomAPIs.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index 4a2e767884..4c460d5172 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -193,15 +193,15 @@ module.exports = function(app) { users: function(callback) { User.find( { - where: { 'profile.username': { nlike: '' } }, - fields: { 'profile.username': true } + where: { username: { nlike: '' } }, + fields: { username: true } }, function(err, users) { if (err) { debug('User err: ', err); callback(err); } else { - Rx.Observable.from(users) + Rx.Observable.from(users, null, null, Rx.Scheduler.default) .map(function(user) { return user.username; }) @@ -224,7 +224,7 @@ module.exports = function(app) { debug('Challenge err: ', err); callback(err); } else { - Rx.Observable.from(challenges) + Rx.Observable.from(challenges, null, null, Rx.Scheduler.default) .map(function(challenge) { return challenge.name; }) @@ -244,7 +244,7 @@ module.exports = function(app) { debug('Story err: ', err); callback(err); } else { - Rx.Observable.from(stories) + Rx.Observable.from(stories, null, null, Rx.Scheduler.default) .map(function(story) { return story.link; }) @@ -265,7 +265,7 @@ module.exports = function(app) { debug('User err: ', err); callback(err); } else { - Rx.Observable.from(nonprofits) + Rx.Observable.from(nonprofits, null, null, Rx.Scheduler.default) .map(function(nonprofit) { return nonprofit.name; }) @@ -285,7 +285,12 @@ module.exports = function(app) { debug('User err: ', err); callback(err); } else { - Rx.Observable.from(fieldGuides) + Rx.Observable.from( + fieldGuides, + null, + null, + Rx.Scheduler.default + ) .map(function(fieldGuide) { return fieldGuide.name; }) @@ -301,7 +306,7 @@ module.exports = function(app) { if (err) { return next(err); } - setTimeout(function() { + process.nextTick(function() { res.header('Content-Type', 'application/xml'); res.render('resources/sitemap', { appUrl: appUrl, @@ -312,7 +317,7 @@ module.exports = function(app) { nonprofits: results.nonprofits, fieldGuides: results.fieldGuides }); - }, 0); + }); } ); } @@ -383,7 +388,7 @@ module.exports = function(app) { } function unsubscribe(req, res, next) { - User.findOne({ email: req.params.email }, function(err, user) { + User.findOne({ where: { email: req.params.email } }, function(err, user) { if (user) { if (err) { return next(err); From 742a49b70dc38c8d20da29680512475cbb10c505 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 22:48:59 -0700 Subject: [PATCH 10/12] fix mainjs resetEditor --- .../coursewaresJSFramework_0.0.6.js | 96 +++++++++++-------- public/js/main_0.0.2.js | 1 - server/boot/challenge.js | 1 - server/views/coursewares/showBonfire.jade | 2 +- 4 files changed, 55 insertions(+), 45 deletions(-) diff --git a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js index 7d7867fd78..ae7237ef5d 100644 --- a/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js +++ b/public/js/lib/coursewares/coursewaresJSFramework_0.0.6.js @@ -1,3 +1,7 @@ +$(document).ready(function() { + $('#reset-button').on('click', resetEditor); +}); + var widgets = []; var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { lineNumbers: true, @@ -41,61 +45,68 @@ editor.setOption("extraKeys", { /* - Local Storage Update System By Andrew Cay(Resto) - codeStorage: singleton object that contains properties and methods related to - dealing with the localStorage system. - The keys work off of the variable challenge_name to make unique identifiers per bonfire + Local Storage Update System By Andrew Cay(Resto) + codeStorage: singleton object that contains properties and methods related to + dealing with the localStorage system. + The keys work off of the variable challenge_name to make unique identifiers per bonfire - Two extra functionalities: - Added anonymous version checking system incase of future updates to the system - Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage + Two extra functionalities: + Added anonymous version checking system incase of future updates to the system + Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage */ var codeStorage = { - version: 0.01, - keyVersion:"saveVersion", - keyValue: null,//where the value of the editor is saved - updateWait: 2000,// 2 seconds - updateTimeoutId: null, - eventArray: []//for firing saves + version: 0.01, + keyVersion:"saveVersion", + keyValue: null,//where the value of the editor is saved + updateWait: 2000,// 2 seconds + updateTimeoutId: null, + eventArray: []//for firing saves }; // Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere") codeStorage.hasSaved = function(){ - return ( updateTimeoutId === null ); + return ( updateTimeoutId === null ); }; codeStorage.onSave = function(func){ - codeStorage.eventArray.push(func); + codeStorage.eventArray.push(func); }; codeStorage.setSaveKey = function(key){ - codeStorage.keyValue = key + 'Val'; + codeStorage.keyValue = key + 'Val'; }; codeStorage.getEditorValue = function(){ - return localStorage.getItem(codeStorage.keyValue); + return ('' + localStorage.getItem(codeStorage.keyValue)); }; + +codeStorage.isAlive = function() { + var val = this.getEditorValue() + return val !== 'null' && + val !== 'undefined' && + (val && val.length > 0); +} codeStorage.updateStorage = function(){ - if(typeof(Storage) !== undefined) { - var value = editor.getValue(); - localStorage.setItem(codeStorage.keyValue, value); - } else { - var debugging = false; - if( debugging ){ - console.log('no web storage'); - } - } - codeStorage.updateTimeoutId = null; - codeStorage.eventArray.forEach(function(func){ - func(); - }); + if(typeof(Storage) !== undefined) { + var value = editor.getValue(); + localStorage.setItem(codeStorage.keyValue, value); + } else { + var debugging = false; + if( debugging ){ + console.log('no web storage'); + } + } + codeStorage.updateTimeoutId = null; + codeStorage.eventArray.forEach(function(func){ + func(); + }); }; //Update Version (function(){ - var savedVersion = localStorage.getItem('saveVersion'); - if( savedVersion === null ){ - localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version - }else{ - if( savedVersion !== codeStorage.version ){ - //Update version - } - } + var savedVersion = localStorage.getItem('saveVersion'); + if( savedVersion === null ){ + localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version + }else{ + if( savedVersion !== codeStorage.version ){ + //Update version + } + } })(); @@ -104,8 +115,8 @@ codeStorage.updateStorage = function(){ /// Update local save when editor has changed codeStorage.setSaveKey(challenge_Name); editor.on('keyup', function(){ - window.clearTimeout(codeStorage.updateTimeoutId); - codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait); + window.clearTimeout(codeStorage.updateTimeoutId); + codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait); }); @@ -114,9 +125,10 @@ if (attempts) { attempts = 0; } -var resetEditor = function() { +var resetEditor = function resetEditor() { editor.setValue(allSeeds); codeStorage.updateStorage(); + }; var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { @@ -150,7 +162,7 @@ var tests = tests || []; var allSeeds = ''; (function() { challengeSeed.forEach(function(elem) { - allSeeds += elem + '\n'; + allSeeds += elem + '\n'; }); })(); diff --git a/public/js/main_0.0.2.js b/public/js/main_0.0.2.js index 1b0e0812a1..1b41b9066b 100644 --- a/public/js/main_0.0.2.js +++ b/public/js/main_0.0.2.js @@ -331,7 +331,6 @@ $(document).ready(function() { $('#story-submit').on('click', storySubmitButtonHandler); - // $('#reset-button').on('click', resetEditor); var commentSubmitButtonHandler = function commentSubmitButtonHandler() { $('#comment-button').unbind('click'); diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 16e2ba371a..02cecc639e 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -335,7 +335,6 @@ module.exports = function(app) { // save user .flatMap(function(user) { // save user will do nothing if user is falsey - debug('saving user', user.username); return saveUser(user); }) .subscribe( diff --git a/server/views/coursewares/showBonfire.jade b/server/views/coursewares/showBonfire.jade index c28f20e322..a52578dec0 100644 --- a/server/views/coursewares/showBonfire.jade +++ b/server/views/coursewares/showBonfire.jade @@ -84,7 +84,7 @@ block content label.negative-10.btn.btn-primary.btn-block#submitButton i.fa.fa-play |   Run code (ctrl + enter) - #resetButton.btn.btn-danger.btn-big.btn-block(data-toggle='modal', data-target='#reset-modal', data-backdrop='true') Reset Code + #trigger-reset-modal.btn.btn-danger.btn-big.btn-block(data-toggle='modal', data-target='#reset-modal', data-backdrop='true') Reset Code if (user && user.sentSlackInvite) .button-spacer .btn-group.input-group.btn-group-justified From 8f445b58f241ec3e87a9a64e2e8f7a5498e0efb0 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 23:11:33 -0700 Subject: [PATCH 11/12] fix chat links go to Gitter Room --- server/views/partials/navbar.jade | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade index 317ce661d1..e35b9c5e8e 100644 --- a/server/views/partials/navbar.jade +++ b/server/views/partials/navbar.jade @@ -13,12 +13,8 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height a(href='/challenges') Learn li a(href='/map') Map - if (user && user.sentSlackInvite) - li - a(href='/chat', target='_blank') Chat - else - li - a(href='/challenges/waypoint-join-our-chat-room') Chat + li + a(href='//gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat li a(href='/stories') News li From 629262dd0d126ca1b0ff58da6b6871cd1d7fb78c Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 20 Jun 2015 23:15:10 -0700 Subject: [PATCH 12/12] fix /chat should go to gitterim --- server/boot/randomAPIs.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index 4c460d5172..7b658a36df 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -323,13 +323,7 @@ module.exports = function(app) { } function chat(req, res) { - if (req.user && req.user.progressTimestamps.length > 5) { - res.redirect('http://freecodecamp.slack.com'); - } else { - res.render('resources/chat', { - title: 'Watch us code live on Twitch.tv' - }); - } + res.redirect('//gitter.im/FreeCodeCamp/FreeCodeCamp'); } function bootcampCalculator(req, res) {